<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Masoud-Dev</title>
        <link>https://virgool.io/feed/@migrant1369</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-07-06 22:57:24</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/1513943/avatar/wXm0I7.png?height=120&amp;width=120</url>
            <title>Masoud-Dev</title>
            <link>https://virgool.io/@migrant1369</link>
        </image>

                    <item>
                <title>زمان utc و gmt چیست؟</title>
                <link>https://virgool.io/@migrant1369/%D8%B2%D9%85%D8%A7%D9%86-utc-%D9%88-gmt-%DA%86%DB%8C%D8%B3%D8%AA-vxdh5qzkjhzp</link>
                <description>ساعت جهانی UTC مخفف Coordinated Universal Time می‌باشد. این واحد یک استاندارد زمانی در دنیا محسوب می‌شود که تمام دنیا زمان خود را طبق آن تنظیم می‌کنند. ساعت ما توسط ساعت جهانی utc تنظیم و هماهنگ می‌شود. لازم به ذکر بوده که UTC یک منطقه زمانی نمی‌باشد. جالب است بدانید UTC یک استاندار هماهنگ جهانی، و در حقیقت «ساعت صفر» است. ساعت جهانی UTC  در سال 1972 به اجرا درآمد. ساعت هماهنگ جهانی مطابق با چرخش زمین محاسبه می‌شود و بهینه‌ترین راه محاسبه زمان است.منطقه زمانی چیست؟منطقه زمانی˛ به منطقه‌ای از زمین گفته می‌شود که ساعت یکسانی را نشان می‌دهد. به همین علت، همه شهرها و کشورها در این منطقه ساعت یکسانی را می‌بینند. مناطق زمانی به حالت افقی اجرا می‌شوند: علت مشابه بودن و یا تفاوت استاندارد زمانی کشورهای مختلف، متعلق نبودن به یک عرض جغرافیایی یکسان است. مناطق زمانی برای فعالیت‌های اجتماعی و تجاری،استفاده نظامی، دلایل قانونی، تجارت و تقریباً اکثر فعالیت‌های دیگر بسیار مهم هستند. هنگامی که با شخصی صحبت می‌کنید یا فردی که محصول شما را ارسال می‌کند در منطقه زمانی دیگری باشد، این امکان وجود دارد که ساعت شما نیمه شب بوده و فرد مقابل در ساعات ظهر باشد.ساعت جهانی (UTC) و ساعت گرینویچساعت هماهنگ جهانی (Coordinated Universal Time)، به عنوان یک استاندارد تعریف شده از جانب کشورهای جهان برای نمایش ساعت دقیق شناخته شده است. در ساعت هماهنگ جهانی UTC برمبنای ساعت اتمی (International Atomic Time) جهان کار کرده و توسط اتحادیه بین المللی مخابرات تعریف شده است. ساعت اتمی به عنوان دقیق‌ترین ابزار ساخت انسان برای سنجش زمان شناخته می‌شود.ساعت UTC حدود 0.9 ثانیه با ساعت گرینویچ (GTM) دارای اختلاف است. این مقدار ناچیز در کاربردهای عمومی قابل چشم پوشی بوده و به همین دلیل می‌توان ساعت هماهنگ جهانی UTC را با ساعت گرینویچ GTM برابر دانست. لازم به ذکر است که ساعت جهانی و گرینویچ هر دو از ساعت فصلی (Daylight Saving Time) پیروی نمی‌کنند.دلیل تنظیم ساعت جهانی UTC چیست؟به دلیل یکسان نبودن نظم در سرعت چرخش زمین ، ساعت جهانی UTC به آرامی از زمان اتمی دور می‌شود. به همین خاطر، این ساعت باید به صورت دوره‌ای تنظیم شود. بسته به محل زندگی، کشور محل زندگی شما ممکن است ساعت تابستانی (DST) را مشاهده کند، که این ساعت در بهار و پاییز یک ساعت به جلو یا عقب حرکت می‌کند. با این حال ساعت جهانی˛ هیچگاه تحت تاثیر DST تغییر نخواهد کرد.منبع مطالب بالا: لحظه آخرحالا در چه مواردی باید از زمان UTC استفاده کنیم؟وقتی که شما یک کسب و کار جهانی (بیشتر از یک کشور!) دارید باید یک مبنای صفری برای تمام رکوردهایی که ثبت می‌کنید داشته باشید؛ حالا چرا؟فرض کنید کاربر شما یک پرداختی رو انجام داد و شما قصد دارید زمان این پرداخت رو داخل دیتابیس خودتون ذخیره کنید؛ اگه براساس تایم زون و ساعت تابستانی/زمستانی (DST) کاربر بخوایم این کار رو انجام بدیم باید تایم زون کاربر رو هم ذخیره کنیم و این جالب نیست. در عوض ما چیکار می‌کنیم میاییم یه مبنای صفری برای این پرداختمون در نظر می‌گیریم یعنی زمان به UTC یا همون صفر مطلق و هر وقتی کاربر زمان این پرداخت خودش رو بخواد ما تایم زون کاربر رو در نظر میگیرم و این زمان UTC رو به زمان کاربر برمیگردونیم.حالا اگه بخوایم یه نوتیفیکیشنی در ساعت ۹ صبح به کاربرا بفرستیم در این حالت چکار می‌کنیم؟ زمان نوتیف رو به UTC داخل جاب وارد می‌کنیم و هنگام ارسال به کشورهای مختلف این زمان UTC رو به تایم زون کاربر برمیگردونیم و ارسال می‌کنیم. </description>
                <category>Masoud-Dev</category>
                <author>Masoud-Dev</author>
                <pubDate>Sun, 14 Jan 2024 10:52:24 +0330</pubDate>
            </item>
                    <item>
                <title>Impeller in flutter ایمپلر در فلاتر</title>
                <link>https://virgool.io/@migrant1369/impeller-in-flutter-%D8%A7%DB%8C%D9%85%D9%BE%D9%84%D8%B1-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-lawrgzpua04g</link>
                <description>فلاتر برای رندر صفحات از Renderer استفاده میکنه. Renderer یک نرم افزاریه که کد رو به پیکسل ترجمه میکنه.خب برای اینکار از Skia استفاده میکرد و الان از ایمپلر.اشکال Skia این بود که یه عالمه کد اضافه داره و برای فلاتر بهینه نشدهSkia renderer flutterخب اگه بخوایم لوگوی فلاتر رو رندر کنیم فرایند زیر طی میشه تا بتونیم لوگو رو تو صفحه خودمون داشته باشیم.Impeller architectureداخل عکس بالا می‌بینیم که لوگو به یک Render Tree ترجمه میشه و بعد ترجمه میشه به لیستی از DrawPathOp که در سطح پایین‌تر خود این لیست به پیکسل ترجمه میشه.حالا رندر انیمیشن‌های خیلی سنگین با Impeller‌ امکان پذیر شده و در یه آزمایش که داخل ویدیوی معرفی ایمپلر توسط تیم فلاتر می‌بینیم؛ یه انیمیشن سنگین با Skia فقط ۷ فریم بر ثانیه میده و با Impeller میتونیم تا ۶۰ فریم بر ثانیه داشته باشیم و این فوق العادست.برای اطلاعات بیشتر به ویدیوی رسمی خود سایت فلاتر مراجعه کنید:https://docs.flutter.dev/perf/impeller</description>
                <category>Masoud-Dev</category>
                <author>Masoud-Dev</author>
                <pubDate>Tue, 29 Aug 2023 10:23:40 +0330</pubDate>
            </item>
                    <item>
                <title>چطور ساب دایرکتوری گیت هاب رو کلون کنیم؟</title>
                <link>https://virgool.io/@migrant1369/%DA%86%D8%B7%D9%88%D8%B1-%D8%B3%D8%A7%D8%A8-%D8%AF%D8%A7%DB%8C%D8%B1%DA%A9%D8%AA%D9%88%D8%B1%DB%8C-%DA%AF%DB%8C%D8%AA-%D9%87%D8%A7%D8%A8-%D8%B1%D9%88-%DA%A9%D9%84%D9%88%D9%86-%DA%A9%D9%86%DB%8C%D9%85-lxbemctvcsqo</link>
                <description>How to Clone Only a Subdirectory with Gitهمیشه سعی کنید example های هر پکیجی که میخواید استفاده کنیدو چک کنید؛ یکی از مشکلات برای کلون کردن این پوشه‌های example اینکه گیت هاب بصورت دیفالت این قابلیت رو نداره و شما مجبورید کل ریپوزیتوری رو کلون کنید! تا به پوشه example دسترسی داشته باشید.خب این مشکل چطور حل میشه؟با sparseCheckout، بریم ببینیم چطورهدر حالت عادی اگه بخوایم یه ساب دایرکتوری (زیرپوشه!) رو از گیت هاب کلون کنیم ارور میده:git clone https://github.com/terroo/fonts/files
