<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Shahpasand</title>
        <link>https://virgool.io/feed/@shahpasand</link>
        <description>کارشناس ارشد نرم افزار و توسعه دهنده موبایل</description>
        <language>fa</language>
        <pubDate>2026-06-16 06:22:26</pubDate>
        <image>
            <url>https://static.virgool.io/images/default-avatar.jpg</url>
            <title>Shahpasand</title>
            <link>https://virgool.io/@shahpasand</link>
        </image>

                    <item>
                <title>آشنایی با Extension Type در زبان Dart</title>
                <link>https://virgool.io/@shahpasand/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-extension-type-%D8%AF%D8%B1-%D8%B2%D8%A8%D8%A7%D9%86-dart-b6ev3802y1j6</link>
                <description>در این مقاله می‌خوایم با Extension type در زبان Dart آشنا بشیم و کاربردهاش رو با مثال بررسی کنیم. اگر به خوندن خلاصه این مقاله علاقه دارید، پست من در لینکدین رو ببینید.در یک تعریف کوتاه، Extension type یک انتزاع زمان کامپایل برای یک نوع داده است. به این صورت که یک نوع داده‌ رو با یک واسط متفاوت و ایستا wrap می‌کنه. با استفاده از Extension type می‌تونیم بر حسب نیاز، یک سری محدودیت‌ها و قوانین روی یک نوع داده موجود (مثلاً int) اعمال کنیم. از اینجا به بعد، به نوع داده‌ای که Extension type رو روی اون تعریف می‌کنیم، نوع داده اصلی (representation type) می‌گیم. با تعریف Extension type برای یک نوع داده می‌تونیم بعضی متدهای اون نوع داده رو استفاده یا برخی رو حذف کنیم و یا با عملکردهای جدیدی جایگزین‌شون کنیم.برای اینکه موضوع واضح‌تر بشه با یک مثال ادامه میدم. در این مثال، نوع داده اصلی int هست و می‌خوایم بر مبنای int یک Extension type برای شماره ID بسازیم که عملکردهای اصلی int رو با یک سری تغییرات در اختیارمون میذاره. می‌خوایم نوع داده int رو با استفاده از Extension type به صورتی محدود کنیم که تنها عملکردهای معنی‌دار برای شماره ID رو داشته باشه. مثلا برای شماره ID عمل جمع معنی نداره پس این عملکرد رو نیاز نداریم.extension type IdNumber(int id) {
   // Wraps the &#039;int&#039; type&#039;s &#039;&lt;&#039; operator:
   operator &lt;(IdNumber other) =&gt; id &lt; other.id;
   // Doesn&#039;t declare the &#039;+&#039; operator, for example,
   // because addition does not make sense for ID numbers.
}

void main() {
   // Without the discipline of an extension type,
   // &#039;int&#039; exposes ID numbers to unsafe operations:
   int myUnsafeId = 42424242;
   myUnsafeId = myUnsafeId + 10; // This works, but shouldn&#039;t be allowed for IDs.
   var safeId = IdNumber(42424242);
   safeId + 10; // Compile-time error: No &#039;+&#039; operator.
   myUnsafeId = safeId; // Compile-time error: Wrong type.
   myUnsafeId = safeId as int; // OK: Run-time cast to representation type.
   safeId &lt; IdNumber(42424241); // OK: Uses wrapped &#039;&lt;&#039; operator.
}در مثال بالا یک Extension type به نام IdNumber تعریف کردیم که عملگر &lt; رو برای مقایسه شماره Id تعریف کرده. عملگرهای دیگه برای int مثل جمع و تفریق رو نداریم چون برای نوع داده اصلی که در این مثال int هست بی معنی هستند و نیازی بهشون نیست.در متد main ابتدا متغیر myUnsafeId رو تعریف و از نوع داده int برای id استفاده کردیم تا نشون بدیم چطور عملگر + رو میشه به اشتباه روی شماره Id اعمال کرد و هیچ محدودیتی در این زمینه وجود نداره. اما وقتی با استفاده از IdNumber  متغیر  safeId رو تعریف می‌کنیم هنگام استفاده از عملگر + با خطای زمان کامپایل مواجه می‌شیم چون این عملگر برای IdNumber تعریف نشده.به دلیل محدودیت‌هایی که برای IdNumber تعریف شده، نمی‌تونیم متغیری از نوع داده اصلی رو به متغیری از نوع IdNumber تخصیص (Assign) بدیم و نیاز هست که قبل از تخصیص، متغیر نوع IdNumber رو به نوع اصلی تبدیل (Cast) کنیم.خب تا اینجای کار شاید بگید که این کار رو می‌تونستیم با wrapper class هم انجام بدیم. یعنی یک کلاس به نام IdNumber ایجاد کنیم و قوانین و محدودیت‌های مورد نظرمون رو اعمال کنیم. اما تفاوت اینجاست که با استفاده از Extension type نیازی به ایجاد آبجکت‌های (Object) اضافه (از نوع کلاس wrapper) در زمان اجرا (که در مواردی می‌تونه هزینه‌بر هم باشه) نیست. چون Extension type ها ایستا هستند و کامپایل میشن بنابراین در زمان اجرا هیچ هزینه‌ای تحمیل نمی‌کنند.چطور یک Extension type تعریف کنیم؟تعریف Extension type با کلمات کلیدی extension type شروع میشه و بعد نام رو مشخص می‌کنیم و به دنبالش نوع داده اصلی داخل پرانتز درج می‌شه:extension type E(int i) {
    // Define set of operations.
}کد (inti) : مشخص می‌کنه که نوع داده اصلی که Extension type روی اون تعریف شده int هست و i ارجاعی به یک آبجکت از نوع داده اصلیه. دقت کنید که اینجا یک سازنده‌ی ضمنی به صورت E(int i) : i = i داریم.کاربردهای Extension type در مورد Extension type این‌طور فکر کنید که بهتون اجازه میده خودتون روی یک نوع داده یک type safety پیاده کنید، البته دقت داشته باشید که نمی‌تونید رفتار و خصوصیات نوع داده مدنظر رو تغییر بدید.در ادامه می‌خوایم دو کاربرد مهم Extension type رو در قالب مثال بررسی کنیم:کاربرد اول: ایجاد یک واسط متفاوت برای یک نوع داده موجود:extension type NumberE(int value) {
   NumberE operator +(NumberE other) =&gt;
   NumberE(value + other.value);

   NumberE get next =&gt; NumberE(value + 1);
   bool isValid() =&gt; !value.isNegative; 
}

void testE() {
   var num1 = NumberE(1);
   int num2 = NumberE(2); // Error: Can&#039;t assign &#039;NumberE&#039; to &#039;int&#039;.
   num1.isValid(); // OK: Extension member invocation.
   num1.isNegative(); // Error: &#039;NumberE&#039; does not define &#039;int&#039; member &#039;isNegative&#039;.

   var sum1 = num1 + num1; // OK: &#039;NumberE&#039; defines &#039;+&#039;.
   var diff1 = num1 - num1; // Error: &#039;NumberE&#039; does not define &#039;int&#039; member &#039;-&#039;.
   var diff2 = num1.value - 2; // OK: Can access representation object with reference.
   var sum2 = num1 + 2; // Error: Can&#039;t assign &#039;int&#039; to parameter type &#039;NumberE&#039;.

   List&lt;NumberE&gt; numbers = [
   NumberE(1),
   num1.next, // OK: &#039;next&#039; getter returns type &#039;NumberE&#039;.
   1, // Error: Can&#039;t assign &#039;int&#039; element to list type &#039;NumberE&#039;.
   ];
}این نوع Extension type به عنوان یک نوع داده‌ی جدید در نظر گرفته میشه و از نوع داده اصلی متمایزه بنابراین نمی‌تونید مقداری از نوع داده اصلی به متغیری از نوع این Extension type تخصیص بدید و به عملکردهای نوع داده اصلی هم دسترسی ندارید.از این روش می‌شه برای جایگزین کردن واسط یک نوع داده موجود استفاده کرد. به این ترتیب می‌تونیم واسطی ایجاد کنیم که روی یک نوع داده‌ی جدید، محدودیت و قوانین معنی‌دار اعمال کنه، در حالی که از مزیت‌هایی مثل کارایی و سهولت استفاده که نوع داده‌های موجود مانند int برخوردارند، استفاده می‌کنه.کاربرد دوم: ایجاد یک واسط گسترش‌یافته (extended) برای یک نوع داده‌ی موجود:در این حالت، Extension type، نوع داده‌ی اصلی مورد نظر رو implement می‌کنه. به این ترتیب، می‎تونه نوع داده اصلی رو ببینه و اعضای نوع داده اصلی رو فراخوانی کنه. مثال زیر رو در نظر بگیرید تا بیشتر توضیح بدم:extension type NumberT(int value) implements int {
   // Doesn&#039;t explicitly declare any members of &#039;int&#039;.
   NumberT get i =&gt; this;
 }

void main () {
   // All OK: Transparency allows invoking `int` members on the extension type:
   var v1 = NumberT(1); // v1 type: NumberT
   int v2 = NumberT(2); // v2 type: int

   var v3 = v1.i - v1;  // v3 type: int
   var v4 = v2 + v1; // v4 type: int
   var v5 = 2 + v1; // v5 type: int

   v2.i;   // Error: Extension type interface is not available to representation type
}در این نوع Extension type علاوه بر عملکردهای نوع داده‌ی اصلی، به اعضای کمکی که خودتون تعریف می‌کنید هم دسترسی دارید. در نتیجه یک واسط گسترش‌یافته برای یک نوع داده موجود دارید. از این واسط جدید، برای متغیرهایی که از نوع Extension type تعریف می‌کنید استفاده می‌شه. در مثال بالا نوع داده اصلی int هست و همونطور که می‌بینید، متغیرهای نوع NumberT به عملیات تفریق و جمع که مربوط به نوع داده‌ اصلیه دسترسی دارند. علاوه بر این، یک getter با نام i در NumberT تعریف شده که متغیرهای نوع NumberT بهش دسترسی دارند.تفاوت Extension type با Extension method چیه؟در واقع Extension method که بهش extension هم میگیم، مشابه Extension type یک انتزاع ایستاست. هرچند یک Extension method عملکردی رو مستقیما به هر نمونه (instance)، از نوعی که extension روی اون تعریف شده، اضافه می‌کنه. اما Extension type متفاوته: واسطی که با استفاده از Extension type تعریف می‌کنیم فقط روی متغیرهایی اعمال میشه که از نوع همون Extension type تعریف شده باشن. در واقع به صورت پیشفرض از واسط نوع داده اصلی خودشون متمایز هستند.تفاوت extension method و extension type رو به طور خلاصه این‌طور می‌شه گفت: Extension method برای افزودن قابلیت‌های ساده به نوع داده موجود مناسبه در حالی که Extension type  برای ایجاد API های سفارشی و بهینه‌سازی کارایی ایده‌‌آله.مزایای Extension typeتا اینجا به صورت ضمنی به مزایا اشاره کردم اما بطور خلاصه مهم‌ترین مزایای Extension type این‌هاست:1- بهبود کارایی: نیاز به ایجاد آبجکت اختصاصی (wrapper) برای هر instance رو از بین می‌برن و به همین دلیل برای مواردی که حساسیت زیادی روی کارایی وجود داره، به‌خصوص مواردی که با مجموعه داده‌های بزرگ یا دستکاری مکرر داده‌ها سروکار داریم، ایده‌آل هستن.2- انتزاع واضح‌تر: می‌شه APIهای تمیزی مشابه Dart ایجاد کرد تا پیچیدگی‌های داده اصلی پنهان بشه و قابلیت نگهداری و خوانایی کد بهبود پیدا کنه.ممنونم که همراهم بودید. لایک و کامنت‌های شما خوشحالم می‌کنه :)</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Fri, 13 Sep 2024 22:05:21 +0330</pubDate>
            </item>
                    <item>
                <title>به زبان برنامه نویسی محدود نشید</title>
                <link>https://virgool.io/codenevis/%D8%A8%D9%87-%D8%B2%D8%A8%D8%A7%D9%86-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%D9%85%D8%AD%D8%AF%D9%88%D8%AF-%D9%86%D8%B4%DB%8C%D8%AF-oasfdfcbx1bd</link>
                <description>این یک پست کوتاهه در مورد موضوع جالبی که اخیراً بهش برخوردم: به زبان برنامه نویسی که استفاده می کنید محدود نشید. در واقع باید بین برنامه نویسی «به» یک زبان و برنامه نویسی «با» یک زبان تفاوت قائل بشیم.برنامه نویسانی که «به» زبانی برنامه نویسی می کنند، افکار خودشون رو به ساختارهایی که اون زبان مستقیماً براشون فراهم می کنه محدود می کنند. اگر ابزارهای زبان ابتدایی باشن، افکار برنامه نویس هم ابتدایی خواهد بود.اما برنامه نویسانی که «با» یک زبان برنامه نویسی می کنند، اول تصمیم می گیرن که چه افکاری رو می خوان بیان کنند و سپس تعیین می کنند که چگونه اون افکار رو با استفاده از ابزارهای ارائه شده توسط زبان برنامه نویسی مشخصی بیان کنند. در واقع در دیدگاه دوم، زبان برنامه نویسی وسیله ای هست که برنامه نویس ازش برای بیان افکار و پیاده سازی راه حلی که در ذهنش داره استفاده میکنه در حالی که در دیدگاه اول، خود زبان برنامه نویسی مورد استفاده، بخشی از راه حل محسوب میشه. اصول مهم برنامه نویسی اکثراً وابسته به زبان خاصی نیستند، بلکه مهم روش و موقعیتی هست که شما از این اصول استفاده می کنید. منبع: کتاب Code Complete: A practical handbook of software construction از Steve McConnell</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Tue, 02 Apr 2024 11:49:49 +0330</pubDate>
            </item>
                    <item>
                <title>تجربه‌هایی ارزشمند در توسعه نرم‌افزار</title>
                <link>https://virgool.io/codenevis/%D8%AA%D8%AC%D8%B1%D8%A8%D9%87-%D9%87%D8%A7%DB%8C%DB%8C-%D8%A7%D8%B1%D8%B2%D8%B4%D9%85%D9%86%D8%AF-%D8%AF%D8%B1-%D8%AA%D9%88%D8%B3%D8%B9%D9%87-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-ucgtiwtp9068</link>
                <description>مدتی پیش این مقاله رو با عنوان اصلی «درس‌هایی که در توسعه نرم‌افزار آموختم» در وبلاگ Henrik Warne خوندم. نویسنده یک برنامه‌نویس باتجربه است و این مقاله‌اش خیلی مورد توجه قرار گرفته طوری که می‌تونید در لیست‌ «مقاله‌هایی که باید بخوانیم» توسعه‌دهنده‌ها پیداش کنید. خودش میگه اینها لیستی از اکتشافات و قوانین من برای توسعه نرم‌افزاره که در طول این سال‌ها ازشون استفاده کردم. پس بریم سراغ این اکتشاف‌ها که اگر یک برنامه‌نویس هستید، حسابی ارزش خوندن داره.از کم شروع کنید، سپس گسترشش بدید. فرقی نمی‌کنه یک سیستم جدید ایجاد می‌شه یا یک فیچر (feature) جدید به سیستم موجوداضافه می‌شه، من همیشه با ساختن یک نسخه بسیار ساده با تقریباً هیچ یک از عملکردهای مورد نیاز شروع می‌کنم. سپس راه حل رو گام به گام توسعه می‌دم، تا وقتی که تبدیل به چیزی که انتظارش رو داریم بشه. من هیچ وقت نتونستم همه چیز رو از ابتدا و با جزئیات برنامه‌ریزی کنم. در عوض، همین طور که پیش میرم، یاد می گیرم و از این اطلاعات تازه کشف شده در راه حل خودم استفاده می‌کنم. این نقل قول رو دوست دارم که &quot;سیستم پیچیده‌ای که درست کار می‌کنه همیشه از تکامل سیستم ساده‌ای که درست کار می‌کرده ایجاد شده.&quot;در هر لحظه یک چیز واحد رو تغییر بدید. وقتی در حال توسعه هستید و برخی از تست‌ها با شکست مواجه می‌شن یا یک فیچر درست کار نمی‌کنه، پیدا کردن مشکل خیلی راحت‌تره اگر فقط یک چیز رو تغییر بدید. به عبارت دیگه از یک سری تکرارهای کوتاه استفاده کنید: یک تغییر مستقل و واحد انجام بدید، مطمئن شید که کد کار می‌کنه، و این چرخه رو تکرار کنید. این روش رو برای کامیت‌ها هم بکار ببرید. اگر باید قبل از اضافه کردن یک فیچر جدید، کد رو ریفکتور (refactor) کنید، ابتدا ریفکتور رو انجام بدید و کامیت کنید. بعد (در یک کامیت مجزا) فیچر جدید رو اضافه کنید.ثبت لاگ (Logging) و مدیریت خطا (Error Handling) رو هر چه زودتر اضافه کنید. وقتی یک سیستم جدید رو توسعه میدم، یکی از اولین کارهایی که انجام می‌دم اضافه کردن گزارش و مدیریت خطاست، چون هر دو از همان ابتدای کار مفید هستن. برای همه‌ی سیستم‌ها نیاز به روشی داریم که بفهمیم در برنامه چه اتفاقی داره می‌افته. به محض اینکه برنامه طبق انتظار رفتار نکنه باید بتونیم ببینیم چه اتفاقی افتاده. همین قانون در مورد مدیریت خطا هم صادقه. خطاها و استثناها از همون ابتدای برنامه اتفاق می‌افتن، بنابراین هر چه زودتر اونها رو به روشی سیستماتیک مدیریت کنیم بهتره.تمام خطوط جدید برنامه باید حداقل یک بار اجرا بشن. قبل از اینکه کارمون با یک فیچر تموم بشه، باید تستش کنیم. در غیر این صورت از کجا بفهمیم که کاری رو که باید، انجام میده؟ هر خط کد جدید باید حداقل یک بار اجرا بشه. گاهی اوقات ایجاد شرایط مناسب برای تست کد ممکنه سخت باشه. خوشبختانه اینجا می‌تونیم تقلب کنیم! مثلا شرط if رو موقتا معکوس کنیم تا شرایطی که به ندرت اتفاق می‌افته رو ایجاد کنیم. فقط برای اینکه مطمئن بشیم کد اجرا میشه و کاری که باید بکنه رو درست انجام میده. گاهی باگ‌هایی تو کد می‌بینم که نشون میده توسعه‌دهنده‌ی برنامه اون خط کد رو تا به حال اجرا نکرده. وقتی کد رو رویو می‌کنیم در ظاهر باید کار بکنه اما در عمل این اتفاق نیفتاده. اگر سیاست تون این باشه که هر خط کد جدیدی که می‌نویسید تست کنید در آینده با این باگ‌ها خجالت‌زده نمی‌شید :)اول هر یک از ماژول‌ها رو به تنهایی تست کنید. قطعات کدی که به خوبی تست شدن باعث صرفه جویی در زمان می‌شن. وقتی ماژول‌های کد رو ادغام می‌کنیم احتمال بروز مشکل هست. مثلا به خاطر درک نادرست واسط (Interface) ماژول ها. اگر مطمئن باشیم که تک تک ماژول‌ها طبق انتظار کار می‌کنن، ردیابی مشکلات ناشی از ادغام اونها بسیار آسون‌تره.همه چیز بیشتر از چیزی که فکر کنید طول می‌کشه. مخصوصا در برنامه نویسی. تخمین این که یک فیچر چقدر زمان می‌بره، حتی اگر همه چیز به خوبی پیش بره، خیلی سخته. هنگام توسعه نرم‌افزار خیلی مواقع با مشکلات غیرمنتظره مواجه می‌شیم: یک مرج ساده باعث ایجاد یک باگ می‌شه یا ارتقاء یک فریم‌ورک باعث می‌شه بعضی فراخوانی‌های توابع نیاز به تغییر داشته باشن. به نظرم قانون هافستاتر خیلی درسته: همیشه بیشتر از چیزی که انتظار دارین طول می‌کشه، حتی اگر قانون هافستاتر رو در نظر بگیرین!اول کد فعلی رو درک کنید. کدنویسی اکثرا نیازمند تغییر کد موجوده. حتی اگر دارید یک فیچر جدید توسعه می‌دید در نهایت باید در برنامه فعلی بشینه. و قبل از اینکه بتونید فیچر جدیدی به سیستم اضافه کنید، باید راه حل فعلی رو درک کنید. وگرنه ممکنه تصادفا بعضی از عملکردهای فعلی رو خراب کنید. این یعنی خوندن کد مهارتیه که به اندازه نوشتن کد ضروریه. این هم یک دلیل دیگه که چرا تغییرات به ظاهر کوچیک می‌تونه زمان زیادی طول بکشه چون باید قبلش بستری (context) که در اون تغییر ایجاد می‌کنید رو درک کنید.بخونید و اجرا کنید. دو روش مکمل برای درک کد وجود داره: کد رو بخوانید و کد رو اجرا کنید. اجرای کد می‌تونه کمک بزرگی باشه تا بفهمید کد داره چه کاری انجام می‌ده اما حتما همزمان از هر دو روش استفاده کنید.همیشه باگ وجود داره. من رویکردهای توسعه‌ی نرم‌افزاری که ادعا می‌کنند «بار اول کار درست رو انجام بده» دوست ندارم. مهم نیست چقدر تلاش کنیم، همیشه باگ‌هایی وجود دارن. تعریف باگ هم تقریباً همینه: «فکرش رو نکرده بودیم!». رویکرد بهتر اینه که سیستمی داشته باشیم که باگ‌ها رو سریع پیدا و رفع کنیم.مشکلات گزارش شده رو حل کنید. هر توسعه‌دهنده‌ای باید بخشی از زمانش رو صرف رسیدگی به مشکلات گزارش شده مشتریان و رفع اشکال اونها کنه. این کار درک بسیار بهتری از نحوه استفاده‌ کاربران از سیستم و تناسب طراحی سیستم با نیاز کاربران بهتون می‌ده. این کار یک روش عالی هم هست برای قبول مسئولیت سیستمی که توسعه دادین. هیچ کدوم از این مزایا رو از دست ندید.مشکل رو بازتولید (reproduce) کنید. اولین قدم در رفع یک باگ، بازتولیدشه. اینطوری وقتی رفع باگ انجام شد، می‌تونید مطمئن باشید که مشکل برطرف شده. این قانون ساده کمک می‌کنه چیزی رو به اشتباه مشکل فرض نکنید و مطمئن باشید که راه‌حل‌تون واقعاً همون کاری رو که فکر می‌کنین انجام می‌ده.خطاهای شناخته شده رو رفع کنید بعد ببینید چی باقی مونده. گاهی اوقات چندتا مشکل وجود داره که براتون شناخته شده هستن. اشکالات مختلف ممکنه با هم تعامل داشته باشن و باعث اتفاقات عجیب و غریب بشن. پس به جای اینکه برای پیدا کردن دلیل این اتفاق‌ها تلاش کنید، اول مشکلات شناخته شده رو رفع کنید و بعد ببینید چه علائمی هنوز باقی مونده.چیزی رو تصادفی فرض نکنید. وقتی در حال تست و عیب یابی هستید هرگز چیزی رو اتفاقی درنظر نگیرین. شما یک مقدار تایمر رو تغییر دادید حالا سیستم بیشتر ری‌استارت می‌شه؟ این تصادفی نیست. یک فیچر جدید اضافه شد و یک فیچر نامرتبط کندتر شد؟ این تصادفی نیست. به جای این فرضیه‌ها تحقیق کنید.با Timestampها ارتباط برقرار کنید. وقتی دیباگ می‌کنید از timestamp رویدادها کمک بگیرید. مثلا اگر سیستم ری‌استارت شده و ریکوئستی حدود 3000 میلی ثانیه قبلش ارسال شده، احتمالا یک تایمر رویدادی رو راه‌اندازی کرده که منجر به ری‌استارت شدن سیستم شده.ارتباط چهره به چهره بیشترین پهنای باند رو داره. وقتی بحث چگونگی حل یک مشکل در میونه، چهره به چهره بودن ارتباط به تماس ویدیویی، چت یا ایمیل برتری داره. اغلب از اینکه چقدر راه حل‌ها پس از بحث حضوری با همکاران باکیفیت‌تر هستن شگفت زده می‌شم.اردک پلاستیکی. هر وقت گیر کردید، پیش یکی از همکاران‌تون برید و مشکل رو براش توضیح بدید. خیلی وقت‌ها حین صحبت متوجه می‌شید مشکل چیه. حتی اگر همکارتون یک کلمه هم حرف نزنه. شبیه جادو به نظر میاد اما این کار به طرز شگفت انگیزی تاثیر می‌ذاره.بپرسید. خواندن و اجرای کد معمولا برای فهمیدن اینکه کد چه کاری انجام می‌ده یا چطور کار می‌کنه عالیه. اما اگر براتون مقدوره که از شخصی آگاه (شاید برنامه‌نویس قبلی کد) سوال بپرسید، گزینه‌ی خوبی دارین. توانایی پرسیدن سؤالات درست می‌تونه در عرض چند دقیقه اطلاعاتی رو بهتون بده که شاید پیدا کردنشون با روش‌های دیگه روزها طول می‌کشید.اعتبار رو به اشتراک بذارید. جایی که اعتبار دادن ارزش محسوب می‌شه حتما به همکارتون اعتبار بدید. مثلا بگید: «امیر ایده داد که این رو امتحان کنیم...» (اگر این کار رو کرده بود البته)، به جای اینکه بگید «ما این رو امتحان کردیم ...». به این که کی کمک و کی مشارکت کرده اشاره کنید.امتحانش کنید. اگر مطمئن نیستید که یک فیچر زبان‌برنامه‌نویسی مشخص چطور کار می‌کنه، یک برنامه کوچک بنویسید که نحوه عملکرد فیچر رو بهتون نشون بده. همین کار موقع تست سیستمی که در حال توسعه‌اش هستید هم صدق می‌کنه. اگر این پارامتر را روی 1- قرار بدم چی می‌شه؟ اگر این سرویس موقع ری‌استارت سیستم خاموش بشه چه اتفاقی می‌افته؟ در عملکرد کد کاوش کنید. سر و کله زدن با کد اشکالاتش رو آشکار می‌کنه و در عین حال درک‌تون از نحوه عملکرد سیستم رو عمیق‌تر می‌کنه.بخوابید. اگر روی یک مساله‌ی سخت کار می‌کنید، سعی کنید قبل از تصمیم‌گیری، خواب شبانه داشته باشید. این طوری با اینکه مستقیما بهش فکر نمی‌کنید، ضمیر ناخودآگاه‌تون روی مساله کار می‌کنه. در نتیجه روز بعد مساله براتون واضح‌تر می‌شه.تغییر ایجاد کنید. از تغییر نقش یا شغل نترسید. کار با افراد مختلف، روی یک محصول متفاوت یا در یک شرکت متفاوت، نوعی محرکه.به یادگیری ادامه دهید. یکی از خوبی‌های توسعه نرم‌افزار اینه که همیشه فضایی برای یادگیری و دانستن بیشتر وجود داره. زبان‌ها و ابزارهای برنامه‌نویسی مختلف رو امتحان کنید، کتاب‌هایی در زمینه توسعه نرم‌افزار بخوانید، در دوره‌های MOOC شرکت کنید. به زودی پیشرفت‌های کوچکی ایجاد می‌شن که تفاوتی واقعی در دانش و توانایی‌هاتون ایجاد می‌کنند.</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Sat, 02 Mar 2024 22:46:33 +0330</pubDate>
            </item>
                    <item>
                <title>زبان Dart را بیشتر بشناسیم: Pattern</title>
                <link>https://virgool.io/flutter-community/%D8%B2%D8%A8%D8%A7%D9%86-dart-%D8%B1%D8%A7-%D8%A8%DB%8C%D8%B4%D8%AA%D8%B1-%D8%A8%D8%B4%D9%86%D8%A7%D8%B3%DB%8C%D9%85-pattern-oq9zwex6dnxt</link>
                <description>در ادامه‌ی پست های آشنایی با قابلیت‌های جدید زبان دارت، در این پست به بررسی الگوها (Patterns) می‌پردازم. الگوها در نسخه‌های بعد از دارت 3 در دسترس هستند. همونطور که از نامش پیداست، هدف این هست که تطبیق (match) الگویی از مقادیر رو با یک سری مقدار واقعی بررسی کنه. الگو قراره چه کاری انجام بده؟ به طور کلی، یک الگو بسته به شکلش (shape) ممکنه یک مقدار رو تطبیق بده یا یک مقدار رو تخریب کنه (destructure: یعنی ساختار یک مقدار رو از بین ببره) و یا هر دو کار رو انجام بده. برای شروع شاید این تعریف کوتاه واضح نباشه. در ادامه بیشتر در موردش توضیح میدم.بریم سراغ بخش اول تعریف بالا. تطبیق الگو این امکان رو به شما میده که: چک کنید آیا یک مقدار یک شکل مشخص داره یا نهبرابریش با یک مقدار ثابت مشخص رو چک کنیدبرابریش با مقدار دیگه‌ای رو چک کنیدنوع داده اش رو چک کنیدحالا تخریب الگو چیه؟ تخریب الگو با یک syntax مناسب این امکان رو برای شما فراهم می‌کنه که مقداری رو به قسمت‌های تشکیل دهنده‌اش بشکنید. تطبیقالگو همیشه یک مقدار رو تست میکنه تا تشخیص بده که آیا این مقدار اون چیزی که شما انتظار دارید هست یا نه. به عبارت دیگه دارید تطبیق یک مقدار رو با یک الگوی مشخص چک می‌کنید. این که الگو قراره چه چیزی رو چک کنه به نوع الگو بستگی داره. برای مثال الگوی مقدار ثابت (constant pattern) وقتی تطبیق  پیدا میکنه که متغیر با یک مقدار ثابت برابر باشه:switch (number){
   // Constant pattern matches if 1 == number.
   case 1:
      print(&#039;one&#039;);
}خیلی از الگوها از زیرالگوها (subpatterns) استفاده می‌کنند که بهشون الگوهای inner و outer میگیم. الگوها به صورت بازگشتی در زیرالگوهاشون تطبیق داده می‌شن. برای مثال فیلدی که الگوی نوع مجموعه (collection type) داره میتونه خودش الگوی متغیر یا الگوی ثابت داشته باشه:const a = &#039;a&#039;;
const b = &#039;b&#039;;
switch (obj){
    case [a, b]:
        print(&#039;$a, $b&#039;);
}در مثال بالا اول الگوی لیست [a, b] تطبیق داده میشه یعنی چک میشه که آیا obj یک لیست با دو فیلد هست یا نه. بعد چک میشه که آیا فیلدها با زیرالگوهای ثابت &#x27;a&#x27; و &#x27;b&#x27; مطابق هستن یا نه.تخریبوقتی یک object و الگو تطبیق پیدا کنند، الگو می‌تونه به داده های object  دسترسی پیدا کنه و اونها رو استخراج کنه. به عبارت دیگه، الگو object رو تخریب می‌کنه:var numList = [1, 2, 3];
// List pattern [a, b, c] destructures the three elements from numList...
var [a, b , c] = numList;
// ...and assigns them to new variables.
print(a + b + c);موضوع جالب دیگه اینکه می‌تونید هر نوع الگویی رو داخل الگوی تخریب قرار بدید. در مثال زیر الگوی case یک لیست دو عنصری رو تطبیق میده که عنصر اولش a یا b هست:switch (list){
   case [&#039;a&#039; || &#039;b&#039;, var c]:
       print(c);
}کجا می‌تونیم از الگوها استفاده کنیم؟در زبان دارت از الگوها در موارد زیر می‌تونید استفاده کنید:اعلان و مقداردهی متغیرهای محلیحلقه های  for و for-inدستورات if-case و switch-caseجریان های کنترلی مثل collection literalsاعلان متغیرها: از الگوی اعلان متغیر (variable declaration) می‌تونید در هرجایی که دارت اجازه‌ی تعریف متغیر محلی میده استفاده کنید. الگو، کار تطبیق مقدار با سمت راست اعلان رو انجام میده. وقتی مطابقت انجام شد، مقدار تخریب میشه و در متغیر محلی جدید قرار می‌گیره:// Declares new variables a, b, and c.
var (a, [b, c]) = (&#039;str&#039;, [1, 2]);الگوی اعلان متغیر باید با var و یا final و سپس الگو شروع بشه.مقداردهی متغیر: الگوی مقداردهی متغیر (variable assignment)، سمت چپ یک مقداردهی قرار میگیره. اول object تطبیق یافته رو تخریب میکنه. بعد مقدار رو به متغیرهای موجود تخصیص میده (به جای اینکه به یک متغیر جدید مقیدش کنه). مثال زیر رو ببینید که از الگوی مقداردهی برای جابجایی مقدار دو متغیر استفاده می‌کنه و این کار رو بدون استفاده از متغیر موقت سوم انجام میده:var (a, b) = (&#039;left&#039;, &#039;right&#039;);
(b, a) = (a, b);   // Swap
print(&#039;$a $b&#039;);   // Prints &amp;quotright left&amp;quotعبارات switch: هر case clause یک الگو داره. در یک case می‌تونید هر نوع الگویی رو استفاده کنید. الگوهای case جریان کنترل برنامه رو به این صورت تغییر می‌دن:آبجکتی که روی اون switch زدیم رو تطبیق می‌ده و تخریب می‌کنهاگر آبجکت تطبیق پیدا نکنه به اجرا ادامه می‌دهمقادیری که یک الگو تخریب می‌کنه متغیر محلی همون case هستند که تخریب در اون انجام شده. در واقع scope اونها بدنه همون case هست. switch (obj){
   //Matches if 1 == obj.
   case 1:
     print(&#039;one&#039;);

   /// Matches if the value of obj is between the constant values of &#039;first&#039; and &#039;last&#039;.
   case &gt;= first &amp;&amp; &lt;=last :
      print(&#039;in range&#039;);

   /// Matches if obj is a record with two fields, then assigns the fields to &#039;a&#039; and &#039;b&#039;.
   case (var a, var b):
      print(&#039;a = $a, b=$b&#039;);
   default:
}الگوهای یا-منطقی (logical-or) وقتی می‌خوایم یک بدنه رو در switch با چند case به اشتراک بگذاریم کاربردی هستند:var isPrimary = switch (color){
   Color.red || Color.yellow || Color.blue =&gt; true,
    _ =&gt; false
};شاید بگید عبارت های switch می‌تونند بدون استفاده از الگوی یا-منطقی هم چند case داشته باشند که یک بدنه رو با هم به اشتراک گذاشتند اما این الگو وقتی می‌خواید چند case یک محافظ (guard) رو به اشتراک بگذارند هم به کار می‌ره و این ویژگی منحصربفردش کرده (به کاربرد when در این مثال دقت کنید):switch (shape) {
   case Square(size: var s) || Circle(size: var s) when s &gt; 0:
       print(&#039;Non-empty symmetric shape&#039;);
}حلقه های for و for-in: الگوها رو می‌تونید در حلقه های for و for-in هم استفاده کنید تا روی عناصر یک مجموعه حرکت کنید و مقدار هر عنصرش رو تخریب کنید. مثال زیر رو ببینید که از تخریب آبجکت در یک حلقه for-in استفاده می‌کنه تا آبجکت های MapEntry که فراخوانی Map&gt;.entries&gt; برمی‌گردونه رو تخریب کنه:Map&lt;String, int&gt; hist = {
   &#039;a&#039;: 23,
   &#039;b&#039;: 100,
};
for (var MapEntry(key: key, value: count) in hist.entries){
    print(&#039;$key occurred $count times&#039;);
} الگوی آبجکت چک می‌کنه که hist.entries از نوع MapEntry باشه. بعد سراغ زیرالگوی فیلدهای دارای نام key و value می‌ره (اینجاست که الگوها به صورت بازگشتی چک می‌شن). در هر تکرار getter های key و value رو روی MapEntry صدا می‌زنه و نتیجه رو در متغیرهای محلی key و count قرار می‌ده. تخصیص مقدار برگشتی یک getter به متغیری همنام با خودش یک الگوی رایج هست. برای همین الگوهای آبجکت می‌تونند نام getter رو از زیرالگوی متغیر استنتاج کنند. به این ترتیب از چیز اضافه ای مثل key: key خلاص می‌شیم و میتونیم بنویسیم key: for (var MapEntry(: key, value: count) in hist.entries){     
     print(&#039;$key occurred $count times&#039;);
2کار}خب این هم از معرفی انواع الگوها و نحوه به‌کارگیری‌شون. استفاده از الگوها و کلا هر کدوم از قابلیت های جدید زبان دارت، سطح برنامه‌نویسی‌مون با این زبان رو ارتقا میده و مهارت کدنویسی ما به این زبان رو نشون میده.  مثال‌های کاربردی‌تری رو می‌تونیم در ادامه بررسی کنیم. اگر مایل هستید کامنت بذارید تا در مقاله‌ی دیگه‌ای مثال‌ها و کاربردهای بیشتری از الگوها رو با هم یاد بگیریم.</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Mon, 05 Jun 2023 12:47:12 +0330</pubDate>
            </item>
                    <item>
                <title>زبان Dart را بیشتر بشناسیم: record</title>
                <link>https://virgool.io/flutter-community/%D8%B2%D8%A8%D8%A7%D9%86-dart-%D8%B1%D8%A7-%D8%A8%DB%8C%D8%B4%D8%AA%D8%B1-%D8%A8%D8%B4%D9%86%D8%A7%D8%B3%DB%8C%D9%85-record-yjmweepmfgfa</link>
                <description>در این مقاله به بهانه انتشار نسخه 3.0.0 زبان دارت (Dart)، به قابلیتی جدید در این زبان به نام record می‌پردازم که در نسخه های بعد از 3.0 دارت دردسترس قرار داره. پیش از این، یک تابع در زبان دارت تنها می‌تونست یک مقدار برگشتی داشته باشه. در نتیجه توابعی که نیاز به برگردوندن چند مقدار داشتند یا باید اونها را در نوع داده هایی مانند لیست یا map بسته‌بندی می‌کردند یا کلاس جدیدی برای نگهداری این مقادیر تعریف می‌کردند.استفاده از ساختمان داده‌های untyped منجر به تضعیف type safety می‌شه (دارت یک زبان type safe است یعنی نوع داده‌ی یک متغیر رو به صورت ایستا و نیز در زمان اجرا چک می‌کنه تا تضمین کنه نوع داده تغییر نکرده). تعریف کلاس تنها با هدف انتقال مقدار بین توابع، سهولت و سرعت توسعه رو کاهش می‌ده. این دلایل بود که باعث شد record به زبان دارت اضافه بشه. با استفاده از recordها می‌تونید ساختمان داده واضحی برای ذخیره چند مقدار داشته باشید. تابع زیر رو در نظر بگیرید که نام و قد رو از JSON ورودی می‌خونه و هر دو رو در یک record برمی‌گردونه:(String, int) userInfo(Map&lt;String, dynamic&gt; json){
     return (json[&#039;name&#039;] as String, json[&#039;height&#039;] as int);
}این syntax باید برای توسعه‌دهنده‌های دارت آشنا باشه. record شبیه یک لیست مثل [&#x27;Jack&#x27; ,&#x27;Manager&#x27;] است منتها از پرانتز به جای براکت استفاده شده. record در دارت یک قابلیت کلی هست یعنی فقط محدود به استفاده به عنوان مقادیر برگشتی تابع نیست. میتونید record رو در متغیر ذخیره کنید، داخل لیست قرار بدید، به عنوان کلید در یک Map استفاده کنید یا یک record حاوی record های دیگه‌ای ایجاد کنید. حتی می‌تونید در record فیلدهای بدون نام (مشابه چیزی که در کد بالا دیدید) یا فیلدهای دارای نام (named) تعریف کنید مثل:(42, description: &#039;Meaning of life&#039;)توجه کنید که recordها value type هستند (در مقابل reference type ها رو داریم که اشاره‌گری به مقدار متغیر در حافظه نگه می‌دارند) و شناسه ندارند. به این ترتیب کامپایلرها می‌توانند در برخی شرایط آبجکت record رو به طور کامل حذف کنند. record ها سایز ثابت دارند و ناهمگون هستند یعنی مقادیر درون record می‌تونند نوع داده‌های متفاوتی داشته باشند. var record = (&#039;first&#039;, a:2, b: true, &#039;last&#039;);یک متغیر از نوع record رو به این‌صورت می‌تونیم تعریف و مقداردهی اولیه کنیم:// Record type annotation in a variable declaration
(String, int) user;
// Initialize it with a record expression
user = (&#039;Jack&#039;, 32);دقت کنید که اگر record با فیلدهای دارای نام تعریف کردید، نام فیلدها هم بخشی از تعریف record محسوب میشه و حتی اگر نوع داده و تعداد فیلدهای دو record یکسان باشه این دو record بخاطر نام فیلدها متفاوت هستند. مثال زیر رو ببینید:({int a, int b}) recirdAB = (a: 1, b: 2); 
({int x, int y}) recirdXY = (x: 3, y: 4); 
//Compile error! These records don&#039;t have the same type.
// recordAB = recordXY;یک موضوع جالب دیگه در مورد record ها این هست که برای تعریف record کلمه کلیدی نداریم. recordها به صورت ساختارمندی نوع شون بر اساس نوع فیلدهاشون تعریف میشه. شکل (shape) یک record (مجموعه فیلدهاش، نوع فیلدهاش و نام فیلدها -اگر دارای نام بودند-) به صورت یکتا نوعش رو مشخص می‌کنه.فیلدهای record رو می‌تونید از طریق متدهای getter از پیش تعریف شده بخونید. چون record ها immutable هستند برای فیلدهاشون انتظار وجود setter نداشته باشید. به مقدار فیلدهای بدون نام record به صورت &lt;position&gt;$ میشه دسترسی داشت. حالا مثالی ببینیم از این که چطور مقادیر فیلدهای دارای نام و بدون نام record رو با استفاده از getter بخونیم:var record = (&#039;first&#039;, a:2, b: true, &#039;last&#039;);

print(record.$1);   //Prints &#039;first&#039;
print(record.a);   //Prints 2
print(record.b);   //Prints true
print(record.$2);   //Prints &#039;last&#039;عملگر == و توابع hashcode به صورت خودکار برای record تعریف شده. دوتا record برابر هستند اگر شکل یکسانی داشته باشند و فیلدهای متناظرشون مقادیر یکسان داشته باشند. چون ترتیب فیلدهای دارای نام بخشی از شکل record نیست، ترتیب شون روی تساوی تاثیر نداره. </description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Fri, 12 May 2023 19:03:19 +0330</pubDate>
            </item>
                    <item>
                <title>تحلیل سایز اپ در فلاتر</title>
                <link>https://virgool.io/@shahpasand/%D8%AA%D8%AD%D9%84%DB%8C%D9%84-%D8%B3%D8%A7%DB%8C%D8%B2-%D8%A7%D9%BE-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-d2imms2rhlyu</link>
                <description>سایز اپلیکیشن برای بسیاری از برنامه نویسان موبایل مساله ای مهم به حساب می آید. از آن جا که فایل APK یا app bundle و یا IPA اپ فلاتری حاوی کدها و منابع مورد نیاز برای اجرای آن است (مثل تصاویر ایستایی که در اپ استفاده شده است)، سایز اپ اهمیت پیدا می‌کند. هر چه اپ بزرگتر باشد، فضای بیشتری روی دستگاه کاربر اشغال می کند، زمان بیشتری برای دانلود آن نیاز است و برای برخی امکانات مفید (مثل Android instant apps) محدودیت به حساب می آید.توجه: اگر اپ را با دستور flutter run اجرا کنید یا دکمه پلی در IDE خود را کلیک کنید در حالت پیشفرض یک نسخه ی دیباگ تولید خواهد شد. دقت کنید که سایز نسخه دیباگ به خاطر سربارهایی که امکان hot reload و دیباگ کردن را فراهم می کنند، بزرگتر از سایز نهایی اپ خواهد بود و بنابراین نمایانگر خوبی برای اپ نهایی که کاربر دانلود خواهد کرد نیست.نسخه بیلد نهایی (release build) مثل نسخه ای که با دستور flutter build apk یا flutter build ios تولید می‌کنید، برای بارگذاری پکیج در پلی استور یا اپ استور مناسب هستند. اما این نسخه ها هم سایز نهایی اپی که کاربر دانلود خواهد کرد را نشان نمی دهند. استورها عموما پکیجی که بارگذاری می‌کنید را مجددا پردازش کرده و آن را طوری می‌شکنند که برای دستگاهی که آن را دانلود می‌کند مناسب باشد.پس برای تخمین سایز نهایی اپ چه روشی مناسب است؟ برای بدست آوردن نزدیک ترین تخمین از سایز اپ در هر پلتفرم از روشی که در ادامه می آید استفاده کنید:اندروید: پس از بارگذاری فایل aab در کنسول گوگل پلی، سایز اپ نهایی را از Android vitals -&gt; App size ببینید: سایز دانلود براساس یک دستگاه xxxhdpi با معماری arm64-v8a محاسبه می شود. سایز دانلود کاربر نهایی بسته به سخت افزار دستگاهش متفاوت است.آی او اس: برای بدست آوردن سایز اپ در آی او اس می توانید گزارش سایز اپ را در Xcode ایجاد کنید. این لینک جزئیات بیشتری در این زمینه در اختیار شما می‌گذارد. برای اندازه‌گیری دقیق سایز اپ آی او اس باید release IPA را در اپ استور کانکت اپل قرار دهید و گزارش سایز اپ را از آنجا بدست آورید. برای اپلیکیشن هایی که با فلاتر توسعه داده می شوند، معمولا سایز IPA بزرگتر از APK است.تحلیل سایز اپبا شروع از فلاتر نسخه 1.22 و DevTools نسخه 0.9.1، ابزار تحلیل سایز برای کمک به توسعه دهندگان جهت درک بخش های تشکیل دهنده نسخه release در دسترس قرار گرفت.ابزار تحلیل سایز را می توانید با اضافه کردن فلگ analyze-size به انتهای دستور بیلد فراخوانی کنید:flutter build apk   --analyze-size  --target-platform=android-arm64
flutter build      appbundle --analyze-size   --target-platform=android-arm64
flutter build ios      --analyze-size
flutter build      linux --analyze-size
flutter build      macos --analyze-size
flutter build      windows --analyze-sizeبیلدی که با این دستور بدست می آورید به دو دلیل از بیلد استاندارد متفاوت است:1- این ابزار، دارت را به نحوی کامپایل می کند که سایز استفاده شده توسط پکیج های دارت را ثبت می کند2- ابزار یک خلاصه از تقسیم سایز اپ در ترمینال نمایش می دهد و یک فایل *-code-size-analysis_*.json ایجاد می کند که برای تحلیل دقیق تر سایز اپ در DevTools به کار می رود.در خلاصه وضعیت سایز اپ شما می توانید با یک نگاه از میزان حجم مصرفی به ازای هر دسته بندی (مثل asset، native code، Flutter library) مطلع شوید. برای کد دارت کامپایل شده سایز هر پکیج نیز مشخص شده است.تحلیل عمیق تر به کمک DevToolsفایل json تولید شده در مرحله قبل را می توان به کمک DevTools بیشتر تحلیل کرد طوری که محتوای اپ تا رسیدن به سطح فایل شکسته می شود. برای این کار دستور flutter pub global run devtools را اجرا و Open app size tool را انتخاب کنید. سپس فایل json را آپلود کنید.علاوه بر تحلیل یک بیلد، می توان دو بیلد را با بارگذاری فایل های json آنها در DevTools، مقایسه کرد.کاهش سایز اپوقتی نسخه نهایی اپ خود را بیلد می کنید، از تگ زیر  استفاده کنید. این تگ می تواند سایز اپ را به میزان قابل توجهی کاهش دهد.--split-debug-infoرعایت موارد زیر نیز به کاهش سایز اپ کمک می کنند:پکیج های مورد استفاده را تا حد ممکن به حداقل برسانیداستفاده از asset را به حداقل برسانید و حجم فایل های png و jpeg را کاهش دهید</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Wed, 15 Sep 2021 23:49:03 +0430</pubDate>
            </item>
                    <item>
                <title>چرخه حیات عناصر ListView در فلاتر</title>
                <link>https://virgool.io/flutter-community/%DA%86%D8%B1%D8%AE%D9%87-%D8%AD%DB%8C%D8%A7%D8%AA-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-listview-ced87srpofw9</link>
                <description>استفاده از ویجت لیست ویو (ListView) در فلاتر، برای نمایش آیتم های قابل اسکرول بسیار رایج است و اگر تجربه‌ی کار با فلاتر را دارید حتما با این ویجت آشنا هستید. آنچه در این مقاله خواهید خواند معرفی ویجت لیست ویو نیست. اگر اپلیکیشن های نسبتا بزرگی با فلاتر ایجاد کرده باشید به خوبی می دانید که لیست ویو یک ویجت مهم و تاثیر گذار در کارایی اپلیکیشن است. در ادامه مقاله، چرخه حیات لیست ویو و تاثیر آن در کارایی اپلیکیشن به همراه راه حل هایی برای بهبود کارایی را بررسی خواهیم کرد.چرخه حیات عناصر لیست ویوایجاد: در حالی که لیست ویو در حال ترسیم (Draw) است، عناصر و استیت های فرزندان قابل مشاهده‌ی آن، بصورت lazy ایجاد خواهد شد (مثل وقتی از listView استفاده می شود) یا یک عنصر بصورت lazy فراهم می‌شود (مثل وقتی ListView.builder استفاده شده است).تخریب: هر وقت فرزندی که از (ناحیه قابل مشاهده‌ی) اسکرول ویو خارج شود، عناصر و استیت های آن از درخت ویجت destroy می شوند. وقتی مجدد به همین قسمت اسکرول کنید، یک فرزند جدید مجددا بصورت lazy  ایجاد خواهد شد؛ با عناصر و استیت جدید.کنترل تخریب: برای اینکه استیت فرزندان را در هنگام اسکرول شدن به خارج از ناحیه قابل مشاهده حفظ کنید، چند روش پیشنهاد می شود:منطقی که برای ساخت استیت ویو نیاز دارید را از زیردرخت فرزندان لیست خارج کنید. مثلا اگر یک لیست از پست ها با تعداد لایک های هر کدام دارید که از سرور دریافت می‌شوند، لیست پست ها و لایک های آنها را در یک مدل و خارج از لیست ویجت دریافت و ذخیره کنید. اجازه بدهید که ویوی زیردرخت فرزندان لیست، به راحتی از لیست مدلی که فراهم کردید، مجددا قابل ایجاد باشد. از StatefulWidget در زیردرخت ویجت فرزندان تنها برای ذخیره‌ی فوری استیت ui استفاده کنید.زیردرخت ویجت فرزند (لیست) را که نیاز به حفظش دارید در ویجت KeepAlive قرار بدید. این ویجت، رندر آبجکتی که زیردرخت فرزند در آن قرار گرفته است را برای KeepAlive بودن علامت گذاری می‌کند. وقتی این رندر آبجکت (و طبیعتا ویجت داخل آن) از ناحیه دید خارج شود، لیست به جای اینکه رندر آبجکت فرزند (و عناصر و استیت های وابسته اش) را تخریب کند، آن در کش (cache) لیست ویو قرار می دهد و وقتی فرزند لیست دوباره به ناحیه دید برگشت، آن را همانطوری که در کش هست ترسیم می کند (البته اگر در این بین dirty نشده باشد. اگر با این مفهوم آشنا نیستید به این مقاله مراجعه کنید). این روش را فقط وقتی می‌توانید به کار ببرید که پارامترهای addAutomaticKeepAlives و addRepaintBoundaries لیست ویو مقدار false داشته باشند چون این پارامترها باعث می‌شوند که لیست ویو، هر زیردرخت ویجت فرزند خود را  با ویجت های دیگری wrap کند. (در ادامه بیشتر با عملکرد این دو پارامتر آشنا می شویم.)از ویجت های  AutomaticKeepAlive (بصورت پیشفرض وقتی addAutomaticKeepAlives مقدار true دارد درج می شوند) استفاده کنید. AutomaticKeepAlive اجازه می‌دهد که ویجت های فرزند، کنترل کنند که آیا زیردرخت باید زنده نگه داشته شود یا نه. این رفتار در تضاد با KeepAlive است که بدون هیچ شرطی زیردرخت را زنده نگه می دارد (برای همین وقتی از KeepAlive استفاده می‌شود باید به این پارامتر مقدار false داد). به عنوان مثال ویجت EditableText وقتی فوکوس دارد، به زیردرخت لیست سیگنال زنده ماندن می‌دهد. اگر فوکوس نداشته باشد، وقتی زیردرخت فرزند از ناحیه دید اسکرول خارج شود تخریب می شود. فرزندان AutomaticKeepAlive معمولا با استفاده از AutomaticKeepAliveClientMixin سیگنال زنده ماندن می دهند. اینجا می توانید مثالی را ببینید که توضیح می دهد چطور می توانید ویجتی بنویسید که به همین روش سیگنال زنده ماندن بدهد.</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Mon, 10 May 2021 10:35:49 +0430</pubDate>
            </item>
                    <item>
                <title>ویجت‌ها در فلاتر: چرخه‌ی حیات</title>
                <link>https://virgool.io/flutter-community/%D9%88%DB%8C%D8%AC%D8%AA-%D9%87%D8%A7-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%DA%86%D8%B1%D8%AE%D9%87-%DB%8C-%D8%AD%DB%8C%D8%A7%D8%AA-inzylcc3jmod</link>
                <description>در مقاله‌ی قبل، برخی مفاهیم پایه در رابطه با ویجت‌ها را بررسی کردیم. در این مقاله به شرح و بررسی چرخه‌ی حیات ویجت‌ها در فلاتر می‌پردازیم.   https://virgool.io/@shahpasand/%D9%88%DB%8C%D8%AC%D8%AA-%D9%87%D8%A7-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%D9%85%D9%81%D8%A7%D9%87%DB%8C%D9%85-%D9%BE%D8%A7%DB%8C%D9%87-babfp7aaiii4 فرایند ایجاد و بروزرسانی ویجت‌ها، بارها و بارها در طول اجرای برنامه توسط فلاتر انجام می‌شود. بسته به نوع ویجت (Stateless یا Stateful)، متدهای متفاوتی اجرا خواهند شد اما چیزی که ثابت است این است که ما همیشه از فلاتر (به وسیله‌ی callback) نوتیفیکیشن دریافت می‌کنیم و این به ما کمک می‌کند تا رفتار ویجت را سفارشی کنیم. در ادامه چرخه‌ی حیات هر یک از انواع ویجت را بررسی خواهیم کرد.ویجت‌های Statelessمتدهایی که در هنگام مدیریت ویجت‌های Stateless فراخوانی می‌شوند سرراست هستند:1- برای شروع، سازنده‌ی ویجت فراخوانی می‌شود.2- پس از اینکه سازنده اجرا شد، متد ()build بصورت خودکار توسط سیستم صدا زده می‌شود. این متد یک عنصر بصری جدید برمی‌گرداند که به درخت ویجت (در موقعیتی که آبجکت BuildContext که به عنوان پارامتر ورودی دریافت شده است، مشخص می‌کند) اضافه می‌شود .class MyWidget extends StatelessWidget {
   //XXX: called 1st...
   MyWidget();
   //XXX: called 2nd...
   @override
   Widget build(BuildContext cntxt) {
       return Row(...);
   }
}در نتیجه نمودار توالی ساده و به صورت زیر است:چرخه‌ی حیات ویجت‌های Statelessویجت های Statefulابتدا سازنده‌ی ویجت (Stateful) فراخوانی می شود.سپس متد ()createState به صورت خودکار توسط سیستم فراخوانی می شود. این متد باید آبجکت state را که پشت این ویجت وجود دارد، نمونه سازی و ایجاد کند.class MyOtherWidget extends StatefulWidget {
   MyOtherWidget();
   @override
   State createState() {
      return _MyOtherWidgetState();
   }
}
class _MyOtherWidgetState extends State&lt;MyOtherWidget&gt; {
     ...
}به خاطر داشته باشید که ویجت‌های Stateful همواره دارای state (داده های پویایی که در طول زمان حیات ویجت آپدیت می شوند) هستند بنابراین به یک کلاس جدید نیاز است که آن را کپسوله سازی کند.کلاس state  که همواره از &lt;&gt;State ارث بری می کند، نوع ویجت را به عنوان یک پارامتر generic دریافت می‌کند. علاوه بر این، ویجت وابسته از طریق مشخصه‌ی widget در این کلاس قابل دسترسی است.با این توضیح، نمودار این سناریو واضح خواهد بود:چرخه‌ی حیات ویجت‌های Statefulاگر این نمودار را با نمودار قبل مقایسه کنید حتما این سوال برای شما پیش می آید که چه کسی مسئول ایجاد ویو برای ویجت خواهد یود؟ چون در حال حاضر فقط یک آبجکت state را ایجاد کرده ایم.آبجکت Stateآبجکت‌های State حاوی چندین متد در چرخه‌ی حیات خود هستند که یکی از آنها همان دوست قدیمی، متد ()build است:class _MyOtherWidgetState extends State&lt;MyOtherWidget&gt; {
  @override
  Widget build(BuildContext cntxt) {
      return Column(...);
   }
}بنابراین آبجکت‌های State در حقیقت مسئول ساخت واسط کاربری ویجت‌های وابسته به خود هستند. گرچه این ممکن است در ابتدا عجیب به نظر برسد، در نظر داشته باشید که آبجکت های State:داده‌ی پویایی که ویجت نمایش خواهد داد را نگهداری می‌کنندمی توانند سیستم را وادار به ترسیم مجدد UI کننداینطور به موضوع نگاه کنید که تصمیم گیری در مورد چگونگی نمایش داده را انجام می‌دهند. برای مثال، وقتی یک آبجکت State یک مجموعه داده از اطلاعات کاربران را نگهداری می‌کند باید مشخص کند که واسط کاربری آن بصورت یک لیست است یا جدول.جریان اجرا در چرخه حیات آبجکت state با فراخوانی متدها (یا callbackهای) زیر شکل می‌گیرد:مطابق معمول، سازنده‌ (آبجکت State) فراخوانی می‌شود. به محض اینکه اجرای سازنده با موفقیت پایان گرفت، ویجت وابسته، به درخت ویجت افزوده می‌شود و در وضعیتی با عنوان MOUNTED (یعنی یک BuildContext یا موقعیت مشخص در درخت دارد) قرار می‌گیرد.سپس ()initState یک بار و به صورت خودکار توسط سیستم فراخوانی می‌شود. این متد مکررا برای اجرای تسک‌های مقداردهی اولیه، مانند قالب‌بندی اطلاعات (format) برای نمایش و یا subscribe کردن به مخزن داده‌ای (data source) که آیتم‌های داده را منتشر می‌کند، مورد استفاده قرار می‌گیرد.سپس متد ()didChangeDependencies به طور خودکار توسط سیستم فراخوانی می‌شود. در این لحظه، ویجت DIRTY درنظر گرفته می‌شود، چرا که هنوز محتوای آن روی صفحه به نمایش درنیامده است (یعنی هنوز متد ()build اجرا نشده است).نکته: اگر از مخزن داده استفاده کرده باشید، این متد هر گاه داده‌ی جدیدی منتشر شود فراخوانی خواهد شد. این یعنی بر خلاف ()initState این متد ممکن است بارها فراخوانی شود.سیستم متد ()build را فراخوانی می‌کند و واسط کاربری ساخته می‌شود. اکنون آبجکت CLEAN است.متد ()dispose هنگامی که ویجت از درخت ویجت حذف شد، به صورت خودکار توسط سیستم فراخوانی می‌شود. این متد غالبا برای آزادسازی منابع مورد استفاده قرار می‌گیرد برای مثال unsubscribe کردن از مخزن داده.برای ویجت Stateful جریان‌های اجرای دیگری هم ممکن است اتفاق بیفتد:متد ()setState هر بار که آن را صریحا در کلاس State فراخوانی کنیم اجرا می‌شود. اجرای آن، ویجت را      مجددا در وضعیت DIRTY قرار می‌دهد و سیستم را وادار می‌کند تا UI را مجدد ترسیم کند. این متد تفاوت اصلی ویجت های Stateful و Stateless محسوب می‌شود زیرا این امکان را فراهم می‌کند که برای ویجت خود، از سیستم درخواست آپدیت داشته باشیم. اجرای این متد معمولا در پی تعامل کاربر (مثلا کلیک روی یک دکمه) و برای بروزرسانی UI صورت می‌گیرد.متد ()didUpdateWidget هر بار که ویجت والد در درخت ویجت، محتوای خود را آپدیت می‌کند توسط سیستم فراخوانی می‌شود. ویجتی که به State وابسته است برچسب DIRTY می‌خورد و ترسیم مجدد      آن در دستور کار قرار می‌گیرد.اگر تمامی جریان های اجرا را در کنار هم بگذاریم و State های داخلی را هم درنظر بگیریم نمودار زیر را خواهیم داشت:چرخه‌ی حیات آبجکت Stateجمع‌بندیچرخه‌ی حیات ویجت‌های Stateless تنها آبجکت معادل را ایجاد و UI آن را ترسیم می‌کند.چرخه‌ی حیات ویجت‌های Stateful سازنده را فراخوانی می‌کند و آبجکت State را برای مدیریت داده‌های پویا مشخص می‌کند.چرخه‌ی حیات آبجکت‌های  State شامل چندین callback است. بروزرسانی و رفرش آبجکت می‌تواند توسط سیستم (وقتی عنصری که به آن وابسته هستیم تغییر می‌کند) و یا توسط خود آبجکت State درخواست شود.</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Sun, 21 Mar 2021 19:35:48 +0330</pubDate>
            </item>
                    <item>
                <title>ویجت‌ها در فلاتر: مفاهیم پایه</title>
                <link>https://virgool.io/@shahpasand/%D9%88%DB%8C%D8%AC%D8%AA-%D9%87%D8%A7-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%D9%85%D9%81%D8%A7%D9%87%DB%8C%D9%85-%D9%BE%D8%A7%DB%8C%D9%87-babfp7aaiii4</link>
                <description>اگر به تازگی شروع به یادگیری فلاتر کرده‌اید بهتر است با برخی مفاهیم پایه در فلاتر آشنا شوید. حتی اگر برنامه‌نویسی باتجربه در فلاتر محسوب می شوید، مرور این مفاهیم به درک بهتر شما از این فریم ورک کمک خواهد کرد.فلاتر کیت توسعه‌ی نرم افزار موبایل است که توسط گوگل برای طراحی اپلیکیشن چند پلتفرمی (اپلیکیشنی که یک کد پایه یکسان دارد اما بر روی پلتفرم‌های متفاوت قابل اجرا است) توسعه داده شده است. ویجت‌ها (Widgets) بلاک سازنده‌ی اصلی آن به شمار می‌روند. اما ویجت چیست؟ در این مقاله به دنبال پاسخ این سوال هستیم.ویجت فلاتر در واقع کلاسی است که می‌داند چطور واسط کاربری (User Interface) خود را توصیف کند. اپ‌های فلاتر مجموعه‌ی بزرگی از ویجت‌ها هستند. از ویجت‌ها نه تنها برای نمایش صریح عناصر بصری (مانند دکمه‌ها و کادرهای متنی)، بلکه برای نمایش عناصر انتزاعی مثل ترازبندی نیز استفاده می‌شود.ویجت‌ها چرخه‌ی حیات مخصوص به خود را دارند که دانستن آن به درک عمیق تر فرایندهای پشت صحنه و کشف برخی مشکلات رایج بسیار کمک می‌کند. اما پیش از آن که با چرخه‌ی حیات ویجت‌ها آشنا شویم، بهتر است برخی مفاهیم پایه را بدانیم. (سعی می‌کنم در مقاله‌ای دیگر درباره ‌چرخه‌ی حیات ویجت‌ها توضیح دهم.)درخت ویجت: همانطور که پیش از این اشاره شد، هر اپلیکیشن فلاتر مجموعه‌ای است از ویجت‌هایی که آن را تشکیل می‌دهند. هر جنبه از برنامه به کمک ویجت به نمایش در می آید:مولفه‌های Layout که نقش آنها سازماندهی محتوای نمایش داده شده روی صفحه است (مانند Grid، Container، Row، Column).ویوهای ساختاری (مانند Text، Image، Icon، Button)عناصر استایل (مانند TextStyle، Theme)انیمیشن‌ها (مانند FadeIn، FadeOut، Matrix)آبجکت‌های موقعیت و فاصله گذاری (Alignment، Padding، Margin)همه‌ی ویجت‌ها در مجموعه‌ی ویجت‌ها با گسترش ساختار درختی مرتب می شوند: هر عنصر (گره) می تواند فرزندانش را مشخص کند بنابراین یک رابطه‌ی والد-فرزندی بین آنها برقرار می‌شود.ترکیب (Composition): برخلاف دیگر فریم ورک‌ها و زبان‌های برنامه نویسی، فلاتر استفاده از ترکیب را بر وراثت ارجحیت می دهد. وقتی که یک عنصر UI جدید می‌سازید، به جای اینکه آن را زیرنوع عنصری موجود قرار دهد (subtyping)، آن را داخل عنصر دیگری قرار می دهد (wrapping).برای مثال فرض کنید صفحه‌ای داریم حاوی یک دکمه و یک متن روی آن. درخت ویجت در این صفحه چطور خواهد بود؟ احتمالا چیزی شبیه به تصویر زیر:مثالی از درخت ویجتپس یک Container داریم که ریشه‌ی زیردرخت است و دکمه زیر مجموعه‌ی آن است. دکمه نیز یک فرزند برای نمایش متن روی آن دارد و متن یک استایل دارد که احتمالا رنگ و فونت آن را سفارشی می‌کند. این ساختار درختی آنقدر ادامه پیدا می‌کند تا به گره برگ برسد که فرزندی ندارد.چرخه‌ی حیات ویجت: بطور کلی وقتی در مورد چرخه‌ی حیات ویجت صحبت می‌کنیم از یک فرایند تکرارشونده به صورت زیر صحبت می‌کنیم:1- ایجاد ویجت2- بروزرسانی واسط کاربری ویجت برای بروزرسانی محتوای نمایش داده شده توسط ویجتاین فرایند بارها توسط فلاتر تکرار می‌شود و ما هر بار نوتیفیکیشن‌هایی (از طریق فراخوانی متدهای callback) دریافت می‌کنیم که به ما اجازه می‌دهد رفتار ویجت‌ها را بروزرسانی کنیم.انواع ویجت ها: در یک دسته بندی کلی، ویجت‌ها در فلاتر می‌توانند Stateless یا Stateful باشند:ویجت های Stateless تصنعی (dummy) هستند یعنی مقادیر ایستا، مثل متن و آیکن نمایش می دهند. UI آنها پس از ایجاد دیگر تغییر نخواهد داشت. مولفه‌های Stateless توسط سیستم مدیریت می‌شوند و در هنگام نیاز ایجاد شده و از بین می‌روند (dispose).در مقابل ویجت های Stateful هوشمند (smart) هستند و محتوای پویا نمایش می‌دهند (مقادیر آنها در طول حیات شان ممکن است تغییر کند). مولفه‌های Stateful روی چرخه‌ی حیات‌شان کنترل بیشتری دارند: هنوز هم توسط سیستم مدیریت می‌شوند با این تفاوت که (وقتی محتوا تغییر کرده باشد) می‌توانند بروزرسانی ظاهر خود را درخواست کنند.اشیاء State: ویجت‌های Stateful یک شی state دارند که مسئول ذخیره سازی داده‌ای است که ویجت آن را نمایش می‌دهد.ساخت ویجت: ویجت‌ها حین اجرای متد ()build ساخته می‌شوند. این متد یک مولفه‌ی بصری می‌سازد که به درخت ویجت اضافه خواهد شد: Widget build(BuildContext context) {
    return Container(
       color: Colors.red,
       child: ...
    );
}همانطور که می‌بینید تنها ورودی متد یک نمونه از کلاس BuildContext است. چرا برای ایجاد یک عنصر جدید به context احتیاج داریم؟از آنجا که ویجت‌ها در درخت ویجت پراکنده شده اند، فلاتر نیاز به روشی دارد که به عنصر مشخصی اشاره کند. برای این منظور از نمونه‌های کلاس BuildContext استفاده می‌کند. یک build context در واقع ارجاعی به موقعیت مشخص یک ویجت در درخت است.به دلیل اهمیت درک چرخه‌ی حیات انواع ویجت ها در فلاتر، به این مبحث به صورت کامل‌تر در مقاله‌ی دیگری پرداخته‌ام: https://virgool.io/@shahpasand/ویجت-ها-در-فلاتر-چرخه-ی-حیات-inzylcc3jmod جمع‌بندی: مفاهیمی که در این مقاله توضیح داده شد را می‌توان بصورت زیر خلاصه کرد:فلاتر تقریبا هر جنبه از اپ را با استفاده از ویجت به نمایش می‌گذارد.ویجت‌ها کلاس‌هایی هستند که می‌توانند UI را با نمایش محتوای ایستا یا پویا توضیح دهند.فلاتر از ترکیب، برای تودرتو کردن عناصر استفاده می‌کند.همه‌ی ویجت‌ها در کنار هم یک درخت ویجت می‌سازند.کلاس BuildContext این امکان را فراهم می‌کند که هر عنصر ویجت در درخت ویجت قرار گیرد.</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Fri, 12 Feb 2021 10:06:19 +0330</pubDate>
            </item>
                    <item>
                <title>چطور اپلیکیشن های کاراتری با فلاتر بنویسیم؟</title>
                <link>https://virgool.io/@shahpasand/%DA%86%D8%B7%D9%88%D8%B1-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-%D9%87%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A7%D8%AA%D8%B1%DB%8C-%D8%A8%D8%A7-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%D8%A8%D9%86%D9%88%DB%8C%D8%B3%DB%8C%D9%85-ufeo4w2krrna</link>
                <description>پس از یادگیری مقدمات ساخت اپلیکیشن با فلاتر، اولین قدم برای اینکه اپلیکیشن های حرفه ای تری بنویسید، توجه به کارایی ویجت هایی است که برای پیاده سازی UI انتخاب می کنید. به طور کلی اپلیکیشن های فلاتر در حالت پیش فرض کارا هستند بنابراین نیازی نیست که از ابزارهای پیچیده برای تشخیص میزان کارایی اپ خود استفاده کنید. تنها باید از یک سری نکات ساده پیروی کنید. رعایت این نکات به شما کمک می کند که اپلیکیشنی با حداکثر کارایی ممکن بنویسید.هزینه متد ()build را کنترل کنید.یک نکته کلیدی در حفظ کارایی و سرعت اپلیکیشن فلاتر، پرهیز از بیلد کردن های غیرضروری است. از قرار دادن کدهای پرهزینه در متد ()build پرهیز کنید، چون این متد مکررا و با فراخوانی ویجت در هر بیلد اجرا خواهد شد.استفاده از ویجت هایی مانند ()Column و ()ListView با لیستی طولانی از فرزندان که اکثر آنها در صفحه قابل مشاهده نیستند هزینه متد ()build را بالا می برد.سعی کنید متد ()build را تا جایی که ممکن است کوچک نگه دارید. می توانید آن را بر مبنای کپسوله سازی به ویجت های کوچکتری بشکنید اما توجه داشته باشید که ویجت ها چطور تغییر می کنند:     وقتی در یک state مشخص ()setState را فراخوانی می کنید، تمام ویجت های فرزند ویجت اصلی ریبیلد      خواهند شد. بنابراین ()setState را دقیقا در بخشی از زیردرخت فراخوانی کنید که UI آن نیاز به تغییر دارد.      اگر تغییر به بخش کوچکی از درخت مرتبط است از فراخوانی ()setState در نودهای بالاتر درخت ویجت      خودداری کنید.از افکت ها در حد ضرورت استفاده کنید. استفاده از افکت ها می تواند هزینه بالایی داشته باشد. برخی از افکت ها در پشت صحنه ()saveLayer را فراخوانی می کنند که عملی هزینه بر است. هنگام به کارگیری افکت ها این قوانین را به خاطر داشته باشید:از ویجت Opacity در هنگام ضرورت استفاده کنید. جایگزین های دیگری به جای استفاده مستقیم از ویجت Opacity وجود دارند. برای مثال کد زیر بدون استفاده از این ویجت، opacity برابر با 0.5 ایجاد می کندContainer(color: Color.fromRGBO(255, 0, 0, 0.5))و بسیار سریع تر از کد زیر است:Opacity(opacity: 0.5, child: Container(color: Colors.red))کلیپ (Clip) کردن، متد ()saveLayer را فراخوانی نمی کند و بنابراین به اندازه Opacity پرهزینه نیست اما  هنوز گران است و باید با دقت بیشتری آن را استفاده کنید.ویجت های دیگری هم وجود دارند که ممکن است در شرایطی ()saveLayer را فراخوانی کنند و به همین دلیل پتانسیل پرهزینه بودن را دارند:ShaderMaskColorFilterChip:اگر disabledColorAlpha != 0xff باشد ()saveLayer را فراخوانی می کند.Text:اگر از overflowShader استفاده کند باعث فراخوانی ()saveLayer می شود.چند توصیه دیگر برای اجتناب از فراخوانی های رایج ()saveLayer وجود دارد:برای ایجاد تصویر با افکت محو (Fade) می توانید به جای Opacity از ویجت FadeInImage استفاده کنید.برای ایجاد مستطیل با گوشه های گرد به جای استفاده از Cliping rectangle از خصیصه borderRadius که بسیاری از کلاس های ویجت آن را در اختیار می گذارند استفاده کنید.لیست ها و grid ها را به صورت lazy ایجاد کنید. استفاده از ویجتی مثل ListView.builder که یک متد callback برای ساختن آیتم های لیست از شما دریافت می کند باعث می شود در ابتدا تنها بخشی از لیست که قابل نمایش در صفحه است ساخته شود.با دقت بیشتری از انیمیشن استفاده کنید.وقتی از AnimatedBuilder استفاده می کنید ویجت هایی در زیردرخت ویجت که به انیمیشن ارتباط ندارند را در builder قرار ندهید. این ویجت ها به ازای هر تیک انیمیشن مجددا بیلد می شوند. به جای این کار بخشی از زیردرخت را که باید انیمیشن روی آن اعمال شود به عنوان پارامتر child به AnimatedBuilder پاس بدهید.از کلیپ کردن ویجت در انیمیشن ها اجتناب کنید. برای مثال اگر می خواهید تصویر کلیپ شده ای را انیمیت کنید بهتر است آن را یک بار و پیش از انجام انیمیت کلیپ کنید و سپس انیمیشن را روی آن اعمال کنید.</description>
                <category>Shahpasand</category>
                <author>Shahpasand</author>
                <pubDate>Fri, 22 Jan 2021 18:42:20 +0330</pubDate>
            </item>
            </channel>
</rss>