Cloning into &#039;files&#039; ...
remote: Not Found
fatal: repository &#039;https://github.com/terroo/fonts/files/&#039; not foundمی‌بینید که ساب دایرکتوری &quot;files&quot; رو نتونست پیدا کنه.# هر جا میخواید پروژه رو کلون کنید یه پوشه ایجاد کنید؛ حالا با دستور زیر یا با فایل اکسپلورر
mkdir my-dir &amp;&amp; cd my-dir# با ترمینال یا بش داخل مسیری که ساختید برید و گیت رو اینیشیال کنید
git init# مسیر اصلی پروژه (بدون ساب دایرکتوریها) رو بهش ریموت بزنید
git remote add -f origin https://github.com/terroo/fonts/# با دستور زیر پراپرتی زیر رو فعال کنید
git config core.sparseCheckout true# یه فایل به نام sparse-checkout داخل مسیر git/info بسازید و ساب دایرکتوری که میخواید کلون کنیدو داخلش وارد کنید (اینجا ساب دایرکتوری من files‌ هست) شما میتونید با دستور اینکارو انجام بدید یا با فایل اکسپلورر لینوکس و ویندوز یا مک
echo &#039;files&#039; &gt;&gt; .git/info/sparse-checkoutecho &#039;files&#039; &gt;&gt; .git/info/sparse-checkout# حالا وقتی از مستر pull کنید ساب دایرکتوری که وارد کردید کلون میشه
git pull origin master</description>
                <category>Masoud-Dev</category>
                <author>Masoud-Dev</author>
                <pubDate>Mon, 24 Jul 2023 14:37:27 +0330</pubDate>
            </item>
                    <item>
                <title>مشاهده میزان پیشرفت آپلود و دانلود در فلاتر با استفاده از پکیج رتروفیت</title>
                <link>https://virgool.io/@migrant1369/%D9%85%D8%B4%D8%A7%D9%87%D8%AF%D9%87-%D9%85%DB%8C%D8%B2%D8%A7%D9%86-%D9%BE%DB%8C%D8%B4%D8%B1%D9%81%D8%AA-%D8%A2%D9%BE%D9%84%D9%88%D8%AF-%D9%88-%D8%AF%D8%A7%D9%86%D9%84%D9%88%D8%AF-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D9%BE%DA%A9%DB%8C%D8%AC-%D8%B1%D8%AA%D8%B1%D9%88%D9%81%DB%8C%D8%AA-foqpydkp20cn</link>
                <description>ما داخل Dio همچین قابلیتی داریم:میزان پیشرفت دانلود در Dioمیزان پیشرفت آپلود در Dioو اگه بخوایم با رتروفیت (Retrofit) انجامش بدیم:میزان پیشرفت دانلود در Retrofitقسمتی از کد تولید شده که شامل تابع onSendProgress هستبرای onReceiveProgress هم به همین شکل عمل می‌کنیم.دوستدار شما مسعود سعیدی</description>
                <category>Masoud-Dev</category>
                <author>Masoud-Dev</author>
                <pubDate>Thu, 12 Jan 2023 11:09:15 +0330</pubDate>
            </item>
                    <item>
                <title>compileSdkVersion Vs minSdkVersion Vs targetSdkVersion</title>
                <link>https://virgool.io/@migrant1369/compilesdkversion-vs-minsdkversion-vs-targetsdkversion-h0fplwmvk21b</link>
                <description>با هم بریم ببینیم تفاوت بین این سه تا چیه! حتما می‌دونید که این پراپرتی‌ها تو زیر هستند:android/app/build.gradle۱- compileSdkVersion:- براساس ورژنی که در این قسمت وارد شده برنامه کامپایل میشه، به عنوان مثال شما ورژن ۳۳ رو وارد کردید خب این عدد به این معنیه که شما می‌تونید از تمام امکانات ورژن ۳۳ استفاده کنید (به علاوه‌ی هر امکاناتی که ورژن‌های قبلی دارند).- چه زمانی به ارور کامپایل می‌خوریم؟ وقتی شما از featureهای ورژن ۳۳ استفاده کردید ولی تو compileSdkVersion عدد زیر ۳۳ رو وارد کردید.- این پراپرتی داخل فایل اجرایی apk برنامه‌ی شما نخواهد بود و فقط زمان کامپایل استفاده میشه ولی باید به اخطارهای اون توجه کنید.- این پراپرتی به گریدل میگه که با چه ورژنی از SDK برنامه‌ی شما رو کامپایل، دیباگ و تست کنه.- خب می‌گید ما که اینجاها بزنیم ورژن ۳۳ آیا برنامه‌ی ما روی دستگاه‌هایی که قدیمی‌تر هستند مثلا ورژن ۳۱ هم اجرا میشند؟ اجرا میشند ولی به شرطی که برنامتون به هیچ کدوم از مسیرهای ورژن ۳۳ استناد نکرده باشه.- قابلیت forward-compatible داخل اندروید باعث میشه برنامه‌های قدیمی به راحتی داخل دستگاه‌ی جدید با ورژن اندروید بالاتر اجرا بشن؛ پس بدون نگرانی میتونید این مقدار رو روی آخرین ورژن قرار بدید.۲- minSdkVersion:- این ویژگی حداقل ورژن اندرویدی که برنامه‌ی شما قابلیت اجرا شدن روی اون رو داره مشخص میکنه و گوگل پلی استور و بقیه‌ی مارکت‌ها براساس این مقدار میفهمند که دستگاه شما با این برنامه سازگار هست یا نه.- دقت کنید که این پراپرتی داخل فایل apk برنامه‌ی شما اعمال خواهد شد برخلاف compileSdkVersion.- پس اگه از ویژگی خاصی که تو ورژن‌های بالاتر گنجونده شده استفاده نمی‌کنید بهتره این مقدار رو روی حداقل بزارید این باعث میشه برنامه‌ی شما روی اکثر گوشیها قابلیت اجرا رو داشته باشند.۳- targetSdkVersion:این پراپرتی کاری نداره به اینکه برنامه‌ی شما چجور کامپایل شده یا از چه APIی استفاده کردید. targetSdkVersion برنامه‌ی شما رو روی نسخه‌ای که در این پراپرتی مشخص کردید، تست میکنه. این پراپرتی بیشتر شبیه certification یا امضایی میمونه به سیستم عامل اندروید میگه چجور باید برنامه شما رو از نظر ویژگی‌های سیستم عامل هندل کنه.به عنوان مثال، عدد ۱۱ یا بالاتر این اجازه رو به سیستم میده که از تم جدید پیشفرض Holo داخل برنامتون استفاده کنه وقتی که دستگاه اندروید شما ورژنی بالاتر از ۳ داشته باشه.خود سیستم عامل اندروید وقت اجرا شدن برنامه، ممکنه ظاهر اپ شما رو براساس مقداری که تو این پراپرتی ست کردید، تغییر بده.به عنوان مثال با انتشار نسخۀ ۶ اندروید (API Level 23) مدل مجوزها به  Runtime Permission تغییر کرد. قبل از اون کاربر در زمان نصب برنامه باید  همۀ مجوزها را تأیید می‌کرد تا برنامه نصب می‌شد ولی حالا برنامه در زمان  اجرا تک تک مجوزها را به صورت تفکیک شده از یوزر درخواست می‌کنه. در چنین  شرایطی اگر مقدار targetSdkVersion در برنامۀ شما کمتر از ۲۳ باشد (مثلاً  ۲۲). کاربر هنگام اجرای برنامه با درخواست مجوز مواجه نمی‌شه.خب حالا ما داخل این پراپرتی‌ها چی ست کنیم بهتره؟بهتره برای targetSdkVersion آخرین نسخه API رو ست کنید. این باعث میشه برنامه‌ی شما داخل دستگاه‌های با اندروید جدید با ظاهر خوبی اجرا بشه. اگه targetSdkVersion رو مشخص نکنید اپ برای کامپایل از minSdkVersion استفاده میکنه.به طور کلی:minSdkVersion &lt;= targetSdkVersion &lt;= compileSdkVersionو در حالت ایده‌آل:minSdkVersion (lowest possible) &lt;= targetSdkVersion == compileSdkVersion (latest SDK)</description>
                <category>Masoud-Dev</category>
                <author>Masoud-Dev</author>
                <pubDate>Tue, 26 Jul 2022 17:33:21 +0430</pubDate>
            </item>
                    <item>
                <title>Dio Interceptors</title>
                <link>https://virgool.io/@migrant1369/dio-interceptors-jyttmwovgova</link>
                <description>Interceptors treeگاهی اوقات برنامه‌ها خیلی ساده هستند مثلا یه درخواستی رو میفرستیم به یه سروری و یه لیست بهمون برمیگردونه، مثل:Future&lt;Response&gt; fetchItems() {
    return Dio().(&amp;quothttps://some-website.com/listing&amp;quot);
}هندل کردن اروری نداریم، نیازی نیست که ریسپانس‌هایی که داریمو لاگ بندازیم و هیچ catch کردنی وجود نداره!ولی  همیشه برنامه‌ها اینقد ساده نیستند، و بعضی اوقات کارهای بیشتری نیاز هست  که انجام بدیم تا ریسپانس مناسب رو از سرور بگیریم یا بخوایم برناممون رو  دیباگ کنیم، حالا این کارهای بیشتری که میگیم چیا هستن:ارسال کردن یه هدر داینامیک به سرور. برای مثال وقتی میخوایم یه کلیدی که داخل shared preference ذخیره شده رو ارسال کنیم.یا اگه بخوایم مقادیری که از ریسپانس سرور می‌گیریم بخوایم یه جایی بصورت لوکال ذخیرش کنیم.یا ارورهایی که از سمت سرور برامون ارسال میشه رو بخوایم بدیم به کلاس مربوطش تا برنامه اونو هندل کنه.علاوه  بر این، ممکنه بخوایم یه cache ساده به برناممون اضافه کنیم مثلا وقتی  کانکشن تایم آوت گرفتیم یا کاربر اینترنت نداشت، ما میتونیم ریسپانس قبلی  سرور رو بهش بدیم.و در آخر برحسب نیاز ممکنه بخوایم همه‌ی ریسپانس‌ها و درخواست‌ها رو لاگ بگیریم.اینجاست  که آقای Interceptors که داخل پکیج Dio هست وارد عمل میشه و به ما تو هندل  کردن این موارد با کال بک‌های خاصش (errors، requests و response) کمک  میکنه. معنی لغوی این کلمه میشه «حائل شدن» در اینجا بین سرور و یوزر قرار  میگیره و قبل اینکه درخواست توسط سرور هندل بشه یه صحت سنجی در قسمت خود  یوزر انجام میشه.قبل  اینکه بریم وارد مبحث اینکه چطور میشه از interceptors استفاده کرد بشیم  اجازه بدید یه قدم بریم عقب و ببینیم Dio چطور میتونه config بشه:Dio createDio() {
  return Dio(
    BaseOptions(
      connectTimeout: 5000,
      receiveTimeout: 5000,
      baseUrl: &amp;quothttps://some-website.com&amp;quot
    )
  );
}به هر حال، یه چیزی که نمیتونم به این کانفیگ اضافه کنیم (در این لحظه که دارم این مقاله رو مینویسم) اون Interceptors هست.برای استفاده از Interceptors می‌تونیم به دو روش عمل کنیم:- ساخت یک شی از Dio یا built-in- ساخت کاستوم کلاس و extends کردن از InterceptorsInterceptors treeنکته: با interceptors ما میتونیم درخواستهارو قبل اینکه توسط سرور هندل بشند پردازش کنیم.روش اول (ساخت یک شی از Dio یا روش built-in):Dio addInterceptors(Dio dio) {
    return dio
      ..interceptors.add(
        InterceptorsWrapper(
          onRequest: (options, handler) =&gt; requestInterceptor(options, handler),
          onResponse: (response, handler) =&gt; responseInterceptor(response, handler),
          : (dioError, handler) =&gt; errorInterceptor(dioError, handler),
        ),
    );
 }و با این حرکت ما یه شی کانفیگ شده از Dio داریم که میتونیم برای هر APIی که میسازیم ازش استفاده کنیم.در توضیح کد بالا بگم که ما یک شی از Dio ساختیم به نام addInterceptors که یه پارامتر از نوع Dio میگیره.همونطور  که در اول مقاله گفتیم، بیایید فرض کنیم که یه header داریم که یه مقدار  ذخیره شده داخل shared preferences داخلش فراخوانی شده (خب ما یه مقدار  داینامیک داخلش گذاشتیم و هر لحظه ممکنه یه مقدار داشته باشه) و تابع  DateTime.now که باز هم یه مقدار متغیره.نکته: از اونجا که مقادیر داینامیکی داریم نمی‌تونیم از فیلد extra که BaseOptions داره استفاده کنیم.خب اینجاست که InterceptorWrapper به ما یه شی از RequestOptions میده که پراپرتی‌هایی داره مثل:- dynamic data- String path- Map&lt;String, dynamic&gt; queryParametersبا این اطلاعات میتونیم متد requestInterceptor خودمونو پیاده کنیم.این متد یه مقدار داینامیکی رو برگردونه که میتونه از انواع زیر باشه:- میتونه از نوع RequestOptions باشه اگه میخوایم با همین request ادامه بدیم.- یا میتونه از نوع Response باشه اگه بخوایم خود اپ به request رسیدگی کنه.- یا میتونه DioError یا dio.reject باشه اگه بخوایم یه اروری بفرستیم.این به ما قابلیت validate هر ریکوئستی رو میده قبل اینکه ساخته بشه یا بخوایم add data کنیم و یا اروری throw بشه.توضیحات داکیومنت خود Dio در مورد Interceptors:برای  هر شی dio، ما می‌تونیم یک یا چند Interceptors اضافه کنیم، با هر کدوم  میتونیم Intercept کنیم requests, response &amp; errors قبل اینکه تو then  یا catchError هندل بشند.InterceptorsWrapper خب  بریم سراغ متد onRequest که برای ارسال درخواست به سرور هست و به ما اجازه  میده قبل ارسال به سرور یک سری تغییرات بدیم و بعد به سرور ارسال کنیم.- handler.next(option) میگه هیچ تغییری نده پاسش بده مرحله بعد- handler.resolve(response) اگه بخوایم یسری کاستوم دیتا به ریکوئست/ریسپانس خودمون اضافه کنیم- handler.reject(dioError) اگه بخوایم درخواست رو قبل ارسال شدن با یه ارور مسیجی reject کنیممتد بعدی onResponse هستش که اگه بخوایم با جواب برگشتی (response) یه کارایی بکنیم:- handler.next(response) میگه هیچ تغییری نده پاسش بده مرحله بعد- handler.reject(dioError) اگه بخوایم درخواست رو قبل ارسال شدن با یه ارور مسیجی reject کنیممتد بعدی که جوابهایی که از سمت سرور میاد رو اگه ارور داشته باشه تو این متد هندل میکنیم:- handler.next(e) میگه هیچ تغییری نده پاسش بده مرحله بعد- handler.resolve(response) اگه به مشکلی خوردیم اینجا میشه حللش کردCustomInterceptor class extends Interceptorبریم یه نمونه resolve کردن رو با هم ببینیم:handler.resolveامیدوارم استفاده لازم رو از این آموزش برده باشید.با لایک و کامنت انگیزه بدید دوباره آموزشی بزارم :)دوستدار شما مسعود سعیدی</description>
                <category>Masoud-Dev</category>
                <author>Masoud-Dev</author>
                <pubDate>Tue, 12 Jul 2022 15:16:53 +0430</pubDate>
            </item>
                    <item>
                <title>Cacade notation &amp; Spread notation in dart دو نقطه و سه نقطه در دارت</title>
                <link>https://virgool.io/@migrant1369/cacade-notation-spread-notation-in-dart-%D8%AF%D9%88-%D9%86%D9%82%D8%B7%D9%87-%D9%88-%D8%B3%D9%87-%D9%86%D9%82%D8%B7%D9%87-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%B1%D8%AA-k8ihbekoickr</link>
                <description>از حالا بگم که چیز سختی نیست و میتونیم اصلا ازش استفاده نکنیم ولی بالاخره کارمون رو آسون میکنه و اگه احیانا جایی دیدیم حداقل سرمون گیج نمیره! :)عملگر دو نقطه (Cascade notation):این قابلیت رو بهش میگن syntactic sugar حالا ینی چی؟ تو برنامه نویسی بعضی سینتکس‌ها برای راحتی و خوانایی بیشتر هستند که این اصطلاح این قضیه رو بیان میکنه. داخل دارت این «دو نقطه» به ما اجازه میده تا توالی از operatorها روی یک آبجکت پیاده کنیم. شما می‌تونید با دابل دات متدهای یک شی رو فراخوانی کنید و به پراپرتی‌هاش دسترسی داشته باشید. این عملگر برای اینکه کد ما تمیزتر و کوتاه‌تر بشه مورد استفاده قرار می‌گیره و اغلب دیگه نیاز شما رو از ساختن متغیرهای موقت برطرف میکنه.// with cascade notation:
querySelector(&#039;#confirm&#039;) // Get an object.
  ..text = &#039;Confirm&#039; // Use its members.
  ..classes.add(&#039;important&#039;)
  ...listen((e) =&gt; window.alert&#40;&#039;Confirmed!&#039;&#41;)

// without cascade notation
var button = querySelector(&#039;#confirm&#039;);
button.text = &#039;Confirm&#039;;
button.classes.add(&#039;important&#039;);
button..listen((e) =&gt; window.alert&#40;&#039;Confirmed!&#039;&#41;);توضیح کد:داخل کد اولی از عملگر دو نقطه استفاده شده و می‌بینید که یک شی دریافت می‌کنه و به پراپرتی‌های اون شی دسترسی داره. داخل کد بعدی بدون قابلیت cascade notation مجبور شدیم یه متغیر بسازیم و بعد از اون متغیر برای دسترسی به پراپرتی‌هاش استفاده کنیم.عملگر سه نقطه (Spread notation):این عملگر روی کالکشن‌ها جواب میده مثل List, Map و Set. بخوایم کتابی بگیم، باعث گسترش اون کالکشن میشه و کاری که واقعا انجام میده، میشه اون کالکشن رو صدا میزنه و دونه دونه ممبرهاش رو فراخوانی میکنه مثل یه foreach عمل میکنه.قبلا روی دارت 2.3 ما addAll رو داشتیم تا دو تا لیست رو با هم کمباین کنیم و حالا spread operator همون کار رو برامون راحتتر انجام میده.List&lt;int&gt; list1 = [1, 2, 3];
List&lt;int&gt; result = [0, ...list1];
print(result); //output: [0,1,2,3]اضافه کردن دو تا لیست به یک لیست جدید:List&lt;int&gt; list1 = [1, 2, 3];
List&lt;int&gt; list2 = [4, 5];
List&lt;int&gt; result = [...list1, ...list2];	
print(result); //output: [1,2,3,4,5]بخوایم سختترش کنیم:List&lt;int&gt; result = [...[0, ...list1, ...list2], 6];
print(result); //output: [0,1,2,3,4,5,6]و اگه بخوایم یه لیست نال رو بهش اضافه کنیم چی:List&lt;int&gt; nullList = null;
List&lt;int&gt; result = [...l1, ...nullList];
print(result);خب اینجا اخطار میده و باید بعد از عملگر سه نقطه اجازه نال بودن رو بهش بدیم:List&lt;int&gt; nullList = null;
List&lt;int&gt; result = [...l1, ...?nullList]; //add null-able sign
print(result); //output: [1,2,3]میشه از شرطها هم استفاده کرد:bool condition = false;
List&lt;int&gt; result = [...list1, if (condition) ...list2];
print(result);استفاده از Future لیست:Future&lt;List&lt;int&gt;&gt; list1 = Future.value([1, 2, 3]);	
Future&lt;List&lt;int&gt;&gt; list2 = Future.value([3, 4, 5]);	
List&lt;int&gt; result = [...await list1, ...await list2];
print(result);داخل Map و Set هم به همین صورته لینک میزارم خواستید اونورم چک کنید:https://www.woolha.com/tutorials/dart-using-triple-dot-spread-operator-examplesسورس بعدی:https://flutterbyexample.com/lesson/cascade-notationامیدوارم استفاده‌ی لازمو از این آموزش برده باشید.دوستدار شما مسعود سعیدی</description>
                <category>Masoud-Dev</category>
                <author>Masoud-Dev</author>
                <pubDate>Sat, 09 Jul 2022 12:41:13 +0430</pubDate>
            </item>
                    <item>
                <title>RxDart, BloC Pattern, Retrofit, JsonSerializable</title>
                <link>https://virgool.io/@migrant1369/rxdart-bloc-pattern-retrofit-jsonserializable-mb6ukindoxwf</link>
                <description>سلام دوستانپکیج‌هایی که قراره ازشون استفاده کنیم:پکیج‌های مورد استفاده در این آموزشیه توضیح مختصر بریم روی RxDart که اصلا این بنده خدا قراره برامون  چیکار کنه!ما از این پکیج استفاده میکنیم تا بتونیم دیتاهامون رو بصورت Stream فلاکس (جاری) کنیم داخل UI خودمون، به این صورت که با استفاده از پیروی از الگوی BloC یک کلاس می‌سازیم مثلا PostsBloc و هر جا داخل برناممون به اطلاعات Posts‌ احتیاج داشتیم کافیه یه شی از PostsBloc ایجاد و از متدی که بصورت استریم داخلش تعریف کردیم استفاده کنیم. به همین سادگی شما می‌تونید اطلاعات خودتون رو همه جای برنامه داشته باشید و در عین حال در استفاده از منابع سخت افزاری صرفه‌جویی کنید.ساختار سینک و استریمببینید از سمت چپ تصویر توضیح میدم قسمتی که Sinks داره، همونطور که می‌بینید اطلاعات ما از سمت کاربرها میاد یا سمت سرور و باید طبق این اطلاعات که برای ما ارسال میشه یسری عملیات رو انجام بدیم و در نهایت با پیغام مناسب جواب این رویدادها رو بدیم.همونطور که اول مقاله گفتم پایه‌ی این پکیج استریم‌ها هستند که ما اطلاعات رو بصورت یک جریان به سمت UI می‌فرستیم برای اینکه بتونیم این اطلاعات رو روی استریم داشته باشیم باید اون رو Sink‌ کنیم (همون Input در نظر بگیرید) بعد اینجا BloC وارد عمل میشه و رویداد رو دریافت میکنه یسری کارها روش انجام میده و اونو میده به PublishSubject, BehaviorSubject, ReplaySubject (آبجکتهای مربوط به پکیج rxdart) که مسئول استریم یا سینک کردن اون به UI‌ هستند.* این مقاله به مرور زمان تکمیل‌تر میشه یا خیلی عجله دارید می‌تونید گوگل کنید :)بریم سروقت کدها که خیلی واضح‌تر منظور رو می‌رسونن!- اجازه بدید به فایل pubspec.yaml پکیج‌ rxdart رو اضافه کنیم:اضافه کردن پکیج rxdartمیریم سراغ آبجکت‌های کاربردی که این پکیج در اختیار ما قرار میده:PublishSubject&lt;T&gt; classبا مثال گروه تلگرامی میریم جلو، فرض کنید وارد یه گروه تلگرامی شدید هر پیامی که قبل شما بوده به شما نشون نمیده و از این به بعد هر پیام، فایل و ... (دیتا و رویداد) که کسی بفرسته شما بهش دسترسی دارید.- همه‌ی خصوصیات broadcast که در StreamController هست رو داره با یک استثنا:اینکه هم میتونه Sink کنه هم Stream- این کلاس rxdart به ما اجازه میده تا دیتاها، ارورها و رویدادهایی که انجام شده رو به listener‌ بفرستیم.به عنوان مثال:final subject = PublishSubject&lt;int&gt;();
///
// از این به بعد اگه دیتا یا رویدادی اتفاق بیافته میتونه بشنوه پس الان ۱، ۲، ۳ رو میشنوه با هر چی بعدش بیاد
subject.stream.listen(observer1);
subject.add(1);
subject.add(2);
///
// از این به بعد اگه دیتا یا رویدادی اتفاق بیافته میتونه بشنوه پس الان فقط ۳ رو میشنوه با هر چی بعدش بیاد
subject.stream.listen(observer2);
subject.add(3);
subject.close();BehaviorSubject&lt;T&gt; classفرض کنید وارد گروه تلگرامی شدیم و ما که تازه واردیم آخرین پیام که داخل گروه بوده (قبل از ما) رو بهمون نشون میده، یا اینکه با استفاده از seeded یه مقدار اولیه (مثلا پیام خوش آمد گویی) رو به ما نشون میده.این کلاس از rxdart آخرین آیتمی که add میشه رو به همه‌ی listenerها که بعدا میان سابسکرایب می‌کنند میفرسته! و قابلیت seed هم داره یعنی می‌تونیم یه مقدار اولیه بهش بدیم که به دوستان دنبال کننده بفرسته.final subject = BehaviorSubject&lt;int&gt;();
/// 
subject.add(1);
subject.add(2);
subject.add(3);
/*دقت کنید آیتم‌ها اضافه شدند و سابسکرایبرها با اینحال میتونند به آخرین آیتم دسترسی داشته باشند*/
subject.stream.listen(print); // prints 3
subject.stream.listen(print); // prints 3
subject.stream.listen(print); // prints 3

/// ### مثال همراه با استفاده از seed
final subject = BehaviorSubject&lt;int&gt;.seeded(1);
/*این هم مثال سید دار! که یجورایی تغذیه کننده یا مقدار شروع در نظر بگیرید می‌بینیم که سابسکرایبرها میتونند به عدد سید دسترسی داشته باشند*/
subject.stream.listen(print); // prints 1
subject.stream.listen(print); // prints 1
subject.stream.listen(print); // prints 1
ReplaySubject&lt;T&gt; classخب بریم سراغ بعدی که چیز جالبیه مثلا وقتی بخوایم وارد یه گروه تلگرامی بشیم و بتونیم پیام‌های قبلی رو هم بخونیم که ممبرهای گروه قبلا فرستادن! این میشه کار ReplaySubject!final subject = ReplaySubject&lt;int&gt;();

subject.add(1);
subject.add(2);
subject.add(3);

/*همه‌ی تازه واردها می‌تونند دیتاهایی که قبلا اضافه شده رو شنود کنند*/
subject.stream.listen(print); // prints 1, 2, 3
subject.stream.listen(print); // prints 1, 2, 3
subject.stream.listen(print); // prints 1, 2, 3

/// ### مثال همراه با maxSize
final subject = ReplaySubject&lt;int&gt;(maxSize: 2);

subject.add(1);
subject.add(2);
subject.add(3);
/*همه‌ی تازه واردها می‌تونند دیتاهایی که قبلا اضافه شده رو شنود کنند با این تفاوت که بخاطر مکس سایزی که اضافه کردیم فقط به همون اندازه از پیامهای آخر رو میتونند شنود کنند*/
subject.stream.listen(print); // prints 2, 3
subject.stream.listen(print); // prints 2, 3
subject.stream.listen(print); // prints 2, 3یک مثال ساده:اینارو گفتیم ولی هیچچی مثل کد و یه مثال ساده مطلب رو جا نمیندازه!ما قراره داخل این پروژه‌ی ساده یه Json که از سایت https://jsonplaceholder.typicode.com/posts میگیریم رو با استفاده از پکیج json_serializable و json_annotation پارسش کنیم و با استفاده از پکیج retrofit و dio (رتروفیت از دایو استفاده کرده) که وظیفه‌ی هندل کردن سرویسهای مربوط به شبکه و ارتباط با سرور رو داره، درخواستش رو میفرستیم به سرور (jsonplaceholder.com) و بعد با استفاده از کلاس PublishSubject پکیج rxdart اون رو استریم میکنیم روی UI‌ و کاربر اونو می‌بینه (خدایا این همه کار کنیم که کاربر یه لیستی از پست‌ها ببینه خخخخ خدایی نمیصرفه)ساختار پروژه‌ی ما به این صورته:ساختار پروژهقراره از چه پکیج‌هایی استفاده کنیم:پکیج‌هایی فایل پاب اسپکاول ببینید json که از سمت سرور میاد ساختارش چه شکلیه:[
  {
    &amp;quotuserId&amp;quot: 1,
    &amp;quotid&amp;quot: 1,
    &amp;quottitle&amp;quot: &amp;quotsunt aut facere repellat provident occaecati excepturi optio reprehenderit&amp;quot,
    &amp;quotbody&amp;quot: &amp;quotquia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto&amp;quot
  },
  {
    &amp;quotuserId&amp;quot: 1,
    &amp;quotid&amp;quot: 2,
    &amp;quottitle&amp;quot: &amp;quotqui est esse&amp;quot,
    &amp;quotbody&amp;quot: &amp;quotest rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla&amp;quot
  },
]خب قراره یه لیستی از جی سان به ما برگردونه، نحوه‌ی پارس کردن دستی و اتومات رو بهتره برید از این لینک یاد بگیرید که مهندس علی حسین‌پور زحمتش رو کشیدن:https://vrgl.ir/HBYiPخب از اونجا که لیستی از پست‌ها برمیگردونه یک کلاس PostsModel میسازیم (میتونید به دو بخش PostsModel‌ و PostsData تقسیمش کنید که من برای پیچیده نشدن یکی رو ایجاد میکنم و از همون استفاده میکنم ولی کلین کد میگه باید جدا بشند)فولدرهای models, posts و services رو داخل پوشه lib بسازیدداخل پوشه models یک فایل دارت با نام posts_model ایجاد کنید و کدهای زیر رو داخلش بزنید.نکته: میتونید به این سایت https://app.quicktype.io یه جی سان بدید و بهتون کلاس رو تحویل بده!class PostsModelتوضیح کدهای بالا:- خط ۱: این پکیج برای ساخت فایل g (خط سوم) و حاشیه نویسی (JsonSerializable) (خط پنجم) لازمه.-خط ۳: اسمی که به part میدیم باید هم اسم فایلی که داخلش داریم پارت رو می‌نویسیم باشه فقط پسوند فایل رو .g.dart میزاریم، کدهای مربوط به پارس داخل این فایل ایجاد میشه)- خط ۵: کامپایلر وقتی به این خط میرسه میفهمه که قراره یه پارس کردنی رو انجام بدم (از اونجا که ما فقط احتیاج داریم اطلاعات رو بخونیم نیازی به ToJson‌ نداریم پس یه پارامتر به JsonSerializable میدیم که اونو برامون نسازه)- خط ۱۹: اینجا جایی که اطلاعات لازم برای پارس کردن رو میدیم به _$PostsModelFromJson(json)-----&gt; در نهایت با اجرا کردن دستور زیر داخل ترمینال در شاخه‌ی اصلی پروژه فایل g ساخته میشه:flutter pub run build_runner buildفایل posts_model.g.dartبریم سراغ ارتباط با سرور و api:سرویسهای مربوط به شبکه و سروراول بیایید آدرسی که قراره ازش اطلاعات مربوط به پست رو بگیریم به دو بخش تقسیم کنیم:https://jsonplaceholder.typicode.com/postsبخش اول: https://jsonplaceholder.typicode.comبخش دوم: /postsبخش اول میشه baseUrl ما و بخش دوم میشه endpoint‌ مابریم که فایلهای مربوطه رو ایجاد کنیم.اول endpoints.dart:فایل endpoints.dartما فقط از EndPoints.posts قراره استفاده کنیم.خب الان بریم سراغ فایل network_handler.dart خودمون:فایل network_handler.dartخط ۱: پکیج dio پیش نیاز پکیج retrofit‌ هست.خط ۲: Endpointهایی که ساختیم رو قراره به baseUrl رتروفیت اضافه کنیم.خط ۳: پکیج retrofit که برای @RestApi و ایجاد محتویات فایل g لازمه.خط ۴: مدلی که با اطلاعات json ساختیم رو اینجا فراخوانی میکنیم.خط ۶: هم اسم فایل باید فایل g رو هم بسازیمخط ۸: @RestApi با baseUrl سرور که برای محتویات فایل g استفاده میشهخط ۱۰: فکتوری مربوط به کلاس ApiClient که وظیفه‌ی ارتباط با سرور رو داره.خط ۱۲: @GET با پارامتری از کلاس EndPoints که با متد get از پکیج رتروفیت اطلاعات رو واکشی میکنه.خط ۱۳: یه متدی میسازیم و یه اسم بهش میدیم مثلا getPosts که از نوع Future هستش و لیستی از کلاس PostsModel-----&gt; در نهایت با اجرا کردن دستور زیر داخل ترمینال در شاخه‌ی اصلی پروژه فایل g ساخته میشه:flutter pub run build_runner buildفایل network_handler.g.dartبریم سراغ فایل repository.dartفایل repository.dart در پوشه مربوطهمحتویات فایل repository.dartاین فایل وظیفه‌ی ارتباط با فایل post_bloc.dart رو داره، کدها نیازی به توضیح نداره چیز جدیدی داخلش نیست. ریپوزیتوری میشه منبع، ینی جایی که ما ازش اطلاعاتمون رو واکشی میکنیم، پس هر اطلاعاتی که قراره بیاد از روی این کلاس میخونیم و استریمش میکنیم روی UI.خب رسیدیم به بخش جذاب RxDart. فایلش رو داخل پوشه‌ی مربوطه ایجاد کنید و محتویات زیر رو داخلش بزارید:محتویات فایل post_bloc.dartخط ۱: همونطور که گفتم باید ریپوزیتوری رو بدیم تا برامون استریم کنه.خط ۲: پکیج rxdart که معرف حضورتون هست.خط ۳: وقتی میخوایم نوع داده‌ها رو مشخص کنیم باید بگیم از نوع کلاس PostsModel هست.نکته: قبل اینکه بخوام بقیه کد رو توضیح بدم یه شمای کلی بهتون بدم که قراره چیکار بشه! ببینید اطلاعات از ریپو میاد داخل ما قراره هر چیزی که به ریپو اضافه میشه رو sink کنیم بعد بدیمش به متدی که برامون استریمش کنه روی UI، خب بریم ادامه توضیحات کد.خط ۶: از ریپو یه شی درست میکنیم.خط ۷: از پکیج rxdart یه شی میسازیم از کلاس PublishSubject که هم قراره برامون عمل سینک رو به گردن بگیره هم استریم.خط ۹: متد getter که باید از نوع Stream‌ باشه قبلا Observable بود که الان جاش استریم اومده و کارشو انجام میده. این متد قراره UI ما رو تغذیه کنه پس باید .stream هم بهش اضافه کنیم.خط ۱۱: متدی مینویسیم برای هر موقع صداش بزنیم شروع میکنه به گرفتن اطلاعات از ریپو و sink‌ کردن (input) اون روی شی که از PublishSubject ساختیم.خط ۱۶: حتما StreamController‌ خودتون رو close‌ کنید که اینجا الان PublishSubject این کار رو به عهده گرفته پس تو متد disopose کارش رو تموم میکنیم که state leak نداشته باشیم.خب بالاخره بریم سراغ UI خودمون، فایل posts_list.dart رو تو پوشه‌ی مربوطه ایجاد کنید و محتویات زیر رو داخلش قرار بدید:فایل posts_list.dartنکته‌ی خاصی نداره فقط محض اطلاع دوستان بگم:- یه متد داخل PostBloc نوشتیم که اسمش fetchAllPosts بود اونو صدا بزنید تا آخرین اطلاعات رو از سرور بگیره.- snapshot رو اجازه بدید که نال هم باشه وگرنه ارور میگیرید (خط ۱۶)- نال بودن snapshot.data رو چک کنید (خط ۲۶)UIنکته‌ی خیلی جذاب اینه که ما با یه StatelessWidget تونستیم اطلاعات رو بصورت داینامیک و استریم روی UI خودمون داشته باشیم کاری که StatefulWidget با کلی صرف منابع انجام میداد!امیدوارم که استفاده‌ی لازمو از این آموزش برده باشید.دوستدار شما مسعود سعیدی</description>
                <category>Masoud-Dev</category>
                <author>Masoud-Dev</author>
                <pubDate>Mon, 04 Jul 2022 12:56:02 +0430</pubDate>
            </item>
            </channel>
</rss>