<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Ali Hoseinpoor</title>
        <link>https://virgool.io/feed/@Ali_Hoseinpoor</link>
        <description>دوستان لطفا کانال یوتوب برنامه نویسی بنده رو حمایت کنید : https://www.youtube.com/c/FlutterStan</description>
        <language>fa</language>
        <pubDate>2026-06-17 10:32:02</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/50751/avatar/avatar.png?height=120&amp;width=120</url>
            <title>Ali Hoseinpoor</title>
            <link>https://virgool.io/@Ali_Hoseinpoor</link>
        </image>

                    <item>
                <title>فایل pubspec.yaml رو قووورت بده ??</title>
                <link>https://virgool.io/@Ali_Hoseinpoor/%D9%81%D8%A7%DB%8C%D9%84-pubspecyaml-%D8%B1%D9%88-%D9%82%D9%88%D9%88%D9%88%D8%B1%D8%AA-%D8%A8%D8%AF%D9%87-lvykykkfzep9</link>
                <description>سلام به همه دوستان عزیزم امیدوارم که حالتون تو این روزای کرونایی خووووب باشه. ❤️❤️قبل از اینکه بخوام برم بالا منبر واستون ?? تمام این مباحثی که توی این مقاله به شما میگم جامع تر و کامل ترش همراه با کلی مثال رو میتونید توی کانال یوتوبم ببینید. تماشای ویدیو pubspec.yaml در یوتوب منبزارین اول از همه چیز بهتون بگم که اصن این فایل pubspec.yaml واسه چی تو همه برنامه های ما هستش؟؟???این فایل تمام اطلاعات و وابستگی های پکیج ما به پکیج های دیگه رو توی خودش نگه میداره و با زبان yaml هم نوشته میشه. واسه همین تو تمام برنامه هایی که میسازید ردپای این فایل رو میبینید. ??اما این اطلاعات که گفتم چیان ؟Name :اسم پکیج ما رو مشخص میکنه و ضروری هم هست ینی هر برنامه ای که میسازید باید مقدار name رو بهش بدید. حالا اگه پکیج ما یک library package باشه (ینی بقیه پکیج ها بتونن به پکیجی که ما نوشتیم وابسته بشن) پکیج های دیگه با این name ای که ما تعریف کردیم میتونن از پکیج ما استفاده بکنن. و توی سایت هایی که میزبان پکیج هستند مثه pub.dev پکیج ما با این name ای که گفتیم نمایش داده میشه. هم چنین زمانی که توی کدمون میخوایم فایل های برنامه خودمون رو import کنیم باید اولش اسم پکیجمون رو بنویسیم یعنی اگه من یک پکیج داشته باشم با اسم ali و بخوام توی کدم فایل مثلا main رو import کنم باید به این شکل عمل کنم :import &#039;package:ali/main.dart&#039;;قوانینی که برای انتخاب name وجود داره عبارتند از :تمام حروفش حروف لاتین باشهفاصله بین کلمات نباشه و برای گذاشتن فاصله از _ استفاده کنید. با عدد شروع نشهکلمه رزرو شده نباشهنحوه مقدار دهی name در pubspec.yamlنمایش نام پکیج در pub.devEnvironment :این پارامتر به ما اجازه میده تا هم به Dart SDK و هم به نسخه Flutter محدودیت هایی اضافه کنیم، به این معنی که وقتی مینویسیم :اعمال محدودیت روی dart sdkما مشخص میکنیم که این برنامه یا پکیج فقط بر روی نسخه های Dart SDK بالاتر یا مساوی از 2.7.0 اما پایین تر از 3.0.0 اجرا شود.علاوه بر این، میتونیم با استفاده از پارامتر flutter مشخص کنیم که از چه نسخه فلاتر استفاده می‌کنیم : اعمال محدودیت روی dart sdk و نسخه فلاتردر این مورد، ما فقط در صورتی به برنامه خود اجازه اجرا میدیم که از نسخه Flutter 1.22.0 استفاده کنیم.Version : اگه شما قصد دارید که پکیج خودتون رو منتشر کنید که بقیه هم ازش استفاده کنند (مثلا توی pub.dev بزاریدش) حتما باید شماره ورژن رو مشخص کنید و هر بار که آپدیتی روی پکیجتون میدید این ورژن رو تغییر بدید این جوری کسانی که میخوان از پکیج شما استفاده کنن میتونن ورژن های پکیج شما رو ببینند و یا حتی از ورژن های مختلف پکیج شما استفاده کنند. اما اگه پکیجتون یک پکیج local (یا همون application package که جایی منتشر نشده) باشه میتونید ورژن رو مقدار دهی نکنید و دارت به صورت پیش فرض مقدار 0.0.0 رو به عنوان ورژن پکیج شما میزاره.حالا اگه کلا نمیدونین چجوری باید ورژن بندی کنید من توی مقاله های بعدی نحوه ورژن گذاری رو به شما کامل یاد میدم.(یا میتونید ویدیوی یوتوبم رو ببینید که کامل نحوه ورژن گذاری رو توضیح دادم : ویدیو یوتوب )نحوه ورژن گذاری در pubspec.yamlنمایش ورژن های مختلف یک پکیج در pub.devDescription :این پارامتر هم اگه میخوایم که پکیجمون رو به صورت عمومی منتشر کنیم (library package) ضروری است. با استفاده از این پارامتر ما میتونیم یک توضیحات خلاصه از پکیجمون و کاری که میکنه رو فراهم کنیم که زمانی که دیگران دنبال پکیج ما میگردند (مثلا در pub.dev) توضیحات مربوط به پکیج ما رو ببینند و با پکیج ما آشنا بشن. اما اگه قصد ندارید که پکیج خودتون رو منتشر کنید (application package) این پارامتر اختیاریست و میتونید اون رو نادیده بگیرید.قوانینی که برای نوشتن description وجود داره عبارتند از :حتما به زبان انگلیسی باشدخلاصه باشد. بین 60 تا 180 کاراکترنحوه نوشتن description در pubspec.yamlنمایش توضیحات یک پکیج در pub.devHomepage :اگه بخوایم آدرس وب سایت پکیجمون رو مشخص کنیم میتونیم این پارامتر رو مقدار دهی بکنیم.اما این پارامتر کاملا اختیاریه چه واسه library package ها چه واسه application package ها.اما اگه پکیج شما یک library package هست که قصد دارید اون رو منتشرش کنید بهتره که این پارامتر رو مقداردهی کنید تا کسانی که میخوان از پکیج شما استفاده کنن بفهمن پکیج شما از کجا اوومده.تنها قانونی هم که واسه مقداردهی این پارامتر وجود داره اینه که :حتما باید یک url باشهنحوه مقداردهی homepage در pubspec.yamlنمایش homepage در pub.devRepository :این پارامتر هم شبیه به homepage است ینی هم کاملا اختیاریست و هم اینکه حتما باید یک url باشد با این تفاوت که آدرس url ای که اینجا قرار میدید آدرس source code پکیج شماست. اگر پکیج شما روی github قرار داره میتونید آدرس github پکیجتون رو برای این پارامتر قرار بدید.به این صورت : https://github.com/&lt;user&gt;/&lt;repository&gt;با اینکه مثل homepage کاملا اختیاریه ولی اگه قصد دارید پکیج تون رو عمومی منتشر کنید بهتره که این پارامتر رو مقدار دهی کنید تا اطلاعات بیشتری در دسترس کسانی که میخوان از پکیج شما استفاده کنند قرار بدید.نحوه مقداردهی repository در pubspec.yamlIssue tracker :این پارامتر هم مثل دوتا پارامتر قبلیه یعنی هم کاملا اختیاریه هم اینکه حتما باید یک url باشه ولی فرقش اینه که آدرسی که قرار میدیم باید آدرس جایی باشه که کسانی که از پکیج شما استفاده میکنن بتونن باگ هایی که روی پکیج وجود داشت رو ببینن و هم اینکه اگه باگی وجود داشته باشه اون رو ثبتش کنن که شما بتونید اون باگ رو بررسی و رفعش کنید.با اینکه کاملا اختیاریه ولی بهتره که برای پکیج های عمومی این پارامتر رو مقدار دهی کنید.نکته : اگه پکیج شما توی pub.dev باشه و پارامتر repository (پارامتر قبلی) رو با آدرس github پکیجتون مقدار دهی کرده باشید، در این حالت اگه پارمتر issue tracker رو مقدار دهی نکنید خود pub.dev به صورت اتوماتیک issue tracker گیت هاب پکیجتون رو به عنوان مقدار این پارامتر قرار میده. (https://github.com/&lt;user&gt;/&lt;repository&gt;/issues)نحوه مقداردهی issue_tracker در pubspec.yamlDocumentation : برخی از پکیج ها یک صفحه وبی دارند که توی اون صفحه مستندات کامل اون پکیج گذاشته شده. اگه پکیج شما هم به صورت عمومی منتشر شده و این صفحه مستندات رو هم به صورت جداگانه داره میتونید آدرس اون صفحه رو به عنوان مقدار این پارامتر قرار بدید.این پارامتر هم برای library package ها و هم برای application package ها کاملا اختیاریست. و حتما باید یک url باشد.Dependencies :اگر میخوایم توی برنامه خودمون از یک سری پکیج دیگه استفاده کنیم باید اسم اون پکیج ها رو در این بخش وارد کنیم. ما دو نوع dependency داریم. dependencies و dev_dependencies. اما فرقشون چیه ؟اگه به یک پکیج هم در فاز توسعه و هم در فاز اجرا نیاز داشته باشیم اسم اون پکیج رو در بخش dependencies قرار میدهیم و این پکیج ها در خروجی نهاییه برنامه وجود خواهند داشت ولی اگه به یک پکیج تنها در فاز توسعه و طراحی نیاز داشته باشیم اون پکیج رو در بخش dev_dependencies قرار میدیم. مثلا اگه از پکیج های linter استفاده میکنید که یک سری قوانین روی کد نوشتن شما اعمال میکنه یا از پکیج هایی استفاده میکنید که یک سری کد رو واسه ما تولید میکنه و یا از پکیج های تست نویسی استفاده میکنید، چون این کارها فقط در فاز توسعه و طراحی به درد ما میخوره پس این پکیج ها رو در بخش dev_dependencies قرار میدیم.نکته : dev_dependencies ها در خروجی نهاییه برنامه ما وارد نمیشنما به 4 روش میتونیم یک پکیج رو به برناممون اضافه کنیم :استفاده از پکیج هایی که روی سایت pub.dev هستنداستفاده از پکیج هایی که روی سیستم local خودمون هستند. (path pacakge)استفاده از پکیج هایی که روی git هستنداستفاده از پکیج هایی که روی سرورهای میزبان پکیج هستند. (pub.dev هم یکی از این سرورهاست)نحوه مقداردهی dependencies و dev_dependencies در pubspec.yamlDependency Overrides :این سناریو را تصور کنید: ما از پکیج bloc با نسخه 6.0.0 به صورت مستقیم توی برناممون استفاده می کنیم، اما در عین حال از یه پکیج دیگه ای هم توی برناممون استفاده میکنیم که اون پکیجه خودش از نسخه 5.0.0 bloc استفاده می کنه.الان برنامه ما به صورت مستقیم داره از نسخه 6 بلاک استفاده میکنه و به صورت غیر مستقیم به نسخه 5 بلاک هم وابسته است. الان با این شرایط اگه دستور flutter pub get رو بزنیم به ما ارور میده که ورژن های بلاک با هم همخونی ندارن. حالا اگه ما نتونیم ورژن bloc ای که توی پکیج دیگه داره استفاده میشه و ورژن 5 رو داره زیادش کنیم و به ورژن 6 تبدیلش کنیم، یا باید ورژن bloc ای که داریم ازش به صورت مستقیم استفاده میکنیم رو از 6 بیاریم روی 5 (که خب این باعث میشه از ویژگی های جدید پکیج بلاک محروم بشیم) یا اینکه از dependency_overrides استفاده کنیم. و بهش بگیم که برنامه من حتما باید از نسخه 6 بلاک استفاده کنه صرف نظر از اینکه هر پکیجی داره از چه نسخه بلاکی استفاده میکنه. و پکیج بلاک رو به شکل زیر در لیست dependency_overrides  قرار میدهیم:dependency_overrides:
  flutter_bloc: 6.0.6الان با این کار برنامه من کلا با بلاک نسخه 6.0.6 کار میکنه و دیگه کاری نداره که پکیج های دیگه ای که از بلاک استفاده میکنن و توی برنامه ما هم هستن از چه نسخه بلاکی استفاده میکنن. الان اگه flutter pub get کنیم مشکل ناهمخونی ورژن ها برطرف میشه.( اما استفاده از dependency_overrides کمی خطرناک است و در بعضی مواقع باعث ارور های بیشتری میشه : میتونی این ارورها رو در ویدیو یوتوبم ببینی )Publish_to :به صورت پیش فرض پکیج ما روی pub.dev منتشر میشه اگه میخوایم که روی هیچ جایی منتشر نشه میتونیم مقدار none رو بهش بدیم :publish_to: noneو اگه میخوایم از سرور های میزبان پکیج دیگه استفاده کنیم میتونیم این پارامتر رو مقدار دهی کنیم.این پارامتر هم کاملا اختیاری است. Flutter section :در پایین فایل ما قسمتی به نام flutter رو می بینیم. زمان ایجاد یک پروژه جدید، می بینیم که از قبل یک پارامتر دارد به اسم uses-material-design که برابر با true است و اگه ما میخوایم از icon font های متریال استفاده کنیم حتما باید مقدارش رو برابر با true بزاریم.و در آخر هم میتونیم asset ها و font های خودمون رو در بخش flutter مشخص کنیم :خب دیگه رسیدیم به آخر این مقاله ?? و من توی این مقاله تمام فیلدهای مهمی که توی فایل pubspec.yaml وجود داشت رو به شما توضیح دادم و امیدوارم که واستون مفید بوده باشه.در انتها اگه میخواین این مبحث رو کامل تر و عمیق تر متوجه بشید میتونید ویدیوی کانال یوتوب من که مربوط به فایل pubspec.yaml میشه رو نگاه کنید. توی اون ویدیو مفاهیم همراه با مثال و جزئی تر بررسی شده اند. لینک ویدیو آموزش فایل pubspec.yaml به صورت کامل : https://youtu.be/zx4NvRdaxacلینک کانال یوتوب من : https://www.youtube.com/c/FlutterStanلینک کانال تلگرام من برای با خبر شدن از آخرین مقالات و ویدیو ها : https://t.me/flutter_stanممنون میشم ازتون که با دنبال کردن کانال یوتوب من از من حمایت کنید  ❤️❤️❤️❤️</description>
                <category>Ali Hoseinpoor</category>
                <author>Ali Hoseinpoor</author>
                <pubDate>Fri, 28 Jan 2022 20:15:37 +0330</pubDate>
            </item>
                    <item>
                <title>آیا زبان دارت قویه ؟؟ ??</title>
                <link>https://virgool.io/@Ali_Hoseinpoor/%D8%A2%DB%8C%D8%A7-%D8%B2%D8%A8%D8%A7%D9%86-%D8%AF%D8%A7%D8%B1%D8%AA-%D9%82%D9%88%DB%8C%D9%87-osp8vpyhw1fe</link>
                <description>سلام دوستان گلم امیدوارم حالتون خوب باشه ❤️❤️قبل از اینکه بخوایم وارد بحث بشیم بگم که تمام مباحثی که توی این مقاله میگم جامع تر و کامل ترش توی کانال یوتوبم هست ممنون میشم به اون هم سر بزنید ??? : تماشای ویدیو در یوتوب خیلی وقتا شده که دوستان ازم میپرسن دارت اصن زبان قویه ؟؟یا تو گروه های تلگرام میبینم که جنگ و دعوا شده سر اینکه یکی میگه دارت بهترین زبان دنیاس یکی میگه نه دارت بدترین زبان دنیاس ???. ( مثه اتفاقی که واسه رونالدو و مسی میوفته ?? )تو این مقاله میخوام یه بار واسه همیشه به این سوال جواب بدم که آقا دارت چجور زبونیه. اصن ارزش داره بریم سمتش یا نه ولش کنیم به امون خدا.اولین نکته ای که وجود داره اینه که اصن این سوال از بیخُ و بُن غلطه چون ما نمیتونیم بگیم یه زبان بهترین زبان دنیاس و یا یه زبان بدترین زبان دنیاس چون آقا هر زبانی رو بهر کاری ساختن ?? قرار نیس یه زبانی ساخته بشه که به تمام نیاز های ما جواب بده و در عین حال هم سریع ترین باشه هم قوی ترین و هم ..... این یکی از محکم ترین قوانین دنیاس که شما نمیتونی بهترینِ کل باشی واسه همین اگه میخوای بهترین زبان از لحاظ سرعت باشی قطعا یه چیز دیگه رو باید از دست بدی پس ما باید بر اساس نیازمون و بر اساس حوزه ای که میخوایم توش فعالیت کنیم یک زبان رو انتخاب کنیم که توی اون حوزه یکی از خوب ها باشه. پس لطفا دیگه سر اینکه کدوم زبان بهترین زبان دنیاس دعوا نکنین ?.پس دوستان اگه قراره مقایسه ای صورت بگیره باید تو حوزه های یکسان این کار انجام بشه نه اینکه بیایم مثلا دارت رو با پایتون مقایسه کنیم یا فلاتر رو با جاوا چون فلاتر اصن زبان نیست و فقط یک فریمورک ui هست پس مقایسه اون با یک زبان اصلا کار درستی نیست.خب الان که این همه سخنرانی کردم ?? وقتشه یکم به خود زبان دارت بپردازم :زبان دارت یک سری ویژگی خیلی خوب داره که به نظرم توی حوزه خودش اون رو به یکی از بهترینا تبدیل کرده (دقت کنید که گفتم یکی از بهترینا)Syntax : گرامر زبان دارت چیزی شبیه به  جاوا و c++ است اما با این تفاوت که خیلی به زبان انسان نزدیک تر و قابل فهم تره و بسیاری از ویژگی های جاوا اسکریپت هم داره. زبان دارت یک زبان شی گرا است و از ارث بری یگانه٬ کلاس های abstract و کلاس های Generic و ... استفاده میکنه. کلا طبق شواهدی که وجود داره گوگل زبان دارت رو توسعه داده تا جایگزین جاوا اسکریپت بشه واسه همین خیلی از قابلیت هایی که جاوا اسکریپت داره رو دارت هم داره.خب تو بخش اول که به نظرم سربلند بود چون هم شی گراس هم ویژگی های جاوااسکریپت رو داره و هم اینکه گرامرش خیلی نسبت به زبان های دیگه قابل فهم تر و ساده تره. بریم سراغ ویژگی های دیگش .Support AOT and JIT : ویژگی خیلی خوبی که دارت داره اینه که همزمان هم میتونه از کامپایلر AOT استفاده کنه هم از کامپایلر JIT. مثلا اگه با فلاتر کار کرده باشید زمانی که توی فاز توسعه و طراحی هستید واسه اینکه قابلیت Hot Reload و ویژگی های مربوط به دیباگ کردن رو داشته باشید دارت از کامپایلر JIT استفاده میکنه و زمانی که میخواید خروجی نهایی از برنامتون بگیرید و نیاز دارید که اپ سرعت خوب و start up time کمی داشته باشه دارت از کامپایلر AOT استفاده میکنه و این ینی یه چیییز فوق العاده ???. همین اتفاق واسه زمانی که میخواید web app هم درست کنید میوفته ینی توی فاز طراحی از کامپایلر dartdevc استفاده میکنه و توی فاز اجرایی از کامپایلر dart2js. در کل دیدیم که دارت با ۴ تا کامپایلر داره همه چیز رو مدیریت میکنه و خب این یه ویژگی خیلی خوبه که دارت این ویژگی رو داره.Documentation :ویژگی بعدی که دارت داره و من وااااقعا امیدوارم که بقیه زبان ها هم این کار رو بکنند اینه که زبان دارت مستندات خیلی قوی و خوبی داره. ینی شما کافیه مستنداتی که خود دارت فراهم کرده رو بخونید تا از همه چیش سر در بیارید. از مفاهیم اولیه و ابتدایی گفته تا پیچیده ترین مفاهیم، و این خیلی خوبه که یه زبان چنین مستنداتی داشته باشه که تو اکثر مواقع اگه یه وقت کارتون جایی گیر کرد مطمئن باشید که خود مستندات رسمیش کارتون رو راه میندازه و مثل خیلی از زبان های دیگه نمیشه که حالا بخواید کل اینترنت و یوتوب رو زیر و رو کنید تا مشکلتون رو حل کنید.اگه میخواید مستنداتش رو ببینید یه سر به این سایت بزنید : https://dart.dev/guidesهم چنین دارت یک ادیتور آنلاین هم داره که واقعا خیلی عاالیه و شما میتونید روی اون ادیتور کد بزنید و کدتون رو هم اجرا کنید ???. دیگه واقعا از یه زبان چی میخواید ????.لینک ادیتور آنلاین دارت : dartpadSound Type Safe &amp; Sound Null Safe :اول از همه بگم که دارت یه زبانیه که حتما باید تایپ یا نوع یک متغیر رو همون اول مشخص کنید و حالا ویژگی بعدی دارت اینه که هم sound type safe هست هم sound null safe. حالا ینی چی ؟؟Sound type safe : این sound type safe ینی اگه شما تایپ یک متغیر رو گذاشتید int دارت به شما این تضمین رو میده که اون متغیر از زمانی که ایجاد بشه تا زمانی که زندس int میمونه. ینی هم توی زمان کامپایل و هم توی زمان اجرا تایپ متغیر حفظ میشه و به هیچ عنوان احتمالش نیس که احیانا چیزی جز int توی متغیر ریخته بشه. Sound null safe :و sound null safe هم به شما تضمین میده که اگه شما یک متغیری ساختید که نمیتونه مقدار null رو بگیره این متغیر از زمانی که ساخته میشه تا زمانی که زندس ینی چه تو زمان کامپایل و چه تو زمان اجرا این متغیر non null able میمونه و نمیتونه به هیچ عنوان مقدار null رو بگیره.دوستان زبان های خیلی کمی هستند که sound null safe هستند چون بقیه زبان هایی که null safe هستند تنها توی زمان کامپایل این تضمین رو داریم که اگه یک متغیر رو non null able تعریف کنیم نتونه مقدار null رو بگیره و توی زمان اجرا این تضمین رو به ما نمیدن و ممکنه به اون متغیر مقدار null رو بدیم و به ارور های null برخورد کنیم اما دیدیم که دارت هم توی زمان کامپایل هم توی زمان اجرا این تضمین رو به ما میده و به همین خاطر خیلی از ارور های ناخواسته مربوط به null رو برطرف میکنه.dynamic &amp; var :دارت از تایپ dynamic هم پشتیبانی میکنه که ما میتونیم هر مقداری که میخوایم رو به یک متغیر dynamic بدیم هم چنین دارت یک کلمه کلیدی داره به اسم var که اجازه میده ما مستقیم نگیم که متغیرمون از چه جنسیه بلکه خود آنالیزور دارت با استفاده از مقداری که به اون متغیر دادیم میاد نوع اون متغیر رو پیدا میکنه که به این ویژگی هم میگن tyep inferenceیه مثال اگه بخوام بزنم :var number = 4;توی خط بالا من به صورت واضح نگفتم که متغیرم از نوع int عه و نوشتم var. حالا اتفاقی که میوفته آنالیزور دارت با استفاده از مقداری که ما به متغیر number دادیم میفهمه که این مقدار از جنس int هست و جنس متغیر number هم int میزاره و تا آخر عمرش هم int میمونه. ینی مثل dynamic نیست که ما هر چی دلمون خواست توی متغیر بریزیم، بلکه اولین مقداری که توی متغیر ریخته شد آنالیزور جنس اون مقدار رو تشخیص میده و جنس متغیر رو هم همون جنس مقدار میزاره و دیگه جنس متغیر قابل تغییر نیس مزیتش اینه که ما میتونیم تنبلی کنیم و به جای اینکه هر دفعه نوع متغیر رو خودمون بنویسیم از var استفاده کنیم و تعیین نوع رو بندازیم گردن آنالیزور دارت. خب دوستان این بود از یه سری ویژگی های مهم زبان دارت و امیدوارم که جواب سوالتون رو داده باشم که آیا دارت زبان قوی هست یا نه. اما بازم در انتها میگم دارت زبان خوب و قوی هست اما توی حوزه خودش و به هیچ عنوان نمیشه اونو با زبان های دیگه که واسه کار های دیگه هستند مقایسش کرد.در انتها اگه میخواید تمام این ویژگی ها رو به صورت کامل تر و همراه با مثال ببینید میتونید ویدیو مقدمه ای بر زبان دارت که توی کانال یوتوب من هست رو تماشا کنید.لینک کانال یوتوب :  https://www.youtube.com/c/FlutterStanلینک ویدیو مقدمه ای بر زبان دارت : https://www.youtube.com/watch?v=PHjWRDnfHEYممنون از وقتی که گذاشتید ❤️</description>
                <category>Ali Hoseinpoor</category>
                <author>Ali Hoseinpoor</author>
                <pubDate>Thu, 27 Jan 2022 16:33:20 +0330</pubDate>
            </item>
                    <item>
                <title>Flutter Stan</title>
                <link>https://virgool.io/@Ali_Hoseinpoor/flutter-stan-ts5a5kopngsh</link>
                <description>کانال یوتوب flutter stanسلام دوستان عزیزم ❤️بعد از مدتی فعالیت رو ویرگول تصمیم گرفتم آموزش های بعدی رو به صورت ویدیو تهیه کنم تا فرآیند یادگیری چندبرابر بشه و بتونیم ارتباط نزدیک تری برقرار کنیم ??واسه همین یک کانال یوتوب ساختم و شروع به فعالیت کردم ????تو این کانال کلی مباحث جدید و خفن قراره یاد بگیریم درباره برنامه نویسی مخصوصا دارت و فلاتر و هم اکنون هم دوره آموزش زبان برنامه نویسیه دارت در حال برگزار شدنه. ?‍♂️?‍♂️تمام ویدیو ها هم به علت اینکه روی یوتوب هستند رایگانن و شما خیلی راحت میتونید به اون ها دسترسی داشته باشید. کلی ویدیو ها و ایده های خفن دارم که کم کم رونماییشون میکنم و حسابی قرار بترکونیم ????لینک کانال یوتوب : https://www.youtube.com/c/FlutterStanاسم کانال هم هست : Flutter Stanممنونم از حمایت هاتون ❤️❤️</description>
                <category>Ali Hoseinpoor</category>
                <author>Ali Hoseinpoor</author>
                <pubDate>Wed, 26 Jan 2022 00:23:26 +0330</pubDate>
            </item>
                    <item>
                <title>پارس کردن JSON به صورت دستی و اتوماتیک در فلاتر</title>
                <link>https://virgool.io/flutter-community/%D9%BE%D8%A7%D8%B1%D8%B3-%DA%A9%D8%B1%D8%AF%D9%86-json-%D8%A8%D9%87-%D8%B5%D9%88%D8%B1%D8%AA-%D8%AF%D8%B3%D8%AA%DB%8C-%D9%88-%D8%A7%D8%AA%D9%88%D9%85%D8%A7%D8%AA%DB%8C%DA%A9-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-ummcahjcusks</link>
                <description>سلام دوستان گلم امیدوارم که حالتون خوب باشه و سال جدید رو با قدرت شروع کرده باشید.اول از همه چیز بگم که از هم اکنون میتوانید ویدیوهای مربوط به آموزش برنامه نویسی مخصوصا فلاتر و دارت رو در کانال یوتوب من دنبال کنید. لینکشم میزارم همین پایین :https://www.youtube.com/c/FlutterStanتو این آموزش میخوایم پارس کردن Json رو فوتِ آب بشیم جوری که تا Json میزارن جلوتون سریع تو ذهنتون پارسش کنید ??. پس بزن بریم سر وقتش ??.لازم به ذکره که پیش نیاز این مقاله آشنایی اندک با موارد زیر است :زبان برنامه نویسی دارتفریمورک فلاتر مباحث ابتداییه کلاس ها و متدهای سازندهمباحث اولیه کار با Mapها در دارتخب اول از همه مثه تمام مقاله های دیگم از پایه و مفاهیم شروع میکنم :اصلا این Json چی هست ؟جی‌سان (JSON) مخفف کلمه JavaScript Object Notation بوده و یک استاندارد  باز است که با ساختاری خوانا برای انسان و هم ماشین، می‌توان اطلاعات و  داده‌های مختلف از جمله داده‌های یک دیتابیس را با استفاده از آن، بین  عوامل مختلف مثلاً مرورگر کاربر و یک سایت منتقل کرد یا در فضای ذخیره  سازی‌ای، آن را ذخیره نمود.ساختار Json :ساختار json بسیار ساده است و همین سادگی یکی از دلایل برتری آن نسبت به  xml است چون با این ساختار، خود کاربر و انسان نیز می‌تواند به  راحتی محتوا را بخواند. قواعد کلی یک نوشته بصورت json به این شکل است:محتوای داخل json با آکولاد باز  ?   }  ?   شروع شده و با آکولاد بسته  ?   {  ?  تمام می‌شوند. این بلاک به عنوان آبجکت مادر نیز شناخته می‌شود.پس ینی تمام محتوای ما باید در یک { } قرار بگیرد.آبجکت‌هاشیء یا آبجکت (Object) در json شامل  مجموعه‌ای نامرتب از داده‌ها (نام/مقدار =&gt; key / value) است که دارای یک نام رشته‌ای  (داخل &quot; &quot;) به عنوان کلید است. کلید آبجکت‌ها بهتر است منحصر به فرد باشد  تا به راحتی قابل تمایز باشند. آبجکت‌ها با آکولاد باز  }  شروع شده و با  آکولاد بسته  {  تمام می‌شوند. کلید با کاراکتر دو نقطه  :  از آکولاد باز جدا  می‌شود. داده‌های داخل آبجکت باید با کاراکتر کاما ( , ) از یکدیگر جدا  شوند. برای مثال:{
    &amp;quotPerson&amp;quot: {
    &amp;quotname&amp;quot: &amp;quotAli&amp;quot,
    &amp;quotlastName&amp;quot: &amp;quotHoseinpoor&amp;quot,
    &amp;quotbirth&amp;quot: 1996
    }
}در مثال بالا ما یک آبجکت با نام کلید Person داریم که دارای خصوصیاتی با  مقادیر name برابر Ali و lastName برابر Hoseinpoor و birth برابر ۱۹۹۶ است.همونجور که دیدید json با { } شروع شده و دارای یک آبجکت به اسم Person است که این آبجکت هم با { } شروع و خاتمه میابد.آرایه‌هاآرایه یا Array در json می‌تواند شامل چندین  مقدار (از یک نوع ارزش) باشد. آرایه‌ها معمولاً دارای یک نام رشته‌ای  (داخل &quot; &quot;) به عنوان کلید است. کلید آرایه‌ها بهتر است منحصر به فرد باشد  تا به راحتی قابل تمایز باشند. آرایه‌ها با براکت باز ] شروع شده و با  براکت بسته [ تمام می‌شوند. کلید با کاراکتر دو نقطه : از براکت باز جدا  می‌شود. آبجکت‌های داخل آرایه باید با کاراکتر کاما ( , ) از یکدیگر جدا  شوند.برای مثال: آرایه‌ای از آبجکت‌ها{
    &amp;quotPersons&amp;quot: [
    {&amp;quotname&amp;quot: &amp;quotAli&amp;quot, &amp;quotlastName&amp;quot: &amp;quotHoseinpoor&amp;quot},
    {&amp;quotname&amp;quot: &amp;quotReza&amp;quot, &amp;quotlastName&amp;quot: &amp;quotHoseini&amp;quot},
    {&amp;quotname&amp;quot: &amp;quotNavid&amp;quot, &amp;quotlastName&amp;quot: &amp;quotHashemi&amp;quot}
    ]
}در این مثال ما یک آرایه به نام Persons داریم که دارای سه آبجکت است. هر آبجکت نیز دو جفت نام/مقدار دارد.نمونه دیگر: آرایه‌ای از یک نوع مقادیر:{
    &amp;quotAges&amp;quot: [
    ۲۵, ۱۲, ۶۵, ۱۶
    ]
}نکته: آرایه فقط می‌تواند شامل یک نوع ارزش باشد. برای مثال یا همه آیتم‌هایش آبجکت باشد یا رشته یا ... .اما این ارزش ها کلا چیا میتونن باشن ؟•    رشته‌ها•    اعداد•    آبجکتی دیگر•    آرایه‌ای دیگر•    مقدار بولی - درست یا غلط (True / False)•    مقدار تهی (Null)قوانین JSONعبارت‌های JSON باید میان آکولاد «{ }» قرار بگیرند.اعضای شیء با علامت ویرگول«,» از هم جدا می‌شوند.برای تعریف یک عضو در یک شیء JSON، ابتدا «نام عضو» سپس دونقطه«:» و در پایان «مقدار» نوشته می‌شود.مقدار  می‌تواند یکی از انواع «عدد»، «رشته»، «بولی»، «آرایه»، «شیء» و «نال» را  بپذیرد. سایر انواع داده باید به صورتی دیگر ذخیره شوند.انواع رشته‌ای باید داخل دو گیومه «&quot;» قرار بگیرند.بسته به زبان مبدا و مقصد انواع داده‌ی دیگری نیز ممکن است قابل پذیرش باشند.خب به نظرم واسه مفاهیم کافی باشه بزن بریم سراغ پارس کردنش.اولین و مهم ترین قانونی که توی پارس کردن وجود داره اینکه همیشه از درونی ترین آبجکت شروع کنید به پارس کردن.قانون دوم : هر کلید/مقدار توی json باید یک فیلد متناظر در کلاس شما داشته باشه.قانون سوم(تکمیلیه قانون بالا) : هر چیزی که از کلید/مقدارهای json مدنظرتون هست رو توی کلاس هاتون ذخیره کنید. به فرض شما نیاز ندارید تاریخ تولد رو توی کلاس هاتون داشته باشید پس اون رو نیاز نیست پارس کنید. مثلا اگه توی json ما یه آبجکت Person داشته باشیم که کلید های اسم، فامیل و تاریخ تولد رو داشته باشه و شما فقط به اسم و فامیل نیاز داشته باشید در این حالت نیاز نیس برای کلاسی که میخواید بسازید فیلد تاریخ تولد رو در نظر بگیرید. قانون چهارم : هر آبجکت متناظر با یک کلاس در کدهاتون است. و هر آرایه متناظر با لیست.به فرض اگر شما یک آرایه از آبجکت توی json داشته باشید توی کدتون باید یک لیست از کلاس را نگه دارید. و یا اگر یک آرایه از رشته در json دارید در کدتون باید یک لیست از رشته را نگه دارید و ... .خب الان با یک json خیلی ساده شروع میکنیم :{
    &amp;quotname&amp;quot: &amp;quotAli&amp;quot,
    &amp;quotlastName&amp;quot: &amp;quotHoseinpoor&amp;quot,
    &amp;quotbirth&amp;quot: 1996
}خب همونجوری که گفتم باید از درونی ترین آبجکت شروع کرد که تو این مثال اولیه ما کلا یه آبجکت بیشتر نداریم و همونجور که گفتیم هر آبجکت متناظر با یک کلاس هست. پس الان طرح اولیه کلاسمون رو مینویسیم :class Person {
  final String name;
  final String lastName;
  final int birth;

  Person({
    this.name,
    this.lastName,
    this.birth,
  });
}خب ما الان یه کلاس داریم به اسم Person(اسم کلاس به انتخاب خودتونه اما بهتره که اسامی مرتبط و قابل فهم باشه) که ۳تا فیلد تو خودش داره دقیقا متناظر با همون داده هایی که توی json بود. الان وقتشه یه متدی بنویسیم که کار پارس کردن رو واسه ما انجام بده :factory Person.fromJson(Map&lt;String, dynamic&gt; json) {
  return Person(
    name: json[&#039;name&#039;] as String,
    lastName: json[&#039;lastName&#039;] as String,
    birth: json[&#039;birth&#039;] as int,
  );
}خب بریم ببینیم این کد اصن چی هست ؟ اصن factory چی میگه این وسط؟خب من اول از همه یک  named constructor (متد سازنده با اسم) ساختم به اسم Person.fromJson ینی در واقعا این همون متد سازنده هست و همونجور که میدونید توی دارت میشه چندتا سازنده داشت و اسمای مختلفی روشون گذاشت. پس چون این یک متد سازنده هست ما موظف هستیم که یک آبجکت از جنس Person رو برگردونیم. اما چرا factory ؟ بیایید یک نگاه به متد سازنده عادی داشته باشیم تا فرقش با factory رو بفهمیم :Person() {
  name = &#039;Ali&#039;;
  lastName = &#039;Hoseinpoor&#039;;
  birth = 1996;
}این یک متد سازنده عادی هست که توی بدنش من اوومدم مقدار دهی رو انجام دادم اما نکته مهم اینجاس که اگه من توی خود بدنه سازنده مقدار دهی بکنم نمیتونم پراپرتی های کلاس رو از نوع final تعریف بکنم چون به من ارور میده و میگه متغیر های final حتما باید قبل از بدنه سازنده صدا زده بشن. ولی اگه بخواین متغیرهاتون حتما final باشن باید سازنده رو به این صورت تغییر بدین : Person({String name, String lastName, int birth}) :
  name = name,
  lastName = lastName,
  birth = birth;یاPerson({
  this.name,
  this.lastName,
  this.birth,
});اگه کمی زبان دارت رو کار کرده باشید میدونید که در این دو سازنده مقدار دهی قبل از بدنه صورت میگیرد و دیگر ارور مربوط به final ها رو نداریم.پس حالا فرق سازنده عادی با factory چی شد ؟ فرقش این شد که ما در سازنده عادی همیشه یک نمونه جدید از کلاس رو برمیگردونیم و دسترسی به کلمه return نداریم اما در factory ما میتونیم روی نمونه‌ای که میخوایم از کلاسمون برگردونیم کنترل داشته باشیم و به return هم دسترسی داریم. (از factory میتونیم توی singleton کلاس ها هم استفاده کنید. کلا چیز خوبیه یه سرچ دربارش حتما بکنید چون توضیح کاملش خارج از مبحث ما هست ) . مثلا فرض کنید قراره روی تاریخ تولد اول یه پردازشی انجام بشه بعد نتیجه اون پردازش بره بشینه توی پراپرتی birth.این factory به ما این قابلیت رو میده که بتونیم یک سری پردازش و ... رو انجام بدیم و در نهایت حتما یک نمونه از کلاس رو return کنیم. پس اگه قرار متغیرهامون final باشن متد سازنده fromJson هم باید factory بشه چون ما قراره توی بدنه سازنده fromJson مقادیر json رو به پراپرتی ها نسبت بدیم و چون تو بدنه داریم این کارو میکنیم و متغیر ها هم final هستن اگه از factory استفاده نکنیم ارور میده که تمام متغیرهای final باید قبل از بدنه مقدار دهی بشن ولی اگه ما متد سازنده fromJson رو factory کنیم چون توش میایم یک نمونه از کلاس رو در آخر return میکنیم دیگه اون ارور مربوط به final ها رو نداریم.کد کامل بدون استفاده از final :class Person {
  String name;
  String lastName;
  int birth;

  Person({
    this.name,
    this.lastName,
    this.birth,
  });

  Person.fromJson(Map&lt;String, dynamic&gt; json) {
    name = json[&#039;Person&#039;][&#039;name&#039;] as String;
    lastName = json[&#039;Person&#039;][&#039;lastName&#039;] as String;
    birth = json[&#039;Person&#039;][&#039;birth&#039;] as int;
  }
}کد کامل با استفاده از final :class Person {
  final String name;
  final String lastName;
  final int birth;

  Person({
    this.name,
    this.lastName,
    this.birth,
  });

  factory Person.fromJson(Map&lt;String, dynamic&gt; json) {
   return Person(
      name: json[&#039;Person&#039;][&#039;name&#039;] as String,
      lastName: json[&#039;Person&#039;][&#039;lastName&#039;] as String,
      birth: json[&#039;Person&#039;][&#039;birth&#039;] as int,
    );
  }
}فرقشون فقط توی استفاده از factory هست.خب حالا که factory رو گفتم بریم سراغ ادامه تحلیل متد سازنده fromJson. نکته بعدی ورودی هست که این متد قبول میکنه : Map&lt;String,dynamic&gt; jsonشما وقتی json رو از سرور گرفتین باید به Map تبدیلش کنید که بتونید به راحتی ازش استفاده کنید و دیتاهاش رو در بیارین. کلید ها که همیشه رشته هستن ولی مقادیر میتونن انواع مختلف دیتا باشن واسه همین میگیم &lt;String,dynamic&gt;.حالا که json رو به عنوان یک Map داریم میتونیم به راحتی داده هاش رو بیرون بکشیم و پراپرتی های کلاس رو مقدار دهی کنیم.الان اگه بخوایم به مقدار name توی json (که الان به صورت Map در اختیار ماست) دسترسی داشته باشیم کافیه بگیم :json[&#039;name&#039;]ما برای دسترسی به مقادیر یک کلید در Map کافیه اسم کلید رو در [ ] بگیم. الان وقتی میگیم json[&#x27;name&#x27;] ینی بیاما برای دسترسی به مقادیر یک کلید در Map کافیه اسم کلید رو در [ ] بگیم. الان وقتی میگیم json[&#x27;name&#x27;] ینی بیا مقدار کلید name  رو به من بده که در این حالت اسم رو به شما برمیگردونه. واسه بقیه پراپرتی ها هم همینطور. پس نکته مهم این بود که json ما تبدیل بشه به Map (که اینم زمانی که دارین از سرور دیتا میگیرین باید اون Json رو دیکود کنین تا به شکل Map در بیاد که خارج از این آموزش هست.)و بعد با استفاده از کلیدهای Map بیایم و مقادیر رو بگیریم.پس تا اینجا یاد گرفتیم که چجور داده های موجود در json رو واکشی کنیم و اونارو بریزیم توی فیلدهای کلاسمون. پس الان اگه ما Person.formJson(json) رو صدا بزینم به ما یک کلاس برمیگردونه که پراپرتی هاش با مقادیر json مقدار دهی شده اند.خب نظرت چیه سخت ترش کنیم json رو ؟{
  &amp;quotPerson&amp;quot: {
  &amp;quotname&amp;quot: &amp;quotAli&amp;quot,
  &amp;quotlastName&amp;quot: &amp;quotHoseinpoor&amp;quot,
  &amp;quotbirth&amp;quot: 1996
  },
  &amp;quotCar&amp;quot: {
  &amp;quotname&amp;quot: &amp;quotLamborghini&amp;quot,
  &amp;quotspeed&amp;quot: 350
  }
}خب الان json ما دوتا آبجکت داره یکی Person و یکی هم Car الان درونی ترین‌ آبجکت کدوم میشه ؟فرقی ندارن جفت Person و Car در یک سطحن پس با هر کدوم میتونید شروع کنید.طبق قانونی که گفتم هر آبجکت باید یک کلاس بشه پس بیایید کلاس هاشون رو بسازیم.کلاس Car : class Car {
  String name;
  String speed;

  Car({
    this.name,
    this.speed,
  });

  factory Car.fromJson(Map&lt;String, dynamic&gt; json) {
    return Car(
      name: json[&#039;name&#039;] as String,
      speed: json[&#039;speed&#039;] as String,
    );
  }
}که دقیقا مثه قبل ساخته شده و چیز جدید و عجیبی نداره.کلاس person هم که قبلا ساختیم :class Person {
  final String name;
  final String lastName;
  final int birth;

  Person({
    this.name,
    this.lastName,
    this.birth,
  });

  factory Person.fromJson(Map&lt;String, dynamic&gt; json) {
   return Person(
      name: json[&#039;name&#039;] as String,
      lastName: json[&#039;lastName&#039;] as String,
      birth: json[&#039;birth&#039;] as int,
    );
  }
}خب حالا اگه به خود json دقت کنید میبینید که json همیشه خودش با یه آبجکت کلی(آبجکت مادر) شروع میشه(json همیشه با } شروع و با { تموم میشه) ینی در واقع ما یه آبجکت کلی داریم که توش دوتا آبجکت Person و Car هستش. پس از اونجایی که گفتم هر آبجکت باید یه کلاس باشه الان ما باید یه کلاس بسازیم که شامل دوتا کلاس Person و Car بشه.(واسه همین میگم همیشه از آبجکت ها درونی شروع کنید.)class MyData {
  final Person person;
  final Car car;

  MyData({
    this.person,
    this.car,
  });

  factory MyData.fromJson(Map&lt;String, dynamic&gt; json) {
    return MyData(
      car: Car.fromJson(json[&#039;Car&#039;] as Map&lt;String, dynamic&gt;),
      person: Person.fromJson(json[&#039;Person&#039;] as Map&lt;String, dynamic&gt;),
    );
  }
}کلاس MyData الان کلاس کلی من هست که توش کلاس Person و Car وجود داره. و توی متد سازنده MyData.fromJson اوومدیم این دوتا کلاس رو مقدار دهی کردیم اما چجور ؟بیینید چون ما قبلا کلاس Person و Car رو ساخته بودیم و واسه هر کدومشون هم fromJson رو نوشته بودیم میتونیم اینجا برای مقدار دهی این دو کلاس از همون متد سازنده fromJson کلاس ها استفاده کنیم.اگه دقت کرده باشید مثلا برای car گفتم : car: Car.fromJson(json[&#x27;Car&#x27;] as Map&lt;String, dynamic&gt;)به اون مقداری که به ورودی متد Car.fromJson دادم دقت کنید : json[&#x27;Car&#x27;]. ینی از اون json که داری مقدار کلید Car رو بده بهش تا بره اون مقدار رو پارس کنه و بریزه تو کلاس Car برای Person هم دقیقا همین عمل انجام شده.خوب دوباره به json نگاه کنید بچه ها.(میخوام که قشنگ واستون جا بیوفته) فایل json ما یه کلید داره به اسم Car که خودش دوباره یه آبجکته دیگس پس اگه من بگم json[&#x27;Car&#x27;]  در واقع مقدار کلید Car رو به من میده که مقدارش یه آبجکته حالا اگه این آبجکت که شامل name و speed هست رو بدم به Car.fromJson که قبلا نوشته بودم واسه کلاس Car میاد دقیقا اون آبجکت رو واسه من پارس میکنه و تهش یه نمونه از کلاس Car به من برمیگردونه که متغیرهاش با مقادیر json مقدار دهی شدن. این اتفاق دقیقا واسه Person هم میوفتهپس ما اول از درونی ترین آبجکت ها شروع کردیم و کلاس و fromJson اون ها رو ساختیم بعد یه لول اوومدیم بالاتر و یه کلاس دیگه ساختیم که شامل دوتا کلاس Person و Car باشه چون Json ما به این شکل بود یه آبجکت کلی بود که شامل دوتا آبجکت Person و Car بود پس ما هم باید یه کلاس داشته باشیم تا شامل دوتا کلاس Person و Car باشه.الان هم میتونیم به راحتی بگیم : MyData.fromJson(json) تا واسه ما Json رو پارس کنه و یه کلاس به ما بده که شامل دیتاهای مد نظر ماست.خب حالا یکم سخترترش کنیم ها ؟؟ ??{
    &amp;quotPerson&amp;quot: {
    &amp;quotname&amp;quot: &amp;quotAli&amp;quot,
    &amp;quotlastName&amp;quot: &amp;quotHoseinpoor&amp;quot,
     &amp;quotbirth&amp;quot: {
        &amp;quotyear&amp;quot: 1996,
        &amp;quotmonth&amp;quot: &amp;quotJan&amp;quot,
        &amp;quotday&amp;quot: 26
       }
    },
    &amp;quotCar&amp;quot: {
    &amp;quotname&amp;quot: &amp;quotLamborghini&amp;quot,
    &amp;quotspeed&amp;quot: 350
    }
}خب اگه دقت کنید این دفعه birth توی آبجکت Person از ۱۹۹۶ به یک آبجکت تغییر پیدا کرده که توی اون آبجکت سال و ماه و روز کامل نوشته شده.پس الان درونی ترین آبجکت کدومه ؟؟ آفرین birth الان درونی ترین آبجکت میشه. پس اول واسه اون یک کلاس میسازیم :class Birth {
  final int year;
  final String month;
  final int day;

  Birth({
    this.year,
    this.month,
    this.day,
  });

  factory Birth.fromJson(Map&lt;String, dynamic&gt; json) {
    return Birth(
      year: json[&#039;year&#039;] as int,
      month: json[&#039;month&#039;] as String,
      day: json[&#039;day&#039;] as int,
    );
  }
}این کلاس Birth یه نگا بهش بندازین. بازم هیچ چیز خاصی نداره و دقیقا مثه قبله.خب حالا یه مرحله میریم بالا تر که میرسیم به آبجکت Person حالا واسه اون یه کلاس میسازیم :class Person {
  final String name;
  final String lastName;
  final Birth birth;

  Person({
    this.name,
    this.lastName,
    this.birth,
  });

  factory Person.fromJson(Map&lt;String, dynamic&gt; json) {
    return Person(
      name: json[&#039;name&#039;] as String,
      lastName: json[&#039;lastName&#039;] as String,
      birth: Birth.fromJson(json[&#039;birth&#039;] as Map&lt;String, dynamic&gt;),
    );
  }
}تنها فرقی که کلاس Person کرده اینه که به جای int birth الان باید بشه Birth birth چون birth دیگه عدد نیست و الان یه آبجکته که کلاس شده و واسه پارس کردنش هم باید از متد سازنده fromJson کلاس Birth  استفاده کنیم همونجور که نوشتیم : birth: Birth.fromJson(json[&#x27;birth&#x27;] as Map&lt;String, dynamic&gt;)پس کلاس Person هم به همین سادگی نوشته شد بریم سراغ Car. کلاس Car هیچ تفاوتی نکرده و دقیقا مثه قبله پس دیگه نمینویسمش که شلوغ نشه اینجا.و در نهایت یه مرحله میریم بالاتر و به آبجکت اصلی میرسیم که دوتا آبجکت Person و  Car توش وجود داره. که اون کلاس هم دقیقا مثه قبله بدون هیچ تغییری.بازم یکم سخت ترش کنیم ها ؟؟ میخوایم بریم سراغ آرایه ها{
    &amp;quotPerson&amp;quot: {
        &amp;quotname&amp;quot: &amp;quotAli&amp;quot,
        &amp;quotlastName&amp;quot: &amp;quotHoseinpoor&amp;quot,
        &amp;quotbirth&amp;quot: {
            &amp;quotyear&amp;quot: 1996,
            &amp;quotmonth&amp;quot: &amp;quotJan&amp;quot,
            &amp;quotday&amp;quot: 26
        },
        &amp;quotfamily&amp;quot: [
            &amp;quothasan&amp;quot,
            &amp;quotfarshad&amp;quot,
            &amp;quotrozhin&amp;quot
        ]
    },
    &amp;quotCars&amp;quot: [
        {
            &amp;quotname&amp;quot: &amp;quotLamborghini&amp;quot,
            &amp;quotspeed&amp;quot: 350,
            &amp;quotmojood&amp;quot: false
        },
        {
            &amp;quotname&amp;quot: &amp;quotBenz&amp;quot,
            &amp;quotspeed&amp;quot: 300,
            &amp;quotmojood&amp;quot: true
        }
    ]
}خب الان به آبجکت Person یه کلید family اضافه شده که یه آرایه از رشته هاس و کلید Car هم کلا تغییر کرده و شده Cars که یه آرایه از آبجکت هاس. پس بزن بریم.دورنی ترین آبجکت چیه ؟دقت کن آبجکت ها فک نکنی آرایه هم جزو آبجکت حساب میشه. کلا آبجکت میشه هر چیزی که بین { } قرار بگیره(توی Json). پس درونی ترین آبجکت میشه همون birth که کلاسش دقیقا مثه قبله. یه مرحله میایم بالاتر و میرسیم به آبجکت Person که کلاس متناظرش میشه این :class Person {
  final String name;
  final String lastName;
  final Birth birth;
  final List&lt;String&gt; family;

  Person({
    this.name,
    this.lastName,
    this.birth,
    this.family,
  });

  factory Person.fromJson(Map&lt;String, dynamic&gt; json) {
    return Person(
      name: json[&#039;name&#039;] as String,
      lastName: json[&#039;lastName&#039;] as String,
      birth: Birth.fromJson(json[&#039;birth&#039;] as Map&lt;String, dynamic&gt;),
      family: json[&#039;family&#039;] as List&lt;String&gt;,
    );
  }
}همونجور که میبینید به این کلاس یه فیلد List&lt;String&gt; family اضافه شده که دقیقا متناظر با داده اون در json هست چون ما توی json یک آرایه از رشته ها داشتیم اینجا توی کلاس هم یک لیست از رشته ها ساختیم و اون رو توی fromJson مقدار دهی مکنیم دقیقا مثه قبل.حالا میریم سراغ آبجکت هایی که توی آرایه Cars وجود داره. اونا هم کلاسشون به این صورت میشه :class Car {
  final String name;
  final String speed;
  final bool mojood;
  

  Car({
    this.name,
    this.speed,
    this.mojood,
  });

  factory Car.fromJson(Map&lt;String, dynamic&gt; json) {
    return Car(
      name: json[&#039;name&#039;] as String,
      speed: json[&#039;speed&#039;] as String,
      mojood: json[&#039;mojood&#039;] as bool,
    );
  }
}خب حالا یه مرحله میایم بالاتر و میرسیم به آبجکت کلی که یه آبجکت Person توشه و یه آرایه از Car ها پس کلاس اون هم اینجوری میشه :class MyData {
  final Person person;
  final List&lt;Car&gt; cars;

  MyData({
    this.person,
    this.cars,
  });

  factory MyData.fromJson(Map&lt;String, dynamic&gt; json) {
    final List&lt;Car&gt; carTemp = [];
    if (json[&#039;Cars&#039;].length != 0) {
      json[&#039;Cars&#039;].forEach((item) {
        carTemp.add(Car.fromJson(item as Map&lt;String, dynamic&gt;));
      });
    }
    return MyData(
      cars: carTemp,
      person: Person.fromJson(json[&#039;person&#039;] as Map&lt;String, dynamic&gt;),
    );
  }
}اینو یکم دقت کنید تا خوب متوجه بشید : این json ما یه آبجکت Person داشت پس کلاس MyData ما هم باید توش یه نمونه از کلاس Person توش باشه دقیقا مثه قبل تا اینجا چیز خاصی نبود اما توی json , ما دیگه آبجکت Car نداریم بلکه یه آرایه از Car ها داریم پس توی کلاس MyData هم باید یه لیست از کلاس Car داشته باشیم . اما واسه پارس کردن این لیست از آبجکت Car ها ما باید اول یه لیست خالی بسازیم. بعد چک کنیم که آیا کلید Cars توی json مقدار داره یا نه یا به عبارت دیگه این آرایه از آبجکت های Car پر هست یا خالیه. حالا اگه خالی نبود با استفاده از forEach میتونیم روی دیتاهای Cars پیمایش کنیم و هر آبجکت Car رو با استفاده از Car.fromJson(json) پارس کنیم و به اون لیست اولیمون اضافه کنیم و در نهایت اون لیستی که اول ساختیم رو انتساب بدیم به پراپرتی cars در کلاس MyData دقیقا مثه کد بالا که نوشتیم. خوب بهش دقت کنید و سعی کنید همراه با خوندن کد متن من رو هم چند بار بخونید تا کامل متوجهش بشید.شاید پیش خودتون بگید خب چرا با اون List&lt;String&gt; این کارو نکردی و از forEach و این چیزا استفاده نکردی؟ببیند لیستی از رشته ها اصن نیاز به پارس شدن نداره همیشه لیستی از یه سری رشتس پس اگه من دیتاش رو بگیرم میتونم خیلی راحت اون لیست رو به فیلد توی کلاسم نسبتش بدماما لیستی از Car ها یه لیست از آبجکته که نیازه اون آبجکت ها هر کدوم جداگانه پارس بشن واسه همین از forEach استفاده کردم تا بتونم به تک تک آبجکت های توی Cars دسترسی داشته باشم و تک تک رو پارس کنم و به لیستی از کلاس Car اضافشون کنم. خب تا اینجا سعی کردم مثال های مختلفی براتون بزنم و همه چیز رو بهتون بگم. بعدش که یکم دستتون راه افتاد واسه پارس کردن میتونید از پکیج فوقالعاده json_serializable واسه پارس کردن json هاتون استفاده کنید. که الان میخوام اونو هم بهتون یاد بدم.خب در مرحله اول باید پکیج های json_serializable  و json_annotation رو به لیست  dependencies هاتون توی فایل pubsec.yaml اضافه کنید و همچنین باید پکیج build_runner رو به لیست  dev_dependencies هاتون اضافه کنید. به شکل  زیر :(فقط حواستون به فاصله ها توی pubspec باشه)dependencies:
  flutter:
    sdk: flutter   json_annotation: ^3.1.1
   json_serializable: ^3.5.1

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner:اما کاری که این پکیج میکنه چیه ؟ ما دیگه نیاز نیس بیاییم دستی متد fromJson رو بنویسیم فقط کافیه یه کلاس بسازیم با پراپرتی هایی که اون کلاس نیاز داره و متد fromJson رو ایندفعه جوری که این پکیج میگه بنویسیم تا خود این پکیج واسه ما به صورت اتوماتیک کدش رو بنویسه اما چجور ؟؟فرض کنید من این json رو دارم : {
    &amp;quotname&amp;quot: &amp;quotAli&amp;quot,
    &amp;quotlastName&amp;quot: &amp;quotHoseinpoor&amp;quot,
    &amp;quotbirth&amp;quot: 1996
}کافیه من یه کلاس بسازم و فیلدهای متناظر با json  رو توش بزارم و اسم اون فیلد ها هم دقیقا مثه اسم همون کلید ها توی json بزارم(بعدا بهتون میگم چجور میشه اسم های متفاوت هم داشت) و به علاوه یکم کدهای دیگه که باید توی کلاسم بزارم. پس اول من کد رو میزارم و بعد ش توضیحش میدم :part &#039;person_entity.g.dart&#039;;

@JsonSerializable(createToJson: false)
class PersonEntity {
  final String name;
  final String lastName;
  final int birth;

  PersonEntity({this.name, this.lastName, this.birth,});

  factory PersonEntity.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$PersonEntityFromJson(json);

}خط اول چی میگه ؟ ببینید این پکیج با استفاده از پکیج build_runner واسه ما کد generate میکنه ینی میاد خودش یه فایل با پسوند g.dart میسازه و توش کد مینویسه و اون خط اول هم میگه که اون فایلی که قراره ساخته بشه بخشی از این فایل هست. اگه شما مثلا یه فایل ساختین به اسم person_entity.dart که توش کلاس PersonEntity رو نوشتین باید توی part اسم همون فایلتون رو بنویسین فقط پسوندش رو بکنید g.dart ینی person_entity.dart  رو باید بنویسید  person_entity.g.dart خب این از خط اول.خط بعدی نوشته : @JsonSerializable(createToJson: false) این چی میگه ؟ببینید ما با این خط داریم annotate میکنیم که این کلاس از JsonSerializable استفاده میکنه و توش هم گفتیم که createToJson برابر با false باشه ینی نمیخوام این پکیج برام متد toJson رو درست کنه فقط میخوام fromJson رو درست کنه(در ادامه این آپشنا رو کامل توضیح میدم)بعدش اوومدم کلاسم رو به صورت عادی نوشتم و فیلدهاشو مشخص کردم و اسم فیلدها رو هم دقیقا مانند اسم کلید ها توی json گذاشتم. چون این پکیج به صورت اتوماتیک واسه واکشیه داده ها از json میاد از اسم خود فیلد های کلاس استفاده میکنه واسه همین باید اسم ها دقیقا مثه هم باشه(هر چند میشه اسم ها رو متفاوت گذاشت که بعدا بهتون میگم چجوری)و در آخر متد fromJson رو نوشتیم. این خط کد دقیقا باید همینجوری باشه و نمیشه تغییرش داد فقط باید اسم کلاس هاتون رو جایگذاری کنید توش. مثلا اگه خواستم بعدا CarEntity داشته باشم جای factory PersonEntity.fromJson باید بنویسم factory CarEntity.fromJson و اون آخر هم جای _$PersonEntityFromJson(json) باید بنویسم _$CarEntityFromJson(json)و تمام الان کافیه توی ترمینال این دستور رو وارد کنید تا خودش براتون کد رو بسازه :flutter pub run build_runner build --delete-conflicting-outputsبعد اینکه این دستور انجام شد میتونید ببینید که فایل person_entity.g.dart به پروژتون اضافه شده و میتونید بازش کنید وببینید که چجور واستون کد رو ساخته شما بعدش کافیه هر جا خواستین اون json رو پارس کنید بنویسید : PersonEntity.fromJson(json) همین.حالا فک کنید من نمیخوام اسم پراپرتی های کلاسم با اسم کلید های json یکی باشه باید چکار کنم ؟شما در این حالت میتونید از @JsonKey(name : &#x27;test&#x27;) استفاده کنید مثلا  :part &#039;person_entity.g.dart&#039;;

@JsonSerializable(createToJson: false)
class PersonEntity {
  final String name;
  
  @JsonKey(name: &#039;lastName&#039;)
  final String family;
  
  @JsonKey(name: &#039;birth&#039;)
  final int birthday;

  PersonEntity({this.name, this.family, this.birthday,});

  factory PersonEntity.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$PersonEntityFromJson(json);

}اینجا با اینکه اسم کلید lastName بوده توی json ولی من تو کلاسم نوشتم  family وبالاش با  JsonKey(name:&#x27;lastName&#x27;) گفتم که وقتی خواستی کد رو generate کنی به جای اینکه از اسم فیلد که family هست استفاده کنی بیا از اسم lastName واسه گرفتن دیتا از json استفاده کن. همینجور واسه birth هم همین کارو کردم و در نهایت باز اون دستور رو میزنم تا کد رو واسم generate کنه. خب همین اول بگم که ما دوتا annotation داریم توی این پکیجیکی JsonKey و یکی هم JsonSerializableاولی که JsonKey باشه روی هر فیلد از کلاس تاثیر میزاره ینی شما باید واسه هر فیلد که خواستین، ازش جداگانه استفاده کنید. اینجوری نیس که یه سری قواعد کلی برای کلاستون و فیلدهاش مشخص کنه. تاثیرش فقط روی یدونه فیلده پس طبعا اگه بخواید روی چندتا فیلد تاثیر بزاره واسه هر فیلد جداگانه باید از JsonKey استفاده کنید. اما JsonSerializable تاثیر کلی داره ینی شما اونو ابتدای کلاس میزارید و میتونید واسه اون کلاس یه سری قواعد کلی رو تعیین کنید تا هنگام generate کردن کد واستون اون قواعد رو رعایت کنه این پکیج. اما مقادیری که میتونیم به JsonKey بدیم چیا هستن ؟ name : اینو گفتم که میتونیم ازش استفاده کنیم تا دقیقا بگیم که وقتی میخوای این فیلد رو مقدار دهی کنی از چه اسمی واسه واکشی داده از json استفاده کنی مثلا اگه همچین چیزی داشته باشم :  @JsonKey(name: &#039;lastName&#039;)
  final String family;بعد اینکه واسه من کد رو generate کرد همچین چیزی توی کد ساخته شده وجود داره :family : json[&#039;lastName&#039;]اگه هم که از name استفاده نکنم همچین چیزی واسه من درست میکنه :family : json[&#039;family&#039;]ینی به صورت دیفالت اسم خود فیلد رو میزاره واسه واکشیه داده ها از json.پس با این name میشه دقیقا به این پکیج بگیم که از این اسم واسه گرفتن داده ها از اون json استفاده کندر واقع ما داریم اسم اون کلید رو مشخص میکنیم چون همونجور که دیگه الان میدونید توی فایل json ما یه سری کلید داریم و وقتی هم این json به شکل Map توی کد ما در میاد ما باید اسم اون کلید رو به Map بدیم تا داده متناظر با اون کلید رو به ما بده که ما اگه واسش name انتخاب نکنیم به صورت دیفالت از اسم خود فیلدها استفاده میکنه حالا اگه اسم فیلد کلاسمون با اسم اون کلید در json متفاوت بود باید با name به این پکیج بگیم که آقا از اسم فیلد واسه گرفتن داده استفاده نکن بیا از این name استفاده کن به جاشignore : وقتی ignore رو برابر با true میزاریم توی کد ساخته شده اون فیلد رو واسه ما مقدار دهی نمیکنه :   @JsonKey(ignore: true)
  final String family;الان وقتی میخواد فیلدهای کلاس رو با مقادیر json مقدار دهی کنه اون فیلدی که گفتیم ignore بشه رو کلا کاری باش نداره و ردش میکنه و اونو مقدار نمیده.واسه جایی خوبه که شما خودتون بعدا میخواید اون فیلد کلاس رو پر کنید و اون فیلد ربطی به دیتاهای json ندارهdefaultValue :بعضی اوقات ممکنه اصلا کلیدی به اون نام که ما میخوایم از سمت سرور برنگرده ینی json ما اصلا کلید lastName رو نداشته باشه یا حتی داشته باشه ولی مقدار اون کلید null باشه ما با استفاده از defaultValue میتونیم توی این مواقع یه مقدار پیش فرض رو بدیم به فیلدمون مثلا : @JsonKey(name: &#039;lastName&#039;,defaultValue: &#039;&#039;)
  final String family;این الان میگه که اولا از اسم lastName واسه پارس کردن استفاده کن بعدش اگه اصلا کلید lastName وجود نداشته یا مقدارش null بود بیاد به صورت دیفالت مقدارش رو یه رشته خالی بزار.fromJson : فرض کنید شما دارید تاریخ تولد رو از json میگیرید اما قبل از اینکه بخواید مقدار همون رو دقیقا بریزید تو فیلد کلاستون میخواید یه سری پردازش روش انجام بدید بعد اون مقدار نهایی رو به فیلد کلاستون انتساب بدید. تو این موقعیت fromJson به کارتون میاد. این fromJson یا باید یه global function (متد سراسری) باشه یا یه متد static مثلا :part &#039;comment_entity.g.dart&#039;;

@JsonSerializable(createToJson: false)
class CommentEntity {
  final int id;
  final String text;
  @JsonKey(fromJson: _calculateDateTime)
  final String createdAt;

  CommentEntity({
    this.id,
    this.user,
    this.text,
  });

  factory CommentEntity.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$CommentEntityFromJson(json);

  static String _calculateDateTime(String dateTime) {
    return calculateDatetime(dateTime: dateTime);
  }
}به کد بالا دقت کنید. من یه کلاس ساختم واسه پارس کردن comment ها که یه id دارن و یه text و یه createdAd که میشه تاریخ ساخته شدن اون کامنت. اما من نمیخوام که همون مقدار تاریخی که تو json هست رو توش بریزم مثلا میخوایم اگه تاریخش مال ۲ روز قبله اون تاریخ تبدیل بشه به 2days ago در واقع میخوام تاریخ رو تبدیل کنم به یه رشته که بگه مال چند روز قبله اون تاریخ. واسه همین از fromJson استفاده میکنم : @JsonKey(fromJson: _calculateDateTime)
  final String createdAt;که همونجور که گفتم این fromJson یا یه متد سراسری میگیره یا یه متد static که من اوومدم یه متد static  به اسم _calculateDateTime نوشتم و این متد رو  دادم به fromJson. حالا وقتی کد واسه من generate بشه هر موقع که خواست json رو پارس کنه به صورت اتوماتیک واسه پارس کردن createdAt میاد این متد رو صدا میزنه و مقدار خروجی از اون متد رو میریزه تو اون فیلد کلاس. فقط نکته مهم اینجاس که خروجی اون متد باید جنسش مشابه با اون فیلد تو کلاس باشه. ینی اگه فیلد createdAt از نوع String هست خروجی اون متد هم باید از نوع String باشه. و ورودی این متد هم باید جنسش دقیقا همون جنسی باشه که تو json برگشت داده میشه ینی اگه createdAt توی فایل json از نوع String هست ورودی متد هم باید String باشه.پس خروجیه متد هم جنس با نوع فیلد توی کلاس و ورودیه متد هم هم جنس با نوع اون داده توی json.در آخر اضافه کنم که این fromJson که اینجا هست هیچ ربطی به اون متد سازنده fromJson نداره. در واقع وقتی توی JsonKey از fromJson استفاده میکنیم که باید یه فانکشن حتما بهش پاس بدیم، این میاد میگه که اون فیلد از کلاسمون هر موقع خواست دیتا بگیره، بیا این متد رو براش صدا بزن و مقدار اون داده متناظر توی json رو بده به ورودیه این تابع تا اونو پردازش کنه و در نهایت اون تابع یه خروجی بده که اون خروجی در نهایت میره میشینه تو اون فیلد کلاسمون. پس به جای اینکه مستقیم دیتای json بره تو فیلد کلاسمون از یه فیلتر که یه تابع هست رد میشه و خروجیه اون تابع میره میشینه تو اون فیلد. خب اینا ویژگی های مهم توی JsonKey بودن اما بریم سراغ ویژگی های مهم توی JsonSerializable :createToJson : اگه بگیم  createToJson : false این به پکیج میگه که واسه من متد toJson رو نساز فقط همون fromJson رو بساز اگه هیچی هم نگیم به صورت دیفالت toJson رو هم میسازه واسمون.@JsonSerializable(createToJson: false)genericArgumentFactories : این واسه کسایی که حرفه ای ترن به درد میخوره. در واقع میتونید از generic ها استفاده کنید. یه مثال میزنم(لطفا کسایی که با مبحث generic ها آشنایی دارن بخونن) :part &#039;pagination_data_entity.g.dart&#039;;

@JsonSerializable(genericArgumentFactories: true, createToJson: false)
class PaginationDataEntity &lt;T&gt;{
  final List&lt;T&gt; items;
  final MetaPaginationEntity meta;

  PaginationDataEntity({
    this.items,
    this.meta,
  });

  factory PaginationDataEntity.fromJson(Map&lt;String, dynamic&gt; json,T Function(Object json) fromJsonT,) =&gt; _$PaginationDataEntityFromJson(json,fromJsonT);
}من توی این کلاس میخوام لیستم generic باشه واسه همین باید genericArgumentFactories رو برابر با true کنم و از T هم استفاده کنم. فقط تنها فرقش اینه که توی متد fromJson الان دوتا ورودی میگیریم به جای یه ورودی. قبلا فقط json رو میگرفتیم الان یه فانکشن هم باید بهش پاس بدیم. و اما نحوه استفادش اینجوری میشه :PaginationDataEntity&lt;Test&gt;.fromJson(
  json as Map&lt;String, dynamic&gt;,
  (data) =&gt; Test.fromJson(data as Map&lt;String, dynamic&gt;),
),الان T من شده کلاس Test و توی متد سازنده fromJson کلاس PaginationDataEntity علاوه بر json یک فانکشن هم بهش دادیم که توش باید از fromJson اون کلاس T مون که الان کلاس Test هست استفاده کنیم.اینم مهم ترین ویژگی های JsonSerializable حالا میریم یه مثال سخت باش حل میکنیم.این json ما :{
    &amp;quotperson&amp;quot: {
        &amp;quotname&amp;quot: &amp;quotAli&amp;quot,
        &amp;quotlastName&amp;quot: &amp;quotHoseinpoor&amp;quot,
        &amp;quotbirth&amp;quot: {
            &amp;quotyear&amp;quot: 1996,
            &amp;quotmonth&amp;quot: &amp;quotJan&amp;quot,
            &amp;quotday&amp;quot: 26
        },
        &amp;quotfamily&amp;quot: [
            &amp;quothasan&amp;quot,
            &amp;quotfarshad&amp;quot,
            &amp;quotrozhin&amp;quot
        ]
    },
    &amp;quotcars&amp;quot: [
        {
            &amp;quotname&amp;quot: &amp;quotLamborghini&amp;quot,
            &amp;quotspeed&amp;quot: 350,
            &amp;quotmojod&amp;quot: false
        },
        {
            &amp;quotname&amp;quot: &amp;quotBenz&amp;quot,
            &amp;quotspeed&amp;quot: 300,
            &amp;quotmojod&amp;quot: true
        }
    ]
}خب من اول یه فایل به اسم my_data_entity.dart میسازم و کدهامو تو اون فایل میزنم(شما میتونید هر کلاس رو توی فایل های مجزا بزنید.)خب میریم سراغ درونی ترین آبجکت تا کلاسش رو بسازیم. ینی آبجکت birth.(دقت کنید همه کدها درون فایل my_data_entity.dart هستن)import &#039;package:freezed_annotation/freezed_annotation.dart&#039;;

part &#039;my_data_entity.g.dart&#039;;

@JsonSerializable(createToJson: false)
class Birth {
  final int year;
  final String month;
  final int day;

  Birth({
    this.year,
    this.month,
    this.day,
  });

  factory Birth.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$BirthFromJson(json);
}همونجور که دیدید اولش از part استفاده کردم تا واسه من کدی رو که توی فایل my_data_entity.g.dart درست میشه رو بخشی از این فایل my_data_entity.dart بکنه و بعدش هم به صورت عادی کلاسم رو نوشتم ومتد fromJson رو جوری که خود پکیج گفته نوشتم. حالا بریم سراغ کلاس Person :@JsonSerializable(createToJson: false)
class Person {
  final String name;
  final String lastName;
  final Birth birth;
  final List&lt;String&gt; family;

  Person({
    this.name,
    this.lastName,
    this.birth,
    this.family,
  });

  factory Person.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$PersonFromJson(json);
}این هم کلاس Person که دقیقا نحوه ساختش مثه همون Birth هست و حواستون باشه توی کلاس Person ما یه فیلد از کلاس Birth رو داریم دقیقا مثه کدای قبل. چون توی فایل Json در آبجکت person ما آبجکت birth رو داریم پس توی کلاس هاشون هم این اتفاق میوفته ینی توی کلاس Person باید کلاس Birth رو داشته باشیم. حالا میریم سراغ کلاس Car:@JsonSerializable(createToJson: false)
class Car {
  final String name;
  final int speed;
  final bool mojood;

  Car({
    this.name,
    this.speed,
    this.mojood,
  });

  factory Car.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$CarFromJson(json);
}این هم کلاس Car که دقیقا مثه بقیه ساخته میشه. (فقط حواستون باشه که اسم کلاس توی متد fromJson رو عوض کنید)خب حالا باید بریم سراغ آبجکت اصلیمون که شامل  آبجکت Person و لیستی از آبجکت Car هاست. و باید این آبجکت اصلی رو به کلاس تبدیل کنیم پس داریم :@JsonSerializable(createToJson: false)
class MyData {
  final Person person;
  final List&lt;Car&gt; cars;

  MyData({
    this.person,
    this.cars,
  });

  factory MyData.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$MyDataFromJson(json);
}این هم کلاس اصلیمون به همین راحتی. حالا فقط کافیه اون دستور رو اجرا کنیم و صبر کنیم تا واسمون کد رو generate کنه.بعدش که کد با موفقیت generate شد میتونید برید و کد رو خودتون نگا کنید و ببینید که اگه خودتون میخواستین اون کدرو بزنید چقد زیاد و وقت گیر میشد. اینجوری فقط نیازه شما بدنه اصلی کلاس رو بنویسید و اون متد fromJsonرو جوری که خود پکیج گفته(و ما هم اینجا دقیقا همونجوری نوشتیم) رو در کلاستون بنویسید و همچنین part رو هم فراموش نکنید.خب دوستان اینم از آموزش پارس کردن json هم به صورت دستی هم با استفاده از پکیج.امیدوارم که آموزش مفیدی بوده باشه براتون و تونسته باشم چیزی هر چند اندک به اندوخته هاتون اضافه کنم.تا آموزشای بعدی خدانگهدار❤️❤️. لینک کانال یوتوب من لطفا سر بزنید : https://www.youtube.com/c/FlutterStan</description>
                <category>Ali Hoseinpoor</category>
                <author>Ali Hoseinpoor</author>
                <pubDate>Mon, 29 Mar 2021 16:26:43 +0430</pubDate>
            </item>
                    <item>
                <title>‌از سیر تا پیازِ BLoc</title>
                <link>https://virgool.io/flutter-community/%E2%80%8C%D8%A7%D8%B2-%D8%B3%DB%8C%D8%B1-%D8%AA%D8%A7-%D9%BE%DB%8C%D8%A7%D8%B2%D9%90-BLoc-vcprqxvboqh7</link>
                <description>سلام دوستان. امیدوارم که حالتون خوب باشه. من علی حسین پور هستم و امروز میخوام با همدیگه Bloc رو یاد بگیریم.اول از همه چیز بگم که از هم اکنون میتوانید ویدیوهای مربوط به آموزش برنامه نویسی مخصوصا فلاتر و دارت رو در کانال یوتوب من دنبال کنید. لینکشم میزارم همین پایین :https://www.youtube.com/c/FlutterStanتوی این مقاله میخوام هر چیزی که نیازه که درباره Bloc بدونین رو بهتون بگم. پس همراه من بیاید.???واسه اینکه به درک خوبی از Bloc برسیم چندتا چیز رو باید از قبلش بلد باشیم به همین خاطر من این مقاله رو به ۳ تا بخش مهم و اصلی تقسیم کردم. بخش اول معرفی اجمالی Stream هاست.بخش دوم معرفی خود الگوی BLoC هست.بخش سوم و نهایی معرفی و نحوه استفاده از پکیج Bloc هست.(flutter_bloc).تو هر بخش یه سری مثال و نمونه کد نشونتون میدم و سعی میکنم خیلی باحوصله و یواش جلو برم تا بتونم این Bloc که خیلیا یه غول ازش ساختن رو براتون آسونش کنم. ??بخش اول معرفی اجمالی Stream :با یه مثال ساده واسه توضیح Stream شروع میکنم. شاید خیلی هاتون نوارهای نقاله توی کارخونه ها رو دیده باشین که روش محصولات رو قرار میدن و اون محصول روی اون نوار حرکت میکنه تا یه جایی یه نفر دیگه اون رو از رو نوار برداره.نوار نقالهخب حالا ممکنه بگید این چه ربطی به Stream ها داره ؟؟ ????ببینید نوار نقاله همیشه در حال حرکته و اینجور کار میکنه که یه نفر ابتدای اون نوار جسمی رو روش قرار میده و چون این نوار در حال حرکته این جسم روی این نوار حرکت میکنه تا یه جایی که یه نفر دیگه به این نوار داره نگاه میکنه و هر وقت جسمی اوومد سمتش اون رو از رو نوار برمیداره.این عمل دقیقا توی Stream ها هم اتفاق میوفته. Stream به معنی یک جریان هست و دقیقا مثله این نوار نقاله هست که ما میتونیم توی این جریان داده ای رو قرار بدیم و جای دیگه به این جریان گوش کنیم و هر وقت داده ای اوومد خبر دار شیم و از اون داده استفاده کنیم.حالا که با مفهوم کلیه Stream آشنا شدیم چجور میتونیم توی کدمون ازش استفاده کنیم ؟؟??من یه سوال مطرح میکنم و سعی میکنیم با هم دیگه حلش کنیم.سوال اینه که فرض کنید من میخوام توی یک متد یه حلقه داشته باشم که از ۱ تا ۱۰ میشماره و هر عدد رو بعد از ۲ ثانیه از توی متد به بیرون میفرسته بدون اینکه اجرای متد متوقف شه (return).خب نکته اول اینه که چون میخوام یه جای کد ۲ ثانیه منتظر وایسم و هیچ کاری نکنم نیازه که از Future ها استفاده کنم و اگه کمی با زبان دارت‌ آشنا باشید میدونید که واسه استفاده از Future ها نیازه که متد ما async بشه حالا async چی هست ؟؟در مدل برنامه نویسی sync(همگام) در هر لحظه از زمان فقط یه عملیات میتونه انجام بشه. ینی اجرای کدها از خط اول شروع میشه و به ترتیب و یک ‌به ‌یک خط‌های برنامه اجرا میشه . اگر توی این حین، برنامه به یک متد برسه اجرای برنامه متوقف میشه تا اون متد اجرا بشه، اجرای اون متد از خط اول شروع میشه و تا زمانی که خط‌به‌خط اون متد اجرا نشه و نتیجه رو برنگردونه هیچ بخش دیگه‌ای از برنامه اجرا نمیشه. مثلا فرض کنید قراره ما یه دیتایی از سمت سرور بگیریم که چند ثانیه هم گرفتن اون داده و parse کردنش طول میکشه حالا اگه این کار به صورت کامل sync انجام بشه اون زمانی که برنامه داره دیتا رو از سرور میگیره چون کار زمان بری هست باعث میشه برنامه اونجا وایسه تا جواب از سمت سرور برگرده که اینجوری باعث میشه ui برنامه قفل شه و هیچ کاری نتونیم بکنیم. اما حالا فرض کنید زمانی که دارین دیتا رو از سمت سرور میگیرین میخواین اون زمانی که منتظر جواب هستین به کاربر یه loading نمایش بدین این کار با روش async قابل انجامه.در مدل برنامه نویسی async(ناهمگام) در هر لحظه از زمان میتونه چندین عملیات انجام بشه.فرض کنید شما یک تابع نوشتین... در حین اینکه اون تابع داره اجرا میشه بخش‌های دیگه‌ای از برنامه هم در حال اجرا شدن هستن، بعد از این که تابع اجرا شد و نتیجه رو برگردوند از اون نتیجه، توی بخش‌های دیگه‌ی برنامه در صورت لزوم استفاده میشه. الان یه مثال ساده میزنم تا بهتر متوجه بشین.فرض کنین شما میرین رستوران و مثلا پیتزا سفارش میدین =)) ... در حین اینکه پیتزای شما داره آماده میشه گارسون میره سر میز‌های دیگه و سفارش بقیه‌ی افراد رو میگیره. به این نوع عملکرد میگن async .حالا فرض کنین گارسون سفارش شما رو میگیره و تا وقتی که پیتزایی که شما سفارش دادین رو به دستتون نرسونه سراغ بقیه ی مشتری‌ها نمیره، این میشه sync.به زبون برنامه نویسی خودمون پیتزا رو ریکوئست در نظر بگیرین، آشپزخونه‌ی رستوران رو سرور و مشتری‌هارو کلاینت. گارسون هم میشه راه ارتباطی بین کلاینت و سرور (API). حالا شما فرض کنین کد ما sync باشه... واقعا چی میشه ؟فکر کنم با این مثال اهمیت برنامه‌نویسی async دیگه مشخص شد!خب حالا برگردیم به سوال اصلیه خودمون. من اول یه کد مینویسم و اون رو با هم تحلیل میکنیم :Future&lt;int&gt; getNumberFromMethod () async {
  for (int i = 0 ; i &lt; 10 ; i++){
    await Future.delayed(const Duration(seconds: 2));
    return i;
  }
}خب توی کد بالا متد رو async کردم اول و اوومدم توی بدنه یه حلقه گذاشتم که ۱۰ بار بچرخه و بعد اوومدم ۲ ثانیه هم صبر کردم و اون مقدار رو return کردم به نظرتون این کد درسته ؟؟ ???قطعا نه چون وقتی به return i میرسه عدد ۱ رو به بیرون میفرسته ولی دیگه اون متد هم متوقف میشه چون ما return کردیم. ??? پس چکار کنم که این درست شه و بدون توقف متد هر ۱۰ تا عدد رو برگردونه ؟بله نیاز به Stream داریم تا بیاد یه جریان واسه ما درست کنه و این ۱۰ تا عدد رو واسه ما توی این جریان بفرسته.??خب همچنان متد ما باید async باشه ولی به جای اینکه Future برگردونه باید Stream برگردونه و واسه اینکه ما بتونیم از قابلیتی استفاده کنیم که بدون توقف متد یه داده رو به ما برگردونه به جای کلمه کلیدی async باید از async* استفاده کنیم. async* دقیقا همین کارو میکنه که توی زمان اجرا میگه این متد بعد از برگشت دادن یه داده هنوز ادامه داره و متوقف نمیشه. حالا با این اطلاعاتی که گفتم یه کد دیگه مینویسم.??Stream&lt;int&gt; getNumberFromMethod() async* {
  for (int i = 0; i &lt; 10; i++) {
    await Future.delayed(const Duration(seconds: 2));
    yield i;
  }
}این کد الان کاری که دقیقا میخوایم رو انجام میده. کلمه کلیدی yield دقیقا میگه که این مقدار i رو برگردون ولی حواست باشه که اجرای متد متوقف نمیشه و ادامه داره.خب ما الان یه کدی نوشتیم که یه جریانی از int ها واسه ما باز میکنه و ما داده هامون رو توش میفرستیم ولی چجور بهش گوش بدیم ؟؟هر Stream یه متد داره به اسم listen() که ما میتونیم روی اون جریان داده شنود کنیم و هر وقت داده ای به دست ما رسید متوجه بشیم.getNumberFromMethod().listen(
  (event) {
    print(event);
  },
);متد getNumberFromMethod چون یه Stream به ما برمیگردونه پس میتونیم از متد listen() روش استفاده کنیم و داده ای که توی این جریان به دست ما میرسه رو بفهمیم چیه. خب تا الان با Stream و متد listern() توی استریم ها آشنا شدیم یه دوتا چیز دیگه مونده که اونا هم الان با هم یاد میگیریم .اولین مورد StreamController هست. شما فرض کنید میخواید یه استریم داشته باشید که هر وقت که خواستید بتونید یه داده ای رو روی اون استریم بفرستید. توی این مورد میتونید از StreamController استفاده کنید این StreamController به شما یه استریم برمیگردونه و همچنین یه سری متد که میتونید با استفاده از اون متد ها به اون استریم داده اضافه کنید یا شنود کنید و در کل کنترل داشته باشید روی اون استریم. با یه سری کد این StreamController رو توضیح میدم.StreamController&lt;int&gt; streamController = StreamController();این نحوه ساخت یک StreamController هست. نکته خیلی خیلی مهمی که وجود داره وقتی شما یک StreamController میسازید حتما باید یه جایی اون استریم رو ببندید(close) چون اگه این کار رو نکنید توی برنامتون memory leak خواهید داشت. بهترین جا واسه بستن این استریم ها توی متد dispose ویجت کلاس هاست.(Widget class)@override
void dispose() {
  streamController.close();
  super.dispose();
}خب ما الان میخوایم روی این استریم یه داده ای رو بفرستیم این کار به ۲ صورت میتونه انجام بشه :روش اول : استفاده از متد add به صورت مستقیم روش دوم : استفاده از sink و صدا زدن متد add روی sinkبین این دو روش هیچ تفاوتی وجود نداره. اول کدهاشونو میبینیم بعد تحلیلشون میکنیم.streamController.sink.add(4);
streamController.add(7);هر دوی این ها یه داده روی استریم قرار میدن بدون هیچ تفاوتی اما یه نکته وجود داره.فرض کنید شما یه کلاس دارید که یه متد توی این کلاس هست که فقط و فقط وظیفه داره روی استریم یه داده بزاره ینی قرار نیست بتونه کار دیگه ای با اون استریم انجام بده (مثلا اون رو شنود کنه و .... فقط میتونه داده بزاره).اگه من خود StreamController رو پاس بدم به اون کلاس اون وقت اون متد میتونه کنترل کامل روی اون استریم داشته باشه در صورتی که ما میخوایم دسترسی اون متد محدود باشه به گذاشتن داده، واسه همین StreamController یه قابلیتی داره به اسم sink که این در واقع یه ورودی واسه استریم هست ینی ما با sink فقط میتونیم داده روی استریم قرار بدیم همین و بس واسه همین کافیه به جای فرستادن کل StreamController به اون کلاس StreamController.sink رو به اون کلاس بفرستیم. اینجوری اون متد توی کلاس فقط میتونه روی استریم داده بزاره چون ما کل StreamController رو واسش نفرستادیم فقط sink اون StreamController رو واسش فرستادیم.پس هیچ تفاوتی بین عملکرد اون دو خط کد نیس فقط ما با استفاده از sink میتونیم دسترسی رو به استریم محدود کنیم و فقط بتونیم داده بزاریم روی استریم(sink در واقع یه جور input میشه واسه استریم).واسه شنود کردن StreamController هم کافیه به Stream اون دسترسی داشته باشیم و روش listen کنیم :streamController.stream.listen(
  (event) {
    print(event);
  },
);پس به صورت کلی دوتا ویژگی خیلی مهم وجود داره توی Stream ها یکی sink که در واقع ما با استفاده از اون میتونیم داده روی Stream قرار بدیم و یکی stream که ما میتونیم روی اون شنود کنیم (listen) و داده خروجی از استریم رو بفهمیم.خب بحث Stream ها همین جا تموم شد سعی کردم یه معرفی کلی و خلاصه دربارشون داشته باشم. استریم ها واقعا خودشون یه دنیان چون خیلی خیلی متد و ویژگی خاص دارن ولی از حوصله این مقاله خارجه واسه همین اگه میخواین توی بحث استریم ها پیشرفته تر بشین حتما دربارش تحقیق کنید.بخش دوم معرفی خود الگوی BLoC :کلمه BLoC مخفف Business Logic Components است. نکته اصلی درباره BLoC اینه که همه چیز در برنامه باید به عنوان جریانی(stream) از وقایع(event) نشان داده بشه: ویجت ها وقایع ها(events) را ارسال می کنند. سایر ویجت ها نسبت به اون وقایع پاسخ خواهند داد. کلا Bloc بر پایه Stream هاست واسه همین در بخش اول من Stream ها رو توضیح دادم.خب اول کار مهمه که با دوتا کلمه جدید آشنا بشیم که خیلی خیلی مهمن: Events و States.با چندتا مثال این دوتا کلمه رو کامل واستون توضیح میدم.مثال اول ) فرض کنید یه برنامه دارم که وسط صفحه یه عدد نمایش میده و من میتونم اون عدد رو زیاد یا کم بکنم. پس این برنامه دوتا قابلیت داره یکی افزایش عدد و یکی کم کردن عدد. همونجور که میدونید معنی event ینی رخداد یا واقعه، الان توی این اپ دوتا رخداد میتونه اتفاق بیوفته(افزایش و کاهش عدد) پس این دوتا اتفاق میشه event های ما. پس event شد یه سری وقایع و رخداد که توی اپ ما میتونه به وجود بیاد مثله همین افزایش و کاهش عدد.خب حالا وقتی یکی از این event ها اتفاق افتاد ما انتظار داریم چه چیزی رو شاهدش باشیم ؟ مثلا اگه event افزایش عدد رخ داد ما باید شاهد چه چیزی باشیم ؟ درسته ما باید ببینیم که عدد روی صفحه یکی زیاد میشه. پس وقتی یه event ای اجرا میشه برنامه ما وارد یه حالت جدید میشه مثلا اگه من دارم عدد ۰ رو وسط صفحه میبینم در همین حین اگه event افزایش عدد اتفاق بیوفته برنامه وارد یک حالت جدید میشه که اون حالت نمایش عدد ۱ روی صفحه است. به این حالت میگن state.به صورت کلی برنامه ما همیشه در یک حالت یا وضعیتی قرار داره ما نمیتونیم بگیم برنامه ما الان هیچ وضعیتی نداره در واقع همین که داره عدد ۰ رو روی صفحه نمایش میده این هم یک وضعیته واسه خودش حالا اتفاق افتادن event ها باعث میشه برنامه ما وارد یک حالت و وضعیت جدید بشه.پس اتفاق افتادن event ها باعث تغییر state برنامه میشه.حالا state توی این مثال ما چی میشه ؟ چون event های ما باعث میشه که اون عدد وسط صفحه تغییر کنه پس state ما میشه اون عدد. که توی حالت اولیه(initial) برنامه اون عدد ۰ است و با اتفاق افتادن هر کدوم از اون event ها اون عدد تغییر میکنه و برنامه ما باید اون عدد جدید(حالت جدید) رو نمایش بده.مثال دوم ) فرض کنید یه برنامه تشخیص آب و هوای ساده دارم که یک اسم شهر از من میگیره و آب و هوای اون شهر رو نشون میده. خب وقایعی که توی این اپ میتونه رخ بده اینه که من اسم شهر رو وارد کنم و بعد روی دکمه جستجو کلیک کنم تا آب و هوای اون شهر رو نشونم بده. پس event ما میشه فشار دادن دکمه جستجو که همراه این event اسم شهر وارد شده هم میفرستیم.حالا برنامه ما وقتی روی دکمه جستجو کلیک میکنیم چه حالت هایی میتونه داشته باشه ؟؟۱) همیشه برنامه ما باید یه حالت ابتدایی (initial) داشته باشه ینی این حالت ابتدایی همیشه هست برای مثال قبل هم حالت ابتدایی نمایش عدد ۰ روی صفحه بود و خب منطقی هم هست چون برنامه ما از یه حالت باید شروع به کار کنه نمیشه ما حالت ابتدایی برای چیزی نداشته باشیم تو این برنامه هم حالت ابتداییه ما میشه اینکه مثلا به کاربر یه TextField نمایش بدیم تا اسم شهرش رو وارد کنه(حالت ابتدایی کاملا بستگی به خودتون داره که دوست دارین چی باشه اینجا من یه مثال ساده زدم)۲) چون فرآیند گرفتن داده آب و هوا ممکنه زمان بر باشه پس ما توی این مدت میخوایم یه loading به کاربر نمایش بدیم پس یکی از حالت های ما میشه نمایش loading زمان گرفتن دیتا۳) حالت بعدی این میتونه باشه که کاربر هیچ اسم شهری وارد نکرده باشه و روی دکمه جستجو کلیک کرده باشه که تو این حالت ما میخوایم به کاربر بگیم که اسم شهر نباید خالی باشه پس اینم یه حالت از برنامه میتونه باشه بعد از زدن دکمه جستجو.۴) حالت بعدی اینه که داده آب و هوا برای اون شهر به صورت کامل گرفته شده و حالا میخوایم اون داده ها رو به کاربر نمایش بدیم۵) و حالت نهایی هم این میتونه باشه که زمان گرفتن داده ها به یه مشکلی بخوره و نتونه داده ها رو بگیره تو این حالت ما میخوایم به کاربر یه پیغام خطا نمایش بدیمپس این مثال ما یک event داشت و ۵ تا stateمثال سوم ) فرض کنید میخوایم صفحه سبد خرید رو شبیه سازی کنیم. ما میتونیم به این صفحه آیتم اضافه کنیم و همچنین میتونیم آیتمی از سبد خریدمون پاک کنیم.پس وقایع ما (events) به صورت زیر میتونه باشه :۱) اضافه کردن آیتم به سبد خرید۲) حذف کردن آیتم از سبد خریداما حالت های(states) برنامه ما چی هست ؟ما اول به یک حالت اولیه نیاز داریم.واسه سادگی فرض میکنیم که حالت اولیه ما یه لیست خالی از آیتم های سبد خرید است.وقتی روی اضافه کردن آیتم به سبد خرید کلیک میکنیم انتظار داریم که توی صفحه سبد خرید اون آیتم رو ببینیم پس ما همیشه نیاز داریم یک لیست از آیتم ها رو به عنوان حالت (state) برنامه برگردونیم.دیدیم که توی حالت اولیه ما یه لیست خالی برگردوندیم. توی event اضافه کردن آیتم به سبد خرید میتونیم دوباره یه لیست برگردونیم که اون آیتم توی اون لیست باشه. و همینجور اگه مثلا ما ۵ تا آیتم رو به سبد خرید اضافه کنیم یه لیست خواهیم داشت که ۵ تا آیتم توشه و این رو به عنوان حالت برنامه سبد خرید برمیگردونیم.اگه event حذف آیتم رو زد ما باید اون آیتم رو از لیست فعلیمون پاک کنیم و لیست جدید رو به عنوان حالت برنامه برگردونیم.پس این مثال هم ۲ تا event داشت که اضافه کردن و حذف کردن آیتم ها از سبد خرید بود و یک state داشت که اون هم یه لیستی از آیتم ها بود که در ابتدا خالی بود و با اتفاق افتادن هر event اون لیست تغییر میکرد.خب سعی کردم توی ۳ تا مثال جدا event و state رو کامل بهتون بگم. حالا قبل از اینکه بریم یکم توی کد، میخوام یکم دیگه از مزیت های Bloc رو بهتون بگم.الگوی Bloc علاوه بر اینکه یک استیت منیجر هست و ما میتونیم باهاش حالت های برناممون رو کنترل کنیم میتونیم ازش به عنوان یک الگوی معماری هم استفاده کنیم به این صورت که ما میتونیم کد های مربوط به بخش ظاهر رو از کدهای مربوط به بخش پردازش اطلاعات و گرفتن داده از سرور یا دیتابیس محلی یا ... جدا کنیم.توی شکل بالا میتونیم به صورت کامل ببینیم نحوه جدا سازی بخش ظاهر از بخش دیتا رو.بلاک در وسط برنامه قرار میگیره و ظاهر برنامه ما یا همون ویجت ها با event هایی که وجود داره با بلاک ارتباط برقرار میکنن. مثلا دکمه گرفتن آب و هوا میگه : &quot;هی بلاک میخوام آب و هوای این شهری که کاربر وارد کرده رو بم بگی. بیا اسم شهر رو بگیر و دیتاش رو بم بده&quot;خب تو این حالت ما میاییم یه سری کلاس درست میکنیم واسه برقراری ارتباط با سرور یا دیتابیس محلی یا ... کلا کارهایی که مربوط به پردازش داده و ... میشه و اثری از ظاهر توشون نیس. حالا بلاک که تو قسمت قبل اسم شهر رو از ui گرفته میاد یه سری متد از اون کلاس هایی که نوشتیم صدا میزنه و میگه شما برین آب و هوای این شهر رو واسه من پیدا کنید. تا زمانی که شما چیزی پیدا نکردین من یه حالت loading فعلا به ui برمیگردونم هر وقت شما دیتایی پیدا کردید به من بگید تا من هم اون حالت جدید رو به ui بدم.اینجوری میشه که کدهای بخش ظاهر ما از کدهای دیگمون جدا میشه و بلاک وسط این دوتا قرار میگیره تا ارتباط اون ها رو با یک سری event و state با هم برقرار کنه.مزیت دیگه Bloc اینه که به راحتی میتونه توی معماری های دیگه هم استفاده بشه مثلا میتونیم توی معماری های Clean Architecture یا MVP یا MVVM و ... ازش استفاده کنیم مثلا توی معماری Clean توی لایه application میتونیم از Bloc استفاده کنیم یا توی MVVM میتونیم توی لایه ModelView از Bloc استفاده کنیم.خب حالا میخوایم یکم کد بزنیم. میخوام اون مثال اول که یه عدد رو زیاد یا کم میکرد رو با Bloc بزنیم.(بدون استفاده از پکیج).در قدم اول بیاین event ها رو بنویسیم. من چون دوتا event خیلی ساده بود از enum استفاده کردم ولی شما از کلاس هم میتونین استفاده کنید(من هر دو روش رو مینویسم ولی توی کد از enum استفاده میکنم)روش اول با استفاده از enum :enum CounterEvent {
  increment,
  decrement,
}یه enum ساختم که دوتا حالت داره یا افزایش یا کاهش.روش دوم با استفاده از کلاس ها :abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}یه کلاس پایه داریم به اسم CounterEvent و دوتا کلاس دیگه داریم که این دوتا کلاس باید زیرمجموعه CounterEvent باشن. چون همه event های ما آخرش باید از یه نوع باشن. الان چون IncrementEvent از CounterEvent ارث بری کرده در نهایت این کلاس نوعی از کلاس CounterEvent حساب میشه. پس به این سبک event هامون رو مینویسیم و باید دقت کنید که همگی از یه نوع باشن در نهایت.خب الان میریم سراغ نوشتن کلاس Bloc. همون طور که اولش هم گفتم Bloc بر پایه Stream است ینی event ها و state اون باید به صورت stream باشه. توی این کلاس ما نیاز به یه فیلد counter داریم که از جنس int باشه چون قراره این state برنامه ما باشه و اون عدد رو برگردونیم. حالا میریم سراغ کد این کلاس:class CounterBloc {
  int _counter = 0;

  final StreamController&lt;int&gt; _counterStateController = StreamController&lt;int&gt;();
  StreamSink&lt;int&gt; get _inCounter =&gt; _counterStateController.sink;
  Stream&lt;int&gt; get counter =&gt; _counterStateController.stream;

  final _counterEventController = StreamController&lt;CounterEvent&gt;();
  Sink&lt;CounterEvent&gt; get counterEventSink =&gt; _counterEventController.sink;
}خب این کلاس ما یه فیلد داره به اسم counter که اولش هم ۰ گذاشتیمش. بعد باید بیام دوتا StreamController درست کنم یکی واسه event ها یکی واسه state ها. StreamController هم که گفته بودم واسه این خوبه که ما هر وقت خواستیم میتونیم روی stream داده بفرستیم.⚠️⚠️ هشدار : لطفا متن زیر رو چند بار بخونید تا کامل متوجه بشید ⚠️⚠️قبل اینکه بقیه کد رو توضیح بدم یه نکته بگم که چرا اصن event ها و State ها باید Stream باشن ؟ ببینید همونجور که قبلا گفتم Stream یه جریانه که همیشه برقراره(تا زمانی که اون رو نبستین) ما میخوایم تو برناممون هر زمان که یه واقعه ای رخ داد سریع یه حالتی نسبت به اون واقعه ببینیم تو اپمون، و این نیازمنده اینه که event های ما به صورت جریانی از event ها باشه که ما توی کلاس Bloc بتونیم به این جریان از event گوش کنیم و همچنین نیاز به یه جریان از state ها داریم که هر وقت یه event گرفتیم مختص به اون event ما یه state ای توی اون جریان بزاریم، و سمت ui به اون جریان state ها گوش کنیم و مختص به هر کدوم ظاهر خاص خودمون رو داشته باشیم. پس ما نیاز داریم توی کلاس Bloc به جریانی از event ها گوش کنیم و مختص به هر event یه state به سمت ui بفرستیم و چون سمت ui هم نیاز داره تا به این state ها گوش کنه تا به محض عوض شدن state ظاهر هم تغییر بده واسه همین نیازه که state های ما هم به صورت stream باشن. حالا بریم سراغ ادامه کد.خب ما اول اوومدیم یه StreamController ساختیم به اسم counterStateController که این میشه استریمی از state های ما.بعد دوتا فیلد ساختیم که اولی از نوع StreamSink هست و دومی از نوع Stream. همونجور که قبلا گفتم sink واسه این هست که ما یه داده ای توی stream قرار بدیم. و ما چون میخوایم وقتی یه event ای اومد نسبت به اون event یه state توی جریان برگردونیم نیاز داریم که از sink استفاده کنیم.فیلد بعدی که یه stream هست از state ما، واسه اینه که ui به این stream گوش کنه و نسبت با اون state ظاهر خودش رو بسازه.حالا همینجور واسه event ها هم یک StreamController درست میکنیم. دقت کنید که ما فقط به sink نیاز داریم واسه event هامون چون قراره ما از طریق ویجت هامون  یه event رو توی جریان Stream قرار بدیم واسه همین فقط sink میخواد.حالا ادامه کد رو مینویسم :CounterBloc() {  
    _counterEventController.stream.listen(_mapEventToState); 
}در ادامه میام یه متد سازنده واسه کلاسم میسازم که توی این متد اومدم به اون StreamController ای که واسه event هام ساختم گوش میدم که هر وقت event ای از سمت ui وارد شد اینجا بفهمم. در نهایت توی متد listen من یه متد بهش پاس دادم به اسم mapEventToState که کد اون رو الان میزارم‌:void _mapEventToState(CounterEvent event) {
  if (event == CounterEvent.increment)
    _counter++;
  else
    _counter--;

  _inCounter.add(_counter);
}خب من اومدم روی StreamController ایونت هام شنود گذاشتم که هر وقت event ای وارد شد بفهمم و این متد mapEventToState رو صدا بزنم. کاری که این متد میکنه میاد event رو میگیره بعد چک میکنه اگه که event برابر با افزایش عدد بود یکی اون عدد رو زیاد میکنه و اگه کاهش بود یکی اون عدد رو کم میکنه و در نهایت با استفاده از متغیر inCounter که یه sink بود واسه state های ما میاد اون عدد رو توی جریان state ها قرار میده. حالا هر کی state ها رو شنود کنه این عدد که قرار گرفته تو جریان رو میفهمه.در نهایت یه متد باید بنویسیم که Stream ها رو واسه ما ببنده چون بستن اون ها خیلی خیلی مهم هست.void dispose() {
    _counterStateController.close();
    _counterEventController.close();
  }
}این متد هم کار بستن Stream ها رو میکنه.کد کامل کلاس CounterBlocخب الان ما event ها و state ها و Bloc رو نوشتیم وقتشه که رابطه ی این کلاس رو با ui برقرار کنیم.چون ما توی ui یه جریانی ازstate ها رو میگیریم میتونیم از ویجت StreamBuilder واسه گوش دادن به اون استریم state ها استفاده کنیم و هر وقت state عوض شد ui هم تغییر بدیم.ما اول توی کلاس ui نیاز داریم تا یک نمونه از کلاس CounterBloc بسازیم:class CounterPage extends StatefulWidget {
  @override
  _CounterPageState createState() =&gt; _CounterPageState();
}

class _CounterPageState extends State&lt;CounterPage&gt; {
  final _counterBloc = CounterBloc();  .......}حالا توی متد build میاییم از StreamBuilder استفاده میکنیم. این ویجت چندتا فیلد مهم داره.StreamBuilder&lt;int&gt;(
  initialData: 0,
  stream: _counterBloc.counter,
  builder: (BuildContext context, AsyncSnapshot&lt;int&gt; snapshot) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: &lt;Widget&gt;[
        Text(
          &#039;You have pushed the button this many times:&#039;,
        ),
        Text(
          &#039;${snapshot.data}&#039;,
          style: Theme.of(context).textTheme.display1,
        ),
      ],
    );
  },
)اولیش initialData هست که ما بهش گفتیم ۰ باشه ینی دیتا اولیه ما ۰ هست. فیلد بعدی stream هست که از ما میخواد یه چیزی بهش بدیم از نوع stream. چون ما میخوایم به جریان state های ما گوش بده باید یه stream از state هامون بهش بدیم که counterBloc.counter دقیقا همون stream ای هست که میخوایم.(برید توی کلاس CounterBloc نگاه کنید که counter در واقع یه متد getter هست از استریمی از state هامون). فیلد مهم بعدی builder هست که یه متد باید باشه که دوتا پارامتر داره یکی context و یکی دیگه snapshot و باید توی این متد یک ویجت حتما برگردونیم. این snapshot در واقع دیتای برگشتی از همون stream هست اگه دیتایی برگرده از stream این snapshot دارای مقدار میشه ولی اگه چیزی برنگرده این snapshot مقدار نداره. توی متد builder ما یه سری ویجت برگردوندیم که مهم ترین اونها Text آخری هست که اگه نگاه به دیتای اون Text بکنید نوشته شده snapshot.data این ینی مقداری که از stream برمیگرده رو بزار جای مقدار Text و چون ما یه stream ای از اعداد برمیگردوندیم در واقع دیتای برگشتیه ما از stream میشه یه سری عدد که ما توی Text به کاربر نمایشش میدیم.خب کار نمایش state هامون رو هم درست کردیم با استفاده از StreamBuilder حالا وقتشه که یه سری دکمه بزاریم که وقتی روشون کلیک شد یه event واسه ما صدا بزنه.Row(
  mainAxisAlignment: MainAxisAlignment.end,
  children: &lt;Widget&gt;[
    FloatingActionButton(
      onPressed: () =&gt; _counterBloc.counterEventSink.add(CounterEvent.increment),
      tooltip: &#039;Increment&#039;,
      child: Icon(Icons.add),
    ),
    SizedBox(width: 10),
    FloatingActionButton(
      onPressed: () =&gt; _counterBloc.counterEventSink.add(CounterEvent.decrement),
      tooltip: &#039;Decrement&#039;,
      child: Icon(Icons.remove),
    ),
  ],
)خب اینجا دوتا دکمه گزاشتم که واسه ما event هامون رو صدا میزنه توی onPressed اولی گفتم که evetn افزایش عدد رو صدا بزنه. اگه یادتون باشه توی کلاس CounterBloc من یه StreamController از event هام ساختم و بعدش یه متد getter ساختم به اسم counterEventSink که وظیفش این بود توی جریان event ها داده اضافه کنه. من هم اینجا از اون متد getter استفاده کردم و گفتم که CounterEvent.increment رو به جریان event هام اضافه کنه. و همین کارم برای دکمه دوم کردم ولی اونجا CounterEvent.decrement رو اضافه کردم به جریان event هام.توضیح کلی : ما نیاز داریم که از طریق ظاهر برنامه یه سری event بفرستیم به کلاس Bloc و توی اون کلاس به اون جریان event ها گوش کنیم تا event ای که وارد شد، نوع اون event رو چک کنیم که چی هست بعد عملیات پردازش داده رو انجام بدیم و در نهایت یه state رو توی جریان state ها قرار بدیم تا ظاهر برنامه ما که داره به اون جریان از state ها گوش میده متوجه بشه که state برنامه تغییر کرده و ظاهر رو تغییر بده. به همین علت ما توی کلاس CounterBloc دوتا StreamController ساختیم یکی واسه event ها و یکی واسه state ها که واسه state ها هم stream درست کردیم که ظاهر برنامه بتونه بهش گوش بده و هم sink واسش درست کردیم که توی خود کلاس CounterBloc وقتی یه event ای اضافه میشه مختص به اون event یه state رو درون جریان state ها قرار بدیم و واسه StreamController ایونت هامون فقط اوومدیم sink درست کردیم که ظاهر برنامه بتونه با استفاده از اون یه event به جریان event ها اضافه کنه و توی متد سازنده کلاس هم به خود جریان event ها گوش دادیم تا هر وقت هر event ای از طرف ظاهر برنامه اضافه میشه رو بفهمیم و state مختص به اون رو برگردونیم.اینم از خود الگوی Bloc توی بخش بعدی میریم سراغ استفاده از پکیج Bloc.بخش سوم معرفی و نحوه استفاده از پکیج Bloc :پکییج flutter_bloc در واقع اوومده یه سری از کار ها رو واسه ما ساده کرده. ما هنوز نیاز داریم که event ها و state هامون رو درست کنیم ولی این پکیج اوومده کار با خود Bloc رو ساده کرده دیگه نیاز نیس ما StreamController ها رو خودمون از پایه بنویسیم و یا خودمون به event ها گوش بدیم همه این کار ها رو این پکیج واسه ما ساده تر کرده.بزارید من قبل از اینکه توضیح بدم این قسمت رو، یه دید کلی از چیزایی که قراراه گفته بشه تو این بخش رو بهتون بگم : اول درباره Cubit صحبت میکنم، بعد میام ویجتای مهمه این پکیج رو بهتون میگم، بعدش یه مثال با هم میزنیم از این پکیج و بعدش تغییرات مهمی که توی نسخه های آخر کرده رو بهتون میگم و در نهایت یه سری نکات خیلی مهم رو بهتون آموزش میدم.اولین چیزی که قراره صحبت کنم راجع بهش Cubit هست.اگه اشتباه نکنم توی نسخه ۵.۰.۰ این پکیج Cubit معرفی شد. Cubit در واقع یه نسخه ساده تر و سبک تر از Bloc هستش و تنها تفاوتش با Bloc از لحاظ ساختاری اینه که به جای گرفتن یه Stream از event ها ما با function(متد) باش ارتباط برقرار میکنیم.cubitاگه دقت کنید فلش پایینی زیرش نوشته شده functions توی شکل Bloc زیر فلش نوشته شده بود events. پس تنها تفاوت اینه که ما به جای جریانی از event ها از function ها استفاده میکنیم.من تو این مقاله با جفتش براتون مثال هایی میزنم که خوب یادش بگیرید.اول شروع میکنم با معرفی ویجتای مهم این پکیج.BlocProvider : BlocProvider(
  create: (BuildContext context) =&gt; BlocA(),
  child: ChildA(),
);این ویجت به نظرم از مهم ترین ویجتای این پکیجه کاری که این ویجت واسه ما میکنه اینه که میاد یه نمونه از کلاس Bloc ما رو واسه ویجت هایی که توی  widget tree زیر اون BlocProvider قرار دارن فراهم میکنه و اون کلاس ها میتونن به اون Bloc دسترسی داشته باشن. به این کار dependency injection هم میگن. شما کلاس CounterBloc رو تصور کنید. فرض کنید که توی یک صفحه من ویجتای مختلفی دارم و هر ویجت هم به صورت جداگانه اوومدم واسش یه کلاس درست کردم و حالا فرض کنید قراره من توی همه این کلاس ها به اون نمونه CounterBloc دسترسی داشته باشم به جای اینکه بیام اون نمونه کلاس CounterBloc رو واسه همه کلاسای دیگه پاس بدم میام از BlocProvider استفاده میکنم و اون نمونه کلاس CounterBloc رو توی اون صفحه به صورت کامل قابل دسترس میکنم برای همه.widget treeفرض کنید اون نقطه ای که سبز رنگه ویجت BlocProvider ما هستش که واسه ما داره یه bloc رو فراهم میکنه. الان تو این شکل اون دوتا ویجت پایینی که BlocProvider  والد اون دوتا هستش به اون bloc فراهم شده، توسط BlocProvider دسترسی دارن ولی بقیه ویجت ها این امکان رو ندارن.اما چجوری دستری دارن ؟؟ این کار با استفاده از context انجام میشه(اگه مقاله من راجع به context رو نخوندین حتما اول اون رو بخونین). هر ویجت یه context داره که اون context نمایانگره مکان اون ویجت توی widget tree هستش.فرض کنید من یه بلاک دارم به اسم TestBloc و میخوام اون رو با استفاده از BlocProvider (که توی نقطه سبز رنگ قرار داره) برای فرزندانش فراهم کنم.الان ویجتی که زیر اون ویجت سبز رنگ که در واقع BlocProvider ما هست قرار داره میشه فرزند BlocProvider و میتونیم با استفاده از context اون ویجت فرزند بگیم :context.bloc&lt;TestBloc&gt;();
یا 
‌BlocProvider.of&lt;TestBloc&gt;(context);این دوتا خط دقیقا یه کار رو انجام میدن و اونم اینه که میرن واسه ما TestBloc رو پیدا میکنن و به ما برمیگردونن.اما چجوری ؟ما وقتی context یه ویجت رو داشته باشیم میتونیم به ویجت های پدرش دسترسی داشته باشیم. این کد ها هم دقیقا همین کار رو میکنن با استفاده از context ویجت ها رو یکی یکی رو به بالا پیمایش میکنه تا برسه به BlocProvider ای که داره یه TestBloc رو فراهم میکنه واسه ما. اگه رسید بهش که اون TestBloc رو به ما برمیگردونه اگه هم پیدا نکرد که چیزی به ما نمیده.مزیت دیگه ای که داره اینه که BlocProvider به صورت اتوماتیک اون کلاس Bloc رو واسه ما dispose میکنه و ما نیاز نیس نگران این باشیم که خودمون دستی dispose کنیم Bloc رو.اما چند حالت خیلی مهم وجود داره. فرض کنید میخواید یه Bloc رو به صورت سراسری استفاده کنید ینی همه صفحات به اون Bloc دسترسی داشته باشن. تو این حالت ما باید BlocProvider رو در بالاترین نقطه ی widget tree قرار بدیم تا همه بتونن بهش دسترسی داشته باشن. اگه ما BlocProvider رو بالای MaterialApp قرار بدیم در واقع این حالت سراسری رو به وجود آوردیم و همه میتونن به اون BlocProvider دسترسی داشته باشن.void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create :(context) =&gt; TestBloc(),
      child: MaterialApp(
        title: &#039;Flutter Demo&#039;,
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: Home(),
      ),
    );
  }
}الان توی کد بالا من یه BlocProvider دارم که داره TestBlocرو فراهم میکنه. در واقع فیلد create توی BlocProvider واسه همین هست که ما باید یه متد بنویسیم که یه context میگیره و توی بدنه اون متد حتما یه کلاس از نوع Bloc رو return کنیم. الان اینجا من دارم TestBloc رو ایجاد میکنم تا ویجت هایی که زیر این BlocProvider قرار دارن به TestBloc دسترسی داشته باشن.الان به صورت بالا اگه BlocProvider رو بزارین، به صورت سرتاسری بهش دسترسی دارین چون اگه بخوام widget tree کد بالا رو بگم اینجوری میشه :MyApp -&gt; BlocProvider -&gt; MaterialApp -&gt; Home و اگه دقت کنید BlocProvider بعد از MyApp که در ریشه قرار داره اوومده و ینی اینجوری همه با context بهش دسترسی دارن(حتما مقاله BuildContext ای که نوشتم رو بخونید)این ویجت BLocProvider یه فیلد اختیاری دیگه داره به اسم lazy که به صورت دیفالت برابر با false هست اما این فیلد چکار میکنه ؟ این فیلد به BLocProvider میگه هر وقت که من توی کدم برای اولین بار به اون کلاس Bloc فراهم شده توسط تو نیاز داشتم تو بیا اون کلاس Bloc رو واسم create کن ولی اگه بخوایم BLocProvider همون لحظه و در اولین فرصت اون کلاس Bloc رو واسه ما create کنه باید این فیلد رو برابر با true بزاریم.تا اینجا واسه BlocProvider کافیه اما بعدا دوباره میاییم سر وقتش.MultiBlocProvider :MultiBlocProvider(
  providers: [
    BlocProvider&lt;BlocA&gt;(
      create: (BuildContext context) =&gt; BlocA(),
    ),
    BlocProvider&lt;BlocB&gt;(
      create: (BuildContext context) =&gt; BlocB(),
    ),
    BlocProvider&lt;BlocC&gt;(
      create: (BuildContext context) =&gt; BlocC(),
    ),
  ],
  child: ChildA(),
)فرض کنید قراره ما چندتا کلاس Bloc رو فراهم کنیم واسه ویجت های زیرین. واسه این کار میتونیم از این ویجت استفاده کنیم. بقیه چیزهاشم مثه همون BlocProvider هست فقط مزیتش اینه که میشه چندتا Bloc رو فراهم کرد. توی فیلد providers ما میتونیم لیست BlocProvider هامون رو بهش بدیم.BlocBuilder : این ویجت یه جورایی مثه همون StreamBuilder است. یه فیلد اجباری داره به اسم builder که یه متد میگیره. اون متد پارامترهاش یه context هست و یه state که state فعلی هست که کلاس Bloc ما داره برمیگردونه.توی این متد ما حتما باید یه ویجت رو return کنیم.این builder ممکنه چندین بار در طول زمان صدا زده بشه به خاطر دو علت. اولیش اینه که هر وقت state برنامه عوض شه این builder صدا زده میشه و علت دوم ممکنه خود موتور فلاتر دوباره باعث بشه که builder صدا زده بشه. واسه همین توی builder فقط و فقط باید ویجت برگردونیم. مثلا انجام یه سری کار ها مثله Navigate کردن یا نشون دادن SnackBar یا تغییر دادن مقدار یه سری داده اصلا خوب نیست و نباید انجام بشه.یه فیلد دیگه داره که اختیاریه به اسم buildWhen که این فیلد هم یه متد میگیره که پارامتر هاش دوتا state هست اما یکی state فعلی برنامه هست و یکی state قبلیه اون state فعلی. توی این متد ما میتونیم یه سری شرط ها بزاریم که مثلا اگه فقط یه فیلد خاص از اون state ما تغییر کرده بود نسبت به state قبلی بیاد و دوباره builder رو صدا برنه و دوباره بیلد کنه ویجت های تو متد builder رو و اگه اون مقدار از state عوض نشده بود متد builder رو صدا نمیزنه. اینجوری میتونیم  performance برنامه رو بهتر کنیم.اگه کلاس Bloc ما همون جایی که داریم از BlocBuilder استفاده میکنم وجود داشته باشه  میتونیم به این صورت از BlocBuilder استفاده کنیم.BlocBuilder&lt;BlocA, BlocAState&gt;(
  cubit: blocA, // provide the local bloc instance
 buildWhen: (previousState, state) {     
    // return true/false to determine whether or not 
    // to rebuild the widget with state  
  },
  builder: (context, state) {
    // return widget here based on BlocA&#039;s state
  }
)ینی فرض کنید من یه کلاس Bloc دارم به اسم CounterBloc اگه این کلاس رو از طریق BlocProvider فراهم نکرده باشم و اوومده باشم توی یک صفحه دستی از اون کلاس نمونه ساخته باشم باید توی ویجت BlocBuilder اون نمونه از کلاسم رو بهش پاس بدم که این کار توسط فیلد cubit توی BlocBuilder انجام میشه در واقع این فیلد cubit یه نمونه از کلاس Bloc رو میگیره. اما اگه با BlocProvider یه کلاس Bloc رو فراهم کرده باشم نیاز به این فیلد cubit نیست.اگه که کلاس Bloc ما توسط BlocProvider فراهم شده باشه میتونیم اینجوری از BlocBuilder استفاده کنیم :BlocBuilder&lt;BlocA, BlocAState&gt;(
  buildWhen: (previousState, state) {
    // return true/false to determine whether or not
    // to rebuild the widget with state
  },
  builder: (context, state) {
    // return widget here based on BlocA&#039;s state
  }
)تنها فرقشون اینه که اگه ‌Bloc توسط BlocProvider فراهم شده باشه نیاز نیس ما از فیلد cubit توی BlocBuilder استفاده کنیم. ولی حتما باید نوع Bloc و state اون Bloc رو توی این علامتِ &lt;&gt; مشخص کنیم اگه از این روش BlocProvider استفاده میکنیم. چون با این روش خودش به صورت اتوماتیک میره میگرده واسه ما و اون کلاس Bloc رو پیدا میکنه. پس اگه Bloc شما توسط BLocProvider فراهم شده باشه نیاز نیست فیلد cubit رو مقدار دهی کنید ولی حتما باید نوع BLoc و state اون رو توی &lt;&gt; مشخص کنید.BlocListener : این ویجت یه فیلد داره به اسم listener که مثله همون builder در BlocBuilder یه متد میگیره که پارامترهاش context و state هست. متدی که اینجا وارد میکنیم واسه listener مقدار برگشتیش بر خلاف builder که Widget بود اینجا void هست ینی ما توی listener نیاز نیس ویجتی برگردونیم. این ویجت به ما تضمین میده که هر موقع که state برنامه عوض شد این listener صدا زده بشه بر خلاف builder که ممکن بود چندین بار صدا زده بشه. پس ما کارهایی مثله Navigate کردن یا نمایش پیغام یا SnackBar یا تغییر مقادیر داده ها رو میتونیم توی این listener انجام بدیم. نکته مهم اینه که متد listener زمانی که کلاس BLoc تازه ساخته میشه و InitialState رو به ما برمیگردونه صدا زده نمیشه ولی متد builder توی BlocBuilder برخلاف این عمل میکنه و حتی توی اولین state برگشتی که initialstate هست هم صدا زده میشه.یه موقع اشتباه نکنیدا متد listener هر زمان که state عوض میشه صدا زده میشه تنها یک باز صدا زده نمیشه اونم زمانی هست که ما یه نمونه از کلاس Bloc ساختیم و اون به ما داره initialState رو برمیگردونه تو این تنها حالت صدا زده نمیشه.یه فیلد دیگه داره به اسم listenWhen که دقیقا مثله همون buildWhen هست و میشه یه سری شرط گذاشت که بگیم کی listener صدا زده بشه کی صدا زده نشه.این ویجت هم مثه BlocBuilder دو جور میشه صدا زد یکی بدین صورت که ما کلاس Bloc رو به فیلد cubit پاس بدیم : BlocListener&lt;BlocA, BlocAState&gt;(
  cubit: blocA,
  listenWhen: (previousState, state) {
    // return true/false to determine whether or not
    // to call listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA&#039;s state
  }
)یکی بدین صورت که Bloc توسط BlocProvider فراهم شده باشه و ما دیگه نیاز نیس از فیلد cubit استفاده کنیم.و به جاش حتما باید نوع BLoc و state اون رو توی علامت &lt;&gt; مشخص کنیم.BlocListener&lt;BlocA, BlocAState&gt;(
  listenWhen: (previousState, state) {
    // return true/false to determine whether or not
    // to call listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA&#039;s state
  },
  child: Container(),
)MultiBlocListener : با این ویجت میشه هم زمان به چندتا Bloc گوش کرد فقط کافیه یه لیست از BlocListener رو بهش پاس بدیم.MultiBlocListener(
  listeners: [
    BlocListener&lt;BlocA, BlocAState&gt;(
      listener: (context, state) {},
    ),
    BlocListener&lt;BlocB, BlocBState&gt;(
      listener: (context, state) {},
    ),
    BlocListener&lt;BlocC, BlocCState&gt;(
      listener: (context, state) {},
    ),
  ],
  child: ChildA(),
)BlocConsumer : فرض کنید به صورت هم زمان میخواید روی یه کلاس ‌Bloc هم از ‌BlocListener استفاده کنید هم از BlocBuilder. به جای اینکه بیایید جدا جدا از این ویجتا استفاده کنید میتونید از این ویجت استفاده کنید. که هم زمان هم listener داره هم builder. تنها کاری که این ویجت کرده اینه که listener  و builder رو با هم ترکیب کرده وگرنه چیز پیچیده ای نیست.BlocConsumer&lt;BlocA, BlocAState&gt;(
  listenWhen: (previous, current) {
    // return true/false to determine whether or not
    // to invoke listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA&#039;s state
  },
  buildWhen: (previous, current) {
    // return true/false to determine whether or not
    // to rebuild the widget with state
  },
  builder: (context, state) {
    // return widget here based on BlocA&#039;s state
  }RepositoryProvider : RepositoryProvider(
  create: (context) =&gt; RepositoryA(),
  child: ChildA(),
);این ویجت دقیقا مثه همون BLocProvider هست ینی هر چی اونجا گفتیم واسه این هم صادقه ولی تنها فرقشون اینه که این ویجت کلاس Bloc رو فراهم نمیکنه واسه ما بلکه هر چیزی غیر از Bloc رو میشه باش فراهم کرد. مثلا فرض کنید یه کلاس دارید که توش کارهای ارتباط با سرور رو انجام میدید و میخواید از این کلاس تنها یک نمونه تو کل برنامه وجود داشته باشه میتونید با استفاده از این ویجت اون کلاس رو توی کل برنامه فراهم کنید و همه بتونن بهش دسترسی داشته باشن همون dependency injection میشه. پس با این ویجت ما میتونیم کلاس های دیگمون که از نوع Bloc نیستن رو توی برنامه تزریق کنیم و بقیه فرزندان بتونن ازش استفاده کنن.MultiRepositoryProvider : MultiRepositoryProvider(
  providers: [
    RepositoryProvider&lt;RepositoryA&gt;(
      create: (context) =&gt; RepositoryA(),
    ),
    RepositoryProvider&lt;RepositoryB&gt;(
      create: (context) =&gt; RepositoryB(),
    ),
    RepositoryProvider&lt;RepositoryC&gt;(
      create: (context) =&gt; RepositoryC(),
    ),
  ],
  child: ChildA(),
)این ویجت هم واسه ما میتونه همزمان چند نمونه از کلاس ها رو فراهم کنه توی برنامه. تنها فرقش همینه که یه لیست از providers ها میگیره از ما.خب این بود ویجتای مهم پکیج Bloc حالا میخوام یه مثال ساده بنویسم با استفاده از این پکیج.مثالی که میزنم مثال معروف CounterApp هست.خب همونجور که قبلا گفتم ما باید event ها و state هامون رو به صورت جدا بازم بنویسیم.الان event های من میشه :enum CounterEvent {
  increment,
  decrement,
}و state این برنامه هم یک عدد int هست.(در بخش قبل صحبت شد که چرا int هست)الان کلاس CounterBloc رو مینویسم :class CounterBloc extends Bloc&lt;CounterEvent, int&gt;{
  CounterBloc() : super(0);

  @override
  Stream&lt;int&gt; mapEventToState(CounterEvent event) async*{
    if (event == CounterEvent.increment){
      yield state + 1 ;
    }else {
      yield state - 1;
    }
  }
}خب نکته اول اینکه باید از Bloc ارث بری کنیم و حتما توی علامت &lt;&gt; باید نوع event و نوع state خودمون رو مشخص کنیم. که الان event ما میشه CounterEvent و state ما میشه int.بعد باید متد سازنده کلاس رو بنویسیم و حتما اون بخش super هم بنویسیم. چیزی که به عنوان ورودی super میدم میشه initialState ما که ما چون state مون از نوع int هست واسه initial ما 0 رو در نظر گرفتیم. پس چیزی که توی super میزاریم میشه initialState ما.متد بعدی که حتما باید override بشه متد mapEventToState هست. این متد یه event میگیره و یه جریان از state ها برمیگردونه و چون این متد داره Stream برمیگردونه باید از async* استفاده کنیم.توی این متد چک کردم اگه event من افزایش عدد بود نوشتم yield state +1. با کلمه کلیدی yield که قبلا آشنا شدیم ولی state میشه همون state فعلیه ما. ما داریم میگم state فعلی رو یکی زیاد کن و برش گردون و واسه کم کردن هم همینجور. همیشه توی کلاس هایی که از Bloc یا Cubit ارث بری کردن ما میتونیم همه جای اون کلاس به state فعلی دسترسی داشته باشیم با نوشتن کلمه state.نکته مهم این متد این بود که این متد از جنس Stream هست و state ها توی اون yield میشن نه return چون ما نیاز داریم همش روی event ها گوش کنیم که به محض اضافه شدن یه event یه state رو برگردونیم واسه همین نباید return کنیم توی این متد چون اگه return کنیم کلا این متد بسته میشه و دیگه به event ها گوش نمیده.خب این شد کلاس Bloc. حالا میریم سراغ ظاهر.class Home extends StatefulWidget {
  @override
  _HomeState createState() =&gt; _HomeState();
}

class _HomeState extends State&lt;Home&gt; {
  final CounterBloc counterBloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child :BlocBuilder&lt;CounterBloc,int&gt;(
          cubit: counterBloc,
          builder: (context, int state){
            return Text(state.toString());
          },
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: &lt;Widget&gt;[
          FloatingActionButton(
            onPressed: () =&gt; counterBloc.add(CounterEvent.increment),
            tooltip: &#039;Increment&#039;,
            child: Icon(Icons.add),
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () =&gt; counterBloc.add(CounterEvent.decrement),
            tooltip: &#039;Decrement&#039;,
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    counterBloc.close();
    super.dispose();
  }
}خب توی کلاس Home من اول یه نمونه از CounterBloc ساختم. بعد توی متد build توی بخش body اوومدم از BlocBuilder استفاده کردم و چون کلاس Bloc من توسط BlocProvider فراهم نشده و خودم دستی اون رو به صورت محلی ساختم میام توی BlocBuilder فیلد cubit رو مقدار دهی میکنم و اون نمونه کلاس Bloc ام رو بهش میدم. و توی builder هم یه Text اوومدم return کردم و مقدارش رو اون state برگشتی گذاشتم.(دقت کنید که نوع state ما int بود) .واسه اضافه کردن event هم توی onPressed اوومدم گفتم‌:counterBloc.add(CounterEvent.increment)این متد add اجازه میده به من که یه event رو صدا بزنم. وقتی این event رو صدا میزنم اتفاقی که میوفته اینه که متد mapEventToState کلاس CounterBloc به صورت اتوماتیک صدا زده میشه و اونجا طبق event دریافتی یه state  برمیگردونه و باعث میشه که state تغییر کنه و چون state تغییر کرده متد builder دوباره صدا زده میشه و اون ویجت Text دوباره ساخته میشه و مقدار جدید state رو نمایش میده.همین مثال رو میشه با Cubit هم نوشت. همونجور که گفتم Cubit نیاز به جریانی از event ها نداره بلکه با function کار میکنه. پس ما دیگه نیازی به CounterEvent و mapEventToState نداریم.class CounterCubit extends Cubit&lt;int&gt; {
  CounterBloc() : super(0);

  void increment() =&gt; emit(state + 1);

  void decrement() =&gt; emit(state - 1);
}این میشه کلاس CounterCubit ما. اولین فرقش اینه که به جای ارث بری از Bloc از Cubit ارث بری کردیم و چون event هم نداریم دیگه نیاز نیس تو علامت &lt;&gt; event رو مشخص کنیم کافیه فقط نوع state رو مشخص کنیم.فرق بعدی اینه که دیگه متد mapEventToState رو نداریم و باید event های خودمون رو به صورت یه سری متد بنویسیم.مثلا ما event اضافه کردن رو به صورت یه متد نوشتیم و تفاوت دیگه هم اینه که به جای yield از emit استفاده میکنیم و نیازی به async* نیست چون متد ما دیگه Stream برنمیگردونه.پس تفاوت مهمش این بود که ما باید event هامون رو به صورت یه سری متد بنویسیم و حتما واسه تغییر state هامون از emit استفاده کنیم.تفاوت آخر هم توی نحوه صدا زدن event هاس. توی روش اول ما باید event رو توی متد add میفرستادیم ولی  با این روش دیگه نیازی به add هم نیست و مستقیما متد های خودمون رو صدا میزنیم :Row(
  mainAxisAlignment: MainAxisAlignment.end,
  children: &lt;Widget&gt;[
    FloatingActionButton(
      onPressed: () =&gt; counterBloc.increment(),
      tooltip: &#039;Increment&#039;,
      child: Icon(Icons.add),
    ),
    SizedBox(width: 10),
    FloatingActionButton(
      onPressed: () =&gt; counterBloc.decrement(),
      tooltip: &#039;Decrement&#039;,
      child: Icon(Icons.remove),
    ),
  ],
)یادمون نره اگه کلاس ‌Bloc رو خودمون دستی داریم میسازیم(نه با استفاده از BlocProvider) حتما حتما اون کلاس Bloc رو close کنیم تا مشکلات memory leak واسمون پیش نیاد.@override
void dispose() {
  counterBloc.close();
  super.dispose();
}خب اینم از یه مثال ساده.در آخر مقاله یه لینک از صفحه gitHub خودم میزارم که توش یه پرژه تستی با Bloc زدم واسه آموزش بیشتر به شما دوستان میتونین اون هم یه نگاهی بندازین(star هم یادتون نره ??)زمانی که flutter_bloc نسخه ۶.۱.۰ معرفی شد یه سری تغییر خیلی مهم داشت که الان میخوام دربارش صحبت کنم.تغییر مهمشون این بود که context.bloc که واسه پیدا کردن یه Bloc بود منسوخ شد و جاشو داد به یه سری چیز دیگه. الان شما میتونید به جای context.bloc از context.read یا context.watch یا context.select استفاده کنید.اما اینا چه فرقی با هم دارن؟؟context.watch : زمانی که شما یه Bloc رو با استفاده از BlocProvider فراهم میکنید میتونید توی ویجت های زیرین با استفاده از context.watch به اون ‌‌Bloc دسترسی داشته باشین اما یه نکته مهم است. در واقع context.watch میره اون Bloc رو پیدا میکنه(با استفاده از context اون کلاس Bloc رو پیدا میکنه) و حواسش هست که هر وقت state اون ‌Bloc عوض شد بیاد متد build رو از اول صدا بزنه. در واقع دیگه با این کار ما نیازی به BlocBuilder نداریم. چون ما با context.watch داریم state اون Bloc رو زیر نظر میگیریم و هر وقت state عوض شه متد build جایی که context.watch هست دوباره از اول صدا زده میشه. واسه همین نیاز به BlocBuilder نیست که بیاد state رو زیر نظر بگیره چون ما الان خودمون با context.watch داریم همین کار رو میکنیم.فرض کنید من CounterCubit رو به صورت سرتاسری فراهم کنم توی برنامه.void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) =&gt; CounterCubit(),
      child: MaterialApp(
        title: &#039;Flutter Demo&#039;,
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: Home(),
      ),
    );
  }
}حالا میام توی کلاس Home به جای BlocBuilder از ویجت Builder استفاده میکنم.class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Builder(
          builder: (context) {
            final counterState = context.watch&lt;CounterCubit&gt;().state;
            return Text(
              counterState.toString(),
              style: const TextStyle(fontSize: 40),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () =&gt; context.read&lt;CounterCubit&gt;().increment(),
      ),
    );
  }
}علت اینکه از ویجت builder استفاده کردم اینه که اگه بیام context.watch رو توی خود متد build کلاس بزارم   هر بار که state اون CounterCubit عوض شه چون اینجا من دارم watch میکنم باعث میشه کل متد build کلاس Home از اول صدا زده بشه چون اگه یادتون باشه گفتم context.watch هر جا باشه متد build اونجا از اول صدا زده میشه واسه همین از ویجت Builder استفاده میکنم و توی این ویجت میگم context.watch تا وقتی state اون CounterCubit عوض شد باعث بشه فقط متد builder ویجت Builder دوباره صدا زده بشه و فقط ویجت Text دوباره از اول ساخته بشه.context.select : دقیقا همون کار context.watch رو میکنه با این تفاوت که مثه buildWhen میتونیم بهش بگیم چه زمانی بیاد  از اول rebuild کنه.class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Builder(
          builder: (context) {
            final counterState = context.select((CounterCubit state) =&gt; state.counter);
            return Text(
              counterState.toString(),
              style: const TextStyle(fontSize: 40),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () =&gt; context.read&lt;CounterCubit&gt;().increment(),
      ),
    );
  }
}تنها تفاوت این کد با کد قبلی اینه که از context.select استفاده کردم. context.select یه متد به عنوان ورودی میگیره که پارامتر این متد state ما هست و باید انتخاب کنیم که روی چه بخشی از state گوش کنه که تا عوض شد بیاد و دوباره rebuild کنه الان اینجا من گفتم روی counter کلاس CounterCubit گوش کنه که هر وقت عوض شد بیاد متد builder ویجت Builder رو صدا بزنه و Text رو از اول بسازه. پس در واقع این context.select مثه همون buildWhen هست توی BlocBuilder.ما با استفاده از context.watch و context.select دیگه میتونیم از ‌BlocBuilder استفاده نکنیم.context.read :این context.read فقط میره واسه ما Bloc رو پیدا میکنه و میاره برعکس دوتای قبلی دیگه به تغییرات state گوش نمیده. در واقع همون context.bloc عه که الان منسوخ شده به جاش میتونین از context.read استفاده کنید.با این context.read شما حتما نیاز به BlocBuilder هم دارین. توی متد هایی مثه onPressed, onTap, d و .... اگه بخواین به Bloc ای که توسط BlocProvider فراهم شده دسترسی داشته باشین حتما حتما باید از context.read استفاده کنید.در پایان میخوام چندتا نکته خیلی مهم رو بهتون بگم که اگه رعایت نکنید قطعا به مشکل میخورید.به کد زیر نگاه کنید : class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) =&gt; CounterBloc(),
      child: Scaffold(
        body: Center(
          child: RaisedButton(
            onPressed: () =&gt; context.read&lt;CounterBloc&gt;().increment(),
          ),
        ),
      ),
    );
  }
} من اوومدم توی کلاس Test از BlocProvider استفاده کردم و CounterBloc رو توی این صفحه فراهم کردم و به عنوان child به BlocProvider یه scaffold دادم و یه RaisedBudtton ساختم که توی onPressed اوومدم با context.read اون CounterBloc رو گرفتم و عملیات افزایش عدد رو صدا زدم. خب به نظرتون این کد بدون مشکل کار میکنه ؟؟این کد ارور میده و مشکلش هم اینه که میگه با این context که توی context.read ازش استفاده کردی من نتونستم یه BlocProvider که CounterBloc رو فراهم میکنه پیدا کنم. اما چرا ؟ببینید widget tree این کد به این صورت است :Test -&gt; BlocProvider -&gt; Scaffold -&gt; Center -&gt; RaisedButtonاون context ای که من ازش توی context.read دارم استفاده میکنم متعلق به Test هست حالا وقتی میگم context.read این شروع میکنه از مکان فعلیش توی widget tree میره بالا تا برسه به یه BlovProvider که CounterBloc داره فراهم میکنه اما اگه به این widget tree که من نوشتم دقت کنید BlocProvider به عنوان فرزند Test هست نه والد اون و چون context مال Test هست از ویجت Test شروع میکنه به بالا حرکت کردن و خب BlocProvider ای هم پیدا نمیکنه چون BlocProvider فرزند Test هست نه والد اون.روش اول حل این مشکل استفاده از ویجت Builder هست :class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) =&gt; CounterBloc(),
      child: Scaffold(
        body: Center(
          child: Builder(
            builder: (builderContext){
              return RaisedButton(
                onPressed: () =&gt; builderContext.read&lt;CounterBloc&gt;().increment(),
              );
            },
          ),
        ),
      ),
    );
  }
}الان widget tree این جوری شده :Test -&gt; BlocProvider -&gt; Scaffold -&gt; Center -&gt; Builder -&gt; RaisedButtonو توی onPressed هم دارم از builderContext استفاده میکنم که context مربوط به ویجت Builder هست.الان از Builder شروع میکنه به بالا حرکت کردن و چون الان Builder از فرزندان BlocProvidre هست پس میتونه به BlocProvider دسترسی داشته باشه  و کد بدون مشکل اجرا میشه.روش دوم :class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) =&gt; CounterBloc(),
      child: Scaffold(
        body: Center(
          child: TestButton(),
        ),
      ),
    );
  }
}

class TestButton extends StatelessWidget {
  @override
  Widget build(BuildContext testButtonContext) {
    return RaisedButton(
      onPressed: () =&gt; testButtonContext.read&lt;CounterBloc&gt;().increment(),
    );
  }
}اومدم ویجت RaisedButton رو توی یه کلاس Stateless جدا نوشتم.الان widget tree این جوری شده :Test -&gt; BlocProvider -&gt; Scaffold -&gt; Center -&gt; TestButtonو context ای که دارم استفاده میکنم ازش testButtonContext هست که متعلق به کلاس TestButton هست. الان از testButtonContext که context کلاس TestButton هست شروع میکنه میره به بالا حرکت کردن و چون الان TestButton از فرزندان BlocProvider هست این کد هم مشکلی نداره.مشکل بعدی که واسه خیلی ها ممکنه پیش بیاد اینه که میگن چرا هر کاری میکنیم استیت برنامه عوض نمیشه ؟؟فرض کنید کلاس زیر کلاس state ما هستش :class LightState {
  bool isOn ;

  LightState(this.isOn);
}این کلاس فقط یه فیلد داره که میگه چراغ روشن هست یا خاموش.این هم کلاس Cubit من هست : class LightCubit extends Cubit&lt;LightState&gt; {
  LightCubit() : super(LightState(false));

  void lightOn() {
    state.isOn = true;
    emit(state);
  }

  void lightOff() {
    state.isOn = false;
    emit(state);
  }
}که initialState رو گزاشتم LightState(false) ینی اولش چراغ خاموش باشه و دوتا متد نوشتم یکی واسه روشن کردن که اومدم توش فیلد isOn اون state فعلیم رو true کردم و بعدش اون state رو emit کردم در lighoFF هم همین کار رو کردم ولی برعکس.این هم کد ظاهر :class Test extends StatefulWidget {
  @override
  _TestState createState() =&gt; _TestState();
}

class _TestState extends State&lt;Test&gt; {
  final LightCubit cubit = LightCubit();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child:BlocBuilder(
          cubit: cubit,
          builder: (context,LightState state){
            return  Text(state.isOn ? &#039;on&#039; : &#039;off&#039;);
          },
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: (){
              cubit.lightOn();
            },
          ),
          SizedBox(width: 10,),
          FloatingActionButton(
            backgroundColor: Colors.black,
            onPressed: (){
              cubit.lightOff();
            },
          )
        ],
      ),
    );
  }

  @override
  void dispose() {
    cubit.close();
    super.dispose();
  }
}وقتی برنامه رو اجرا میکنیم اول مقدار initial رو به ما نشون میده که off هست بعد وقتی روی دکمه lightOn میزنیم نوشته وسط صفحه عوض میشه و مینویسه on ولی دیگه از این به بعد هر کاری کنیم استیت برنامه عوض نمیشه ولی چرا ؟ببینید همونجور که قبل تر گفتم ویجت BlocBuilder به state حساسه ینی هر وقت state برنامه عوض بشه میاد دوباره builder رو صدا میزنه اما اینجا داره چه اتفاقی میوفته که state عوض نمیشه ؟ما توی متد lightOn اوومدیم فقط فیلد همون state فعلی رو عوض کردیم و اون رو emit کردیم خب تا اینجاش اوکی و کار هم میکنه. اما وقتی بعد از اون میاییم lightOff رو صدا میزنیم این هم میاد فقط فیلد stateفعلی رو عوض میکنه و باز همون نمونه از state رو برمیگردونه(یادتون باشه که state ما از نوع کلاس LightState است). اگه بدونین توی دارت مقایسه دوتا آبجکت بر اساس مکان اونها توی حافظه انجام میشه حتی اگه مقدار فیلدهاش عوض شه ولی مکانش همون قبلی باشه از نظر دارت این فرقی نکرده با یه مثال ساده تر اینو توضیح میدم :void main() {
  Test a = Test(20);
  Test b = Test(20);
  print(a == b);
  print(a == a);
  print(b == b);

}

class Test {
  int age;
  Test(age);
}

جواب ها :
false
true 
true
یه کلاس Test دارم که یه عدد میگیره و اوومدم دوتا نمونه a و b رو ازش ساختم که هر دو مقدار ۲۰ رو دارن ولی وقتی چک میکنم که a==b هست یا نه به من false برمیگردونه چون کاری به مقدارش نداره فقط چک میکنه که آیا این دوتا آدرس خونه حافظشون یکی هست یا نه و چون یکی نیس به من false میده. ولی وقتی میگم a==a به من true میده چون آدرس خونه حافظشون دقیقا یکیه. دارت به صورت دیفالت اینجوری دوتا آبجکت رو مقایسه میکنه و ما چون توی lightOff فقط اوومدیم مقدار فیلد isOn رو تغییر دادیم و باز همون نمونه از state رو برگردوندیم از نظر دارت این دوتا state کاملا شبیه به همن و چون شبیه هستن builder رو صدا نمیزنه اما چکار کنیم ؟میتونیم از پکیج equatable استفاده کنیم که این قابلیت رو به دارت میده که آبجکت ها رو بر اساس مقادیر فیلد اونها مقایسه کنه. و همچنین توی این شرایط باید توی emit یک نمونه جدید از اون کلاس state مون برگردونیم. هم چنین میتونیم متد copyWith رو برای کلاس state مون  بنویسیم و توی emit بگیم :void lightOff() {
   emit(state.copyWith(false));
}
این copyWith کاری که میکنه اولا یه نمونه جدید از اون کلاس به ما برمیگردونه دوما میره فیلدهایی که ما تغییر دادیم رو تغییر میده و اونایی که تغییر ندادیم رو دست نخورده میزاره یه سری پکیج هستن که این متد copyWith رو برای کلاسای ما مینویسن.پس یادتون باشه حتما یه نمونه جدید از کلاس state برگردونین.نکته بعدی که میخوام دربارش حرف بزنم روش های دسترسی به Bloc هست. یه کمی دربارش گفتم ولی خب میخوام تو این قسمت کامل توضیحش بدم.روش اول اینجوریه که ما مثلا تو یه صفحه خاص به یه ‌Bloc نیاز داریم کاری که میکنیم تو همون صفحه یه نمونه از اون کلاس Bloc میسازیم, ازش استفاده میکنیم و در نهایت حتما اون رو close  میکنیم.به این روش میگن Local Access.مثال این کد :class TestPage extends StatefulWidget {
  @override
  _TestPageState createState() =&gt; _TestPageState();
}

class _TestPageState extends State&lt;TestPage&gt; {
  final CounterBloc counterBloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocBuilder(
        cubit: counterBloc,
        builder: (context,int state){
          return Text(state.toString());
        },
      ),
    );
  }

  @override
  void dispose() {
    counterBloc.close();
    super.dispose();
  }
}من توی کلاس TestPage اوومدم یه نمونه از CounterBloc ساختم ازش توی BlocBuilder استفاده کردم و درنهایت توی متد dispose اون رو close کردم.حالا فرض کنید برنامه ما ۱۰ تا صفحه مختلف داره و میخوایم فقط توی دوتا صفحه اون، از CounterBloc استفاده کنیم. شاید بگین خب توی صفحه دوم هم یه نمونه از CounterBloc بساز دیگه. آره این میشه ولی با این کار عملا من دوتا CounterBloc دارم و اگه توی صفحه اول عدد رو به ۵ افزایش بدم و بعد برم توی صفحه دوم چون اون یه نمونه جدا از ConterBloc هست از ۰ شروع میشه در صورتی که من میخوام عدد اون صفحه دوم هم مثله عدد همون صفحه اول باشه که واسه این کار کلا نیازه یه نمونه از CounterBloc وجود داشته باشه. خب این مشکل ۲تا راه حل داره :راه حل اول : استفاده از BlocProvider به صورت سرتاسری که به این روش میگن Global Access ینی ما از همه جا بهش دسترسی داریم.راه حل دوم : استفاده از BlocProvider.value و فراهم کردن CounterBloc فقط واسه صفحه دوم. که به این روش میگن Route Access.راه حل اول عملیه ولی زیاد مناسب نیس چون ما نمیخوایم این CounterBloc توی همه صفحات قابل دسترس باشه فقط میخوایم توی دوتا از صفحات قابل دسترس باشه.اما راه حل دوم : ببینید BlocProvider یه فیلد داره به اسم create همون جور که قبلا گفتم ما توی این create میاییم یه نمونه از کلاس Bloc مون رو میسازیم و اون رو واسه فرزندای ‌BlocProvider توی widget tree فراهم میکنیم اما این BlocProvider یه متد سازنده دیگه داره  به اسم BlocProvider.value که این دیگه فیلد create نداره بلکه یه فیلد داره به اسم value که یه کلاس Bloc که وجود داره رو میگیره و اون رو واسه فرزنداش فراهم میکنه. مثلا من یه نمونه ساختم از CounterBloc میتونم اون نمونه رو بدم به BlocProvider.value و اون نمونه از CountetBloc رو توی صفحه دوم فراهم کنم تا اون صفحه هم بتونه از همون نمونه CounterBloc استفاده کنه. به این صورت :class TestPage extends StatefulWidget {
  @override
  _TestPageState createState() =&gt; _TestPageState();
}

class _TestPageState extends State&lt;TestPage&gt; {
  final CounterBloc counterBloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          BlocBuilder(
            cubit: counterBloc,
            builder: (context, int state) {
              return Text(state.toString());
            },
          ),
          SizedBox(
            height: 15,
          ),
          RaisedButton(
            child: Text(&#039;go to second page&#039;),
            onPressed: () {
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (pageContext) =&gt; BlocProvider.value(
                    value: counterBloc,
                    child: SecondPage(),
                  ),
                ),
              );
            },
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () =&gt; counterBloc.increment(),
      ),
    );
  }

  @override
  void dispose() {
    counterBloc.close();
    super.dispose();
  }
}
این صفحه اول منه که توش یه CounterBloc ساختم و توی بدنه هم از BlocBuilder استفاده کردم و چون از BlocProvider برای فراهم کردم CounterBloc استفاده نکردم و خودم دستی اون رو ساختم توی همین صفحه، واسه همین نیازه که فیلد cubit موجود در BlocBuilder رو مقدار دهی کنیم. و بعد یه دکمه گذاشتم واسه افرایش عدد و توی متد dispose هم اوومدم CounterBloc رو close کردم و اما مهمترین بخش اون دکمه ای است که اوومدم باش به صفحه دومم Navigate کردم. تنها فرقی که داره زمانی که میخوام صفحه دومم رو توی MaterialPageRoute برگردونم قبلش از BlocProvider.value استفاده میکنم و اون صفحه دومم رو به عنوان child اون BlocProvider.value میدم با این کار CounterBloc توی صفحه دوم هم قابل دسترس است.در واقع این بخش از کد توی صفحه اول مهم است : RaisedButton(
  child: Text(&#039;go to second page&#039;),
  onPressed: () {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (pageContext) =&gt; BlocProvider.value(
          value: counterBloc,
          child: SecondPage(),
        ),
      ),
    );
  },
)لطفا حواستون باشه توی فیلد value در BlocProvider.value اصلا و ابدا نباید یه نمونه جدید از کلاس Bloc رو بسازید بلکه حتما باید همون نمونه ای که قبلا وجود داره و ساخته شده رو بدید بهش به طور مثال من توی کد بالا اول اوومدم یه نمونه از CounterBloc اول کلاسم ساختم و توی BlocProvider.value از اون نمونه آماده استفاده کردم نیومدم دوباره یه نمونه از کلاس CounterBloc بسازم و پاس بدم بهش. پس به این نکته حتما توجه کنید. کد صفحه دوم :class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: BlocBuilder&lt;CounterBloc, int&gt;(
          builder: (context, int state){
            return Text(state.toString());
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () =&gt; context.read&lt;CounterBloc&gt;().increment(),
      ),
    );
  }
}این هم صفحه دوم منه که توی FloatingActionButton اوومدم با context.read اون CounterBloc فراهم شده واسه این کلاس رو گرفتم و متد increment رو روش صدا زدم. الان هر دوتا پیج یه عدد مشترک رو نمایش میدن. فقط نکته مهم اینجاس که BlocProvider.value برخلاف BlocProvider عادی خودش به صورت اتوماتیک اون نمونه از کلاس Bloc رو واسمون close نمیکنه واسه همین خودمون دستی اون CounterBloc رو close کردیم.و اما نکته آخر :این نکته مربوط میشه به performance برنامتون و سعی کنید حتما این کار رو انجام بدید.فرض کنید یه صفحه دارید که توش ۱۰ تا ویجت مختلف هست اما از بین این ۱۰ تا ویجت فقط قراراه یک ویجت state اون تغییر کنه و نسبت به event ها واکنش داشته باشه و ظاهرش عوض شه پس کاری که ما میکنیم به جای اینکه بیاییم BlocBuilder رو به عنوان اولین ویجت بزاریم و هر ۱۰ تا ویجت رو توی اون BlocBuilder بزاریم میاییم فقط اون یدونه ویجتی که قراره عوض شه رو توی BlocBuilder میزاریم. اینجوری وقتی state برنامه عوض میشه به جای اینکه دوباره ۱۰ تا ویجت رو از اول بسازه فقط یک ویجت رو از اول میسازه و performance برنامه ما بهتر میشه.یه مثال هم میزنم براتون :این کد پایین خووب نیست : ??class Home extends StatelessWidget {
  final CounterCubit counterCubit = CounterCubit();

  @override
  Widget build(BuildContext context) {
    return BlocBuilder&lt;CounterCubit, int&gt;(
      value : counterCubit,
      builder: (context, int state) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(&#039;Push The Button For Change the Number&#039;),
                const SizedBox(height: 10),
                Text(
                  state.toString(),
                  style: TextStyle(fontSize: 40),
                ),
                const SizedBox(height: 10),
                RaisedButton(onPressed: () =&gt; counterCubit.increment(), child: Text(&#039;Increment&#039;)),
                const SizedBox(height: 10),
                RaisedButton(onPressed: () =&gt; counterCubit.decrement(), child: Text(&#039;Decrement&#039;)),
              ],
            ),
          ),
        );
      },
    );
  }
}میبینید که همه چیز توی BlocBuilder هست ولی فقط یه Text داره به state برنامه گوش میده و عوض میشه با عوض شدن state. پس این کار خوب نیست چون با عوض شدن state همه ویجتا از اول ساخته میشن.و اما کد درست : ??class Home extends StatelessWidget {
  final CounterCubit counterCubit = CounterCubit();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(&#039;Push The Button For Change the Number&#039;),
            const SizedBox(height: 10),
            BlocBuilder&lt;CounterCubit, int&gt;(
              cubit: counterCubit,
              builder: (context, int state) {
                return Text(
                  state.toString(),
                  style: TextStyle(fontSize: 40),
                );
              },
            ),
            const SizedBox(height: 10),
            RaisedButton(onPressed: () =&gt; counterCubit.increment(), child: Text(&#039;Increment&#039;)),
            const SizedBox(height: 10),
            RaisedButton(onPressed: () =&gt; counterCubit.decrement(), child: Text(&#039;Decrement&#039;)),
          ],
        ),
      ),
    );
  }
}
تو این کد من فقط اون Text ای که داره به state گوش میده رو توی BlocBuilder گذاشتم. که اینجوری هر وقت state برنامه عوض میشه فقط یه ویجت به اون تغییرات گوش میده و عوض میشه نه ۱۰ تا ویجت. خب دوستان این آموزش هم تموم شد و امیدوارم که تونسته باشم به خوبی مفهوم Bloc رو بهتون گفته باشم. اگه سوالی داشتید زیر همین مقاله کامنت بزارین حتما در اولین فرصت سوال شما رو جواب میدم.??این پایین هم یه لینک از یه پروژه آموزشی ساده با Bloc رو میزارم براتون که توی گیت هابم واسه شما دوستان عزیز نوشتم تا بهتر این مفهوم Bloc رو درک کنید.توی این پروژه در عین سادگی سعی کردم اکثر مفاهیم مهمی که گفتم رو توش بزارم براتون.لینک کانال یوتوب من لطفا سر بزنید : https://www.youtube.com/c/FlutterStan https://github.com/AliHoseinpoor/virgool_bloc_training خدا نگه دار همتون باشه. ❤️❤️</description>
                <category>Ali Hoseinpoor</category>
                <author>Ali Hoseinpoor</author>
                <pubDate>Sun, 03 Jan 2021 15:17:13 +0330</pubDate>
            </item>
                    <item>
                <title>مفاهیم پایه فلاتر : Widget -&gt; BuildContext -&gt; State</title>
                <link>https://virgool.io/flutter-community/%D9%85%D9%81%D8%A7%D9%87%DB%8C%D9%85-%D9%BE%D8%A7%DB%8C%D9%87-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-widget-buildcontext-state-zpevgqvvvnh1</link>
                <description>سلام دوستان امیدوارم که حالتون خووب باشه.اول از همه چیز بگم که از هم اکنون میتوانید ویدیوهای مربوط به آموزش برنامه نویسی مخصوصا فلاتر و دارت رو در کانال یوتوب من دنبال کنید. لینکشم میزارم همین پایین :https://www.youtube.com/c/FlutterStanخب امروز میخوام یکم راجع به مفاهیم پایه تو فلاتر صحبت کنم باهاتون. دلیل اصلی این مقاله از یه ارور معروف میاد که شاید خیلی هاتون باش برخورد کردین :Scaffold.of() called with a context that does not contain a Scaffold.خب ارور که خیلی واضحه اگه بخوایم ترجمش کنیم میگه که شما Scaffold.of رو با context ای فراخوانی کردید که اون context اصن scaffold نداره !! جالب شد نه ؟؟ خب حالا این چیزی که گفته ینی چی ؟؟واسه اینکه جواب سوالمون رو بگیریم اولش باید یکم با مفاهیم سر و کله بزنیم.Widget : ویدجت به زبون خیلی ساده میشه اون چیزی که با توجه به پیکربندی(configuration) و ویژگی هاش توصیف میکنه، ui ما چه شکلی باشه(این تعریف ویدجت توی داکیومنت خود فلاتر هست که خیلی ساده و راحته)توی فلاتر همه چیز ویدجته. حالا ما یه چیز توی فلاتر داریم به اسم widget tree که میشه درختی از ویدجت ها که به صورت والد و فرزند هستن. به ویدجتی که ویدجت های دیگه رو شامل بشه میگن پدر و به اون ویدجت های پایین اون میگن ویدجت فرزند.widget treeعکس بالا به خوبی widget tree رو به ما نشون میده. که اگه بخوام یه مثال بزنم  MyApp پدر MaterialApp هست و بالعکس MaterialApp میشه فرزند MyApp.پس تا اینجا مفهوم کلی ویدجت و ویدجت تری رو یاد گرفتیم بریم واسه بقیش.BuildContext : کانتکست میشه اشاره گری به مکان یه ویدجت توی widget tree. خب نکته مهم اینکه هر ویدجت context مخصوص خودش رو داره که این context مکان اون ویدجت توی درخت رو مشخص میکنه.اگه ویدجت A یه سری فرزند داشته باشه، context این ویدجت A میشه والد context فرزندای اون ویدجت A. در واقع مثه همون widget tree هست که اینجا به جای ویدجت context داریم.توی عکس بالا اگه هر رنگ رو نماد یه context در نظر بگیریم به ما نشون میده که مثلا MyHomePage که با رنگ قرمز نشون داده شده والد context ویدجت های زیریش هست در واقع ما میتونیم از یه فرزند با context، اجداد اون رو پیدا کنیم مثلا توی ویدجت Column ما میتونیم به اجداد اون دسترسی داشته باشیم که اجدادش میشن : MyApp -&gt;MaterialApp -&gt; MyHomePage -&gt;Scaffold -&gt; Centerاگه توی ویدجت Text که توی این عکس در پایین ترین سطح قرار داره بنویسیم :context.ancestorWidgetOfExactType(Scaffold);توی این کد context در واقع context اون ویدجت Text ما هستش که با پیمایش اجداد این context، به ما نزدیک ترین Scaffold به Text رو برمیگردونه .این نکته خیلی مهمه که ما توی یه ویدجت با استفاده از context اون میتونیم به اجدادش دسترسی داشته باشیم. چون همونجور که گفتم در واقع context یه اشاره گری هست به اینکه، یه ویدجت کجای درخت قرار داره و چون این context مکان رو مشخص میکنه ما میتوینم باهاش درخت رو پیمایش کنیم و مکان اجداد اون  ویدجت رو هم بفهمیم و بتونیم از اجداد اون استفاده کنیم. بالفرض من یه Text دارم که والدش یه Scaffold هست حالا اگه من context این Text رو داشته باشم باهاش به راحتی میتونم به Scaffold هم دسترسی داشته باشم.اگه دقت کرده باشین توی کدهاتون خیلی جا ها از متد of() استفاده کردین مثلا Scaffold.of یا Navigator.of یا BlocProvider.of و ... این of دقیقا کاری که میکنه میاد context رو میگیره و از اونجا شروع میکنه به پیمایش تا اولین Scaffold یا Navigator یا BlocProvider یا ... رو پیدا کنه و به شما برگردونه اگه هم پیدا نکرد که null برمیگردونه به شما.در واقع وقتی من توی یه ویدجت میگم Scaffold.of و بهش context اون ویدجت جاریم هم میدم این میاد از اون context شروع میکنه پیمایش کردن درخت رو به بالا تا اولین Scaffold که والد این ویدجت جاریم هست رو پیدا کنه و به من برش گردونه تا من بتونم از خصوصیات Scaffold استفاده کنم.state : به مجموعه دیتا هایی که توی ویدجت نگه داری میشن و بیانگر ویژگی های رفتاری اون ویدجت هستن و ممکنه توی طول عمر اون ویدجت تغییر کنه رو میگن State.زمانی که یه ویدجت ساخته میشه state اون ویدجت مرتبط میشه با context اون ویدجت ینی یه جوری به هم وصل میشن حتی اگه context جاش توی tree عوض شه باز هم state به اون مرتبط هست و هیچ جوره از هم جدا نمیشن. زمانی که این دوتا به هم مرتبط میشن در واقع ویدجت mounted میشه و تا قبل اینکه mounted نشه، ما نمیتونیم از setState استفاده کنیم.خب مفاهیم پایه مون تموم شد حالا وقتشه بریم سر کار اصلی خودمون ینی جواب دادن به اون سوال اصلیمون(همون ارور context) : نکته اولی که باید بدونین اینه که وقتی از Stateless یا Stateful ویدجت استفاده میکنین، اون context که توی متد build پاس داده میشه context همون ویدجت جاری هست که به صورت اتوماتیک توسط خود فلاتر به این ویدجت فعلی پاس داده میشه. با یه مثال توضیح میدم :void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &#039;Test&#039;,
      home: Test(),
    );
  }
}

class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    // به این بخش دقت کنید
    print(context.widget);
    print(context.findAncestorWidgetOfExactType&lt;Test&gt;());
    //
    return const Scaffold();
  }
}اولین print توی کد بالا میاد اسم ویدجتی که این context به اون ویدجت تعلق داره رو به ما برمیگردونه. که خروجیش الان Test هست ینی این context به ویدجت Test تعلق داره.توی کد بالا ما یه MaterialApp داریم که توی home بهش ویدجت Test رو دادیم. توی متد build اون ویدجت Test اوومدیم توی print دوم گفتیم که با این context که داری(که مال همین ویدجت Test هست) برو واسه من اولین ویدجت Scaffold که توی اجدادت هست رو پیدا کن و اون رو برگردون و اون رو print کن. به نظرتون جوابش چیه ؟؟؟بله null برمیگردونه چون ویدجت Scaffold ای پیدا نمیکنه. در واقع ویدجت Test ویدجت جاریه ما هست و ویدجت های والد اون میشن MaterialApp و MyApp که همینجور که میبینیم ویدجت Scaffold جزو والد ها نیست بلکه جزو فرزندهای Test هست. متد findAncestorWidgetOfExactType فقط والد ها رو پیدا میکنه و خود ویدجت جاری هم حساب نمیکنه توی والد ها واسه همین وقتی به context ویدجت Test میگیم برو توی والد هات Scaffold رو پیدا کن به ما null برمیگردونه چون ویدجت والدی به اسم Test نداره.حالا اگه اون بخش print کد رو اینجوری تغییرش بدم :print(context.findAncestorWidgetOfExactType&lt;MyApp&gt;());این دیگه الان به من null برنمیگردونه بلکه به من یه آبجکت از MyApp برمیگردونه چون دارم به context که مال Test هست میگم برو واسه من نزدیک ترین MyApp رو پیدا کن و برگردون. حالا میاد پیمایش میکنه توی اجداد Test که میبینه Test فرزند MaterialApp هست و  MaterialApp فرزند MyApp هست و چون الان MyApp رو پیدا کرده اینو واسه ما برمیگردونه. در واقع پیمایشش اینجور شد : MaterialApp -&gt; MyApp ینی از پایین به بالا شروع به پیمایش کرده تا اون ویدجت رو پیدا کنه.حالا یه مثال دیگه میزنیم : (برای خوانایی بهتر عکس کد رو میزارم و دقت کنید که فقط کلاس Test رو عوض میکنیم بقیه کد دست نخورده هست)توی این مثال اوومدم توی FloatingActionButton یه SnackBar ساختم که واسه ساختنش نیاز داره که یه Scaffold داشته باشه که این Scaffold رو با همون متد معروف of (که یه context میگرف و اجداد رو پیمایش میکرد تا یه آبجکتی که ما خواستیم رو برگردونه) پیدا میکنه.به نظرتون با چیزایی که الان بهتون گفتم جواب این چی میشه ؟؟ آیا نشون میده ؟؟ ( جواب رو نخونین و روش با توجه به نکاتی که گفتم فک کنید)جواب : باز هم به ارور میخوریم و این دفعه ارور معروف رو به ما میده که میگه با این context من scaffold ای پیدا نکردم که بهت برگردونم که باش Snackbar نشون بدی.ولی چرا ارور داد ؟ جوابش باز هم همون نکتس چون این context داره به ویدجت Test اشاره میکنه و توی اجداد Test هم هیچ ویدجت Scaffold ای نیس پس نمیتونه به من یه Scaffold برگردونه پس ارور میده که من هیچ Scaffold ای پیدا نکردم.ولی چجور میشه این رو برطرف کرد و به context اون Scaffold دسترسی داشت ؟واسه حلش دو راه وجود داره :راه حل ۱) استفاده از ویدجت Builder : ویدجت Builder یه پروپرتی داره به اسم builder که این پروپرتی یه متد میگیره که ورودیش یه context هست و یه ویدجت رو برمیگردونه:‌Builder(
  builder: (context) {
    return Container();
  },
)همونجور که بالا تو کد میبینین این متد یه context میگیره و یه ویدجت رو return میکنه. اما نکته مهم اینجاس که این context از کجا میاد ؟؟ این context میشه context همین ویدجت Builder. در واقع ما با این ویدجت به context خود ویدجت Builder دسترسی پیدا میکنیم و میتونیم حالا با این context به اجداد Builder هم دسترسی داشته باشیم. حالا اگه ما Builder رو بزاریم جزو فرزندهای Scaffold در واقع میتونیم با استفاده از context اون ‌ویدجت Builder به Scaffold دسترسی داشته باشیم.(طبق همون نکاتی که گفتم که میشه context رو پیمایش کرد و اجدادش رو به دست آورد)در واقع اگه ما FloatingActionButton رو توی ‌Builder بزاریم چون الان والد Builder اون Scaffold هس در واقع ما میتونیم الان با context این Builder به جدش که Scaffold هست دسترسی داشته باشیم و مشکل رو حل کنیم. چون همه مشکل ما الان اینه که یه context داشته باشیم که بتونیم باش به Scaffold دسترسی داشته باشیم تا بتونیم یه SnackBar نشون بدیم:من الان FloatingActionButton رو گزاشتم توی Builder و Builder الان فرزند Scaffold هست. حالا من توی متد ‌builder اومدم FloatingActionButton رو return کردم و توش دوباره Scaffold.of رو صدا زدم که این دفعه به درستی کار میکنه چون این context همونجور که گفتم context ویدجت Builder هست و به راحتی با پیمایش اجداد این context میتونیم به Scaffold برسیم.حالا یکم پیچیده ترش میکنم :الان widget tree این Test اینجوری میشه :Test -&gt; Scaffold -&gt; Center -&gt; Container -&gt; Builder -&gt; FlatButton -&gt; Textمن اوومدم FlatButton رو گزاشتم توی ویدجت Builder و اجداد Builder هم که توی خط بالا میتونید ببینید.حالا توی onPressed ویدجت FlatButton اوومدم باز گفتم Scaffold.of به نظرتون این کار میکنه ؟؟؟معلومه که کار میکنه چون همونجور که دیگه الان میدونید این ctx(برای خوانایی بهتر ورودیه متد builder که یه context میگیره رو اسمش رو کردم ctx که با اون context که توی متد build خود ویدجت هست قاطی نشه. پس الان ctx میشه context ویدجت Builder ما و context میشه برای ویدجت Test) میشه مال Builder که وقتی این ctx رو پیمایش کنیم رو به بالا(اجدادش رو پیمایش کنیم) Scaffold رو میتونیم توی اجداد Builder پیدا کنیم و ازش استفاده کنیم.به همین راحتی مسئله حل شد.راه حل ۲) راه حل بعدی اینه که ویدجتامون رو از هم جدا کنیم که با کد نشونش میدم که راحت تر قابل فهم باشه :الان من اوومدم FloatingActionButton رو کلا بردم توی یه کلاس Stateless دیگه. نکته مهم اینجاس همونجور که قبلا گفتم این context که واسه متد build کلاس FloatingActionTest فرستاده میشه در واقع context خود ویدجت جاری ینی  FloatingActionTest هست. اگه بخوام widget tree رو بگم اینجور میشه الان :Test -&gt; Scaffold -&gt; FloatingActionTest -&gt; FloatingActionButton که همینجور که مشخصه Scaffold الان والد FloatingActionTest هست و ما میتونیم با پیمایش context اون ویدجت FloatingActionTest به Scaffold برسیم و SnackBar خودمون رو بدون مشکل نشون بدیم.برای اون مثال پیچیده تر هم میتونیم به این روش عمل کنیم :الان ترتیب ویدجت ها به این صورته که :Test -&gt; Scaffold -&gt; Center -&gt; Container -&gt; FlatButtonTest -&gt; FlatButton -&gt; Textکه الان context ای که به کلاس FlatButtonTest فرستاده میشه مال خود FlatButtonTest  هست و چون FlatButtonTest فرزند Container هست و Container هم فرزند Scaffold هست ما میتونیم با پیمایش اجداد context این FlatButtonTest به Scaffold برسیم و باز SnackBar رو نمایش بدیم.خب اینم از راه حلی برای رفع این مشکل.(لازم به ذکره که یه راه حل دیگه هم موجوده که استفاده از GloblKey هست اون راه حل چون به این بحث context مربوط نبود اونو اینجا بازش نکردم ولی بدونین که با کلید هم میشه این کار رو انجام داد و به state یه ویدجت با کلید خیلی راحت میشه دسترسی داشت)در آخر امیدوارم که تونسته باشم این مطلب رو بخوبی آموزش بدم و شما هم بتونین ازش استفاده بکنید توی کدهاتون. تا آموزش های بعد بدرود ...لینک کانال یوتوب من لطفا سر بزنید : https://www.youtube.com/c/FlutterStan</description>
                <category>Ali Hoseinpoor</category>
                <author>Ali Hoseinpoor</author>
                <pubDate>Thu, 19 Nov 2020 19:16:39 +0330</pubDate>
            </item>
                    <item>
                <title>Stateful یا Stateless مسئله این است ؟؟ ??</title>
                <link>https://virgool.io/flutter-community/stateful-%DB%8C%D8%A7-stateless-%D9%85%D8%B3%D8%A6%D9%84%D9%87-%D8%A7%DB%8C%D9%86-%D8%A7%D8%B3%D8%AA-dohju01u10or</link>
                <description>سلام دوستان امیدوارم حالتون خوب باشه.اول از همه چیز بگم که از هم اکنون میتوانید ویدیوهای مربوط به آموزش برنامه نویسی مخصوصا فلاتر و دارت رو در کانال یوتوب من دنبال کنید. لینکشم میزارم همین پایین :https://www.youtube.com/c/FlutterStanاین مقاله راجع به دوتا از مهم ترین و پایه ای ترین ویدجت ها توی فلاتر هست.پس اگه با فلاتر آشنایی دارین همراه من بیاین تا این دوتا ویدجت رو بررسی کنیم.قبل اینکه بخوام راجع به این دوتا ویدجت صحبت کنم لازمه که اصن بدونیم خود State چی هست اصن ؟؟State : استیت اطلاعاتی است که هنگام ساخت یک ویدجت به طور همزمان قابل خواندن است و ممکن است در طول عمر ویدجت(ینی تا زمانی که اون ویدجت در widget tree وجود داره و حافظه بهش اختصاص داده شده) تغییر کند. به زبون خیلی ساده بخوایم بگیم استیت میشه وضعیت و حالت اون ویدجت که ممکنه این وضعیت تغییر کنه.خب حالا که فهمیدیم استیت چی هست بریم سراغ کار اصلیه خودمون. اول با تعریف Stateless شروع میکنیم.Stateless :همینجور که از اسمش پیداس این ویدجت نیاز به استیت تغییر پذیر نداره. این ویدجت زمانی مفیده که اون بخش از رابط کاربری که میخوایم پیاده سازیش کنیم به چیز دیگری به جز اطلاعات پیکربندی(configuration) موجود در خود شی و BuildContext بستگی نداشته باشه. به عبارت ساده تر بخشی از رابط کاربری میشه، که به صورت ثابت هست و قرار نیس در اون بخش از رابط کاربری تغییری ایجاد بشه. برای بخش هایی که ممکنه به صورت پویا تغییر کنه از Stateful باید استفاده کنیم.این ویدجت به صورت immutable(غیر قابل تغییر) پیاده سازی شده است ینی هیچ کدام از خواص(properties) آن قابل تغییر به خودیه خود نیست مگه اینکه یه اتفاقی خارج از اون ویدجت باعث بشه دوباره اون ویدجت ساخته بشه. ینی باعث بشه که دوباره از اون ویدجت نمونه گیری(instantiation) بشه.این ویدجت(و همینطور Stateful) یک متدی داره به اسم build() که وظیفش نشون دادنه بخشی از رابط کاربری هست. ینی ما چیزی که قراره دیده بشه رو باید توی این متد بنویسیم.توی ویدجت Stateless این متد build فقط در ۳ حالت صدا زده میشه :۱) زمانی که برای اولین بار ویدجت در widget tree قرار میگیره۲)زمانی که ویدجت والدش پیکربندی(configuration) خودش رو تغییر میده۳)زمانی که یک InheritedWidget به تغییرات بستگی دارد که اینجور باعث میشه این ویدجت Stateless ما دوباره ساخته بشه و چون دوباره داره نمونه گیری میشه متد build() اون دوباره صدا زده میشهخب پس با این چیزایی که گفتم تا جایی که میتونید بخش های ثابت رابط کاربریتون رو حتما توی Stateless بزارین که پرفورمنس بهتری داشته باشید.(در آخر یه مثال میزنم که این پرفورمنس رو بهتر درک کنیم)در پایین هم یه مثال خیلی کوچیک از Stateless میزنم :class Test extends StatelessWidget {   
    const Test({ Key key }) : super(key: key);   
    @override  
    Widget build (BuildContext context) {    
            return Container ( color: Colors.red );   
     }
 }Stateful :این ویدجت برعکسه Stateless هس ینی این ویدجت به صورت mutable(قابل تغییر) پیاده سازی شده و خواص آن قابل تغییره. در این ویدجت استیت به صورت mutable(قابل تغییر) هست و میتونیم اون رو عوض کنیم.(ولی چجوری ؟؟)زمانی که فریمورک یه ویدجت Stateful میسازه در واقع یه استیت آبجکت(این استیت آبجکت مهمه چون باش کار داریم) میسازه. این آبجکت(یا همون شی) جاییه که تمام استیت تغییر پذیر اون ویدجت در اون نگه داری میشه.اگه نخوام با این تعریفا گیجتون کنم در واقع همون شی ای هست که دارای استیت هس همین.این ویدجت زمانی مفیده که اون بخش از رابط کاربری ما به صورت داینامیک داره تغییر میکنه. چون وقتی که رابط کاربری داره تغییر میکنه ینی داره تغییر حالت میده، و ما با ویدجت Stateful میتونیم استیت رو تغییر بدیم. ما با استفاده از متد setState() میتونیم استیت رو عوض کنیم اما نکته مهم اینجاس که چه اتفاقی میوفته وقتی ما این متد رو صدا میزنیم ؟؟جواب : متد build() که بالاتر هم دربارش توضیح دادم(کاراییش توی هر دو ویدجت به یک صورت است) وقتی که متد setState() صدا زده میشه دوباره از اول اجرا میشه ینی هر باری که ما استیت رو تغییر میدیم این متد build() از اول اجرا میشه و هر چی توی اون متد هس دوباره از اول نمونه گیری میشه.خب حالا اگه یکم فک کنیم میبینیم که شاید تو نگاه اول این تغییر استیت چیز باحالی به نظر بیاد ولی اگه قرار باشه که با هر بار تغییر استیت این متد از اول فراخوانی بشه چیز زیاد باحالی هم نیس و ممکنه خیلی واسه پرفورمنس برناممون بد بشه پس چکار باید بکنیم ؟؟اول اینکه از setState() در جای خودش استفاده کنین. ببینید setState() قراره حالت برنامه رو عوض کنه که توی رابط کاربری یه اتفاقی بیوفته مثلا یه CheckBox تیک بخوره برای عوض کردن متغییر هایی که اصلا هیچ تاثیری در ظاهر برنامه نمیزارن به هیچ عنوان از setState() استفاده نکنیندوم اینکه هیچ کس شما رو مجبور نکرده که همه ویدجتاتون رو توی یه کلاس بنویسین ??ببینید شما باید ویدجت هاتون رو از هم جدا کنین این کار چندین مزیت داره : یکی اینکه خوانایی کدتون میره بالاتر بعدیش اینکه وقتی میخواین تغییری توی کدتون بدین به خدا راحت تر میتونین این کارو بکنین??و مهم ترین مزیت اینه که فقط اون ویدجتی که نیاز به تغییر داره رو میتونین با setState() تغییر بدین(مثالش رو در آخر مقاله میزنم که ینی چی این کار)سوم اینکه استیت منیجمنت ها رو در آغوش بگیرین ??ایشاالله سعی میکنم تو یه مقاله دیگه به استیت منیجمنت ها هم بپردازم یه مثال ساده هم از Stateful این پایین میزنم :class Test extends StatefulWidget {   
      const Test ({ Key key }) : super(key: key);    

      @override 
       _TestState  createState() =&gt; _TestState(); 
}  
class _TestState extends State&lt;Test&gt; {   
       @override  
        Widget build (BuildContext context) {     
                  return Container(color: Colors.red );   
         }
 }خب بچه هایی که از سمت android میان در جریانن که activity ها یه چرخه حیات(life cycle) داشتن لازم به ذکره که Stateful هم واسه خودش یه چرخه حیات داره که الان میگم خدمتون به چه صورته :createState() : این متد زمانی صدا زده میشه که فریمورک میخواد اون ویدجت استیت فول رو شروع کنه بسازه سریعا این متد صدا زده میشه.این متد واسه ما یه استیت تغییر پذیر برای اون ویدجت درست میکنه(یه استیت آبجکت).@override         
_TestState  createState() =&gt; _TestState();mounted == ture :زمانی که BuildContext اختصاص داده میشه به اون استیت صدا زده میشه. اگه شما قبل از اینکه mounted = true بشه ()setState کنین به ارور میخورین. استیت آبجکت تا زمانی که فریمورک متد dispose() رو اجرا نکنه mounted میمونه.initState() :این اولین متدیه که بعد از اینکه ویدجت به صورت کامل ساخته شد صدا زده میشه(البته بعد از سازنده کلاس)این متد فقط و فقط یک بار صدا زده میشه و حتما هم باید ()super.initState را صدا بزند.و کلا این متد واسه مقدار دهی های اولیه خیلی خوبه یا مثلا درخواست زدن به سرور برای گرفتن یه سری داده.@override
initState() {
  super.initState();
  // your code here
}didChangeDependencies() :زمانی که تغییری توی وابستگی(dependency) استیت آبجکت ایجاد بشه این متد صدا زده میشه.این متد به صورت خودکار بعد از initState هم صدا زده میشه.@protected
@mustCallSuper
void didChangeDependencies() { }build() :این متد بخش رابط کاربری برنامه رو هندل میکنه و برای نمایش هست.(که قبلا راجع بهش حرف زدیم)این متد توسط فریمورک در زمان های مختلفی صدا زده میشه :۱) بعد از initState۲)بعد از didUpdateWidget (که پایین تر توضیحش میدم)۳)بعد از اینکه setState انجام شد۴)بعد از اینکه وابستگی(dependency) استیت آبجک تغییر کرد۵)بعد از غیر فعال کردن اون استیت آبجکت(deactivate) و دوباره درج کردن اون استیت آبجکت در درخت در مکانی دیگه@override  
Widget build ( BuildContext context) { 
    return Container(color: const Color( 0xFFFFE306 ) );  
 }didUpdateWidget() زمانی که پیکربندی(configuration) اون ویدجت تغییر کنه صدا زده میشه.زمانی که ویدجت پدر دوباره ساخته شه و درخواست بده که مکانش توی ویدجت تری آپدیت شه برای نمایش یه ویدجت جدید این متد فراخوانی میشه.میتونین این متد رو override کنین تا هر وقت ویدجت تغییر کرد بتونین خبر دار بشین و یه کاری انجام بدین.فریمورک همیشه بعد از این صدا زدن این متد به صورت خودکار متد build هم صدا میزنه پس نیاز نیس توی این متد از setState() استفاده کنید.@mustCallSuper
@protected
void didUpdateWidget(covariant T oldWidget) { }setState() :برای زمانی که شما مقدار استیت داخلی استیت آبجکت رو تغییر میدین.فراخوانی setState به فریمورک  اعلام می کند که وضعیت داخلی این شی به گونه ای تغییر کرده  که ممکنه  رابط کاربری در این زیر درخت را تحت تأثیر قرار بده ، که باعث می شه فریمورک اون استیت آبجکت رو دوباره build کنه.اگر فقط وضعیت را مستقیماً بدون فراخوانی setState تغییر بدهید ، ممکنه این فریمورک برنامه ریزی برای build نداشته باشد و ممکنه رابط کاربری این زیرشاخه به روز نشه تا وضعیت جدید را نشون بده.setState(() { _myState = newValue });deactivate() :زمانی که استیت از درخت پاک شه صدا زده میشه. اما ممکنه قبل از پایان تغییر فریم فعلی مجدداً درج شه.فریمورک این متد را هر زمان که این استیت آبجکت را از درخت خارج کند فراخوانی می کنه. در بعضی موارد ، فریمورک مجدداً استیت آبجکت را در قسمت دیگری از درخت قرار می دهد. اگر چنین اتفاقی بیفتد ، این فریمورک تضمین می کند که build را فراخوانی می کند تا به استیت آبجکت فرصتی برای سازگاری با مکان جدید خود در درخت بدهد.@protected
@mustCallSuper
void deactivate() { }dispose() :زمانی که آبجکت از درخت به صورت دائم پاک شه و حافظه اختصاص داده به اون آزاد شه صدا زده میشه.فریمورک این متد را زمانی فراخوانی می کنه که این استیت آبجکت هرگز دوباره ساخته نشه. پس از اینکه فریمورک dispose رو  فراخوانی کرد ،  استیت آبجکت unmounted در نظر گرفته می شود و ویژگی mounted برابر با false میشود. در این مرحله فراخوانی setState با خطا مواجه میشه. هیچ راهی برای بازپس گیری یک استیت آبجکت که dispose شده است وجود ندارد.زیر کلاس ها باید این متد را override کنند تا منابع ذخیره شده توسط این شی آزاد شود (به عنوان مثال ، جلوی انیمیشن های فعال را بگیرید).@protected
@mustCallSuper
void dispose() {}خب اینم از چرخه حیات Stateful حالا از هر کدوم از این ویدجتا یه سری مثال میزنم که با استفاده از یه کدوم از این دوتا ویدجت پیاده سازی شدن :Stateless Widgets :AlertDialog()Card()Container()Icon()FloatingActionButton()Text() , ......Stateful Widgets :AppBar()BottomNavigationBar()BottomSheet()CheckBox()Slider()TextField() , ....و اما اون نکته ای که راجع به پرفورمنس میخواستم بگم :ببینید اول از همه که سعی کنید یه استیت منیجمنت یاد بگیرین اما اگه هنوز شروع نکردین به یادگیریش و میخواین از setState استفاده کنین حتما ویدجت هاتون رو در کلاس های مختلف بنویسید.من یادمه اون اوایل که تازه شروع کرده بودم به یادگیری فلاتر یه برنامه ساختم که صفحه اولش حالت فروشگاهی مانند بود که یه سری محصول نمایش میداد و اون بالاش هم یه اسلایدر بود.این اسلایدر رو خودم کاستوم درست کرده بودم و هر بار که صفحش عوض میشد من setState میکردم و همه ویدجت هامم تو یه کلاس و یه متد build بود ینی هر بار که صفحه اسلایدر عوض میشد من یه بار کلا همه ویدجتا رو بیلد میکردم. بعد دیدم که اپم لگ میزنه حتی تو حالت ریلیز تا اینکه فهمیدم قضیه چیه.اوومدم تنها چیزی که تو صفحم داینامیک بود ینی همون اسلاید رو بردم تو یه کلاس Stateful جدا و اون کلاس اصلیم که همه ویدجتام توش بود رو Stateless کردم و setState رو فقط تو کلاس اسلایدر صدا میزدم که اینجور باعث میشد فقط متد build اون کلاس اسلایدر صدا زده بشه و فقط اون ویدجت دوباره ساخته شه نه همه ویدجت هام و مشکل لگ خوردن حل شد.پس ویدجت هاتون رو از هم جدا کنید و اون ویدجتی که قرار داینامیک باشه رو فقط Stateful کنید.??سخن آخر :در آخر تشکر میکنم که این مقاله رو خوندین و اگه جایی اشتباهی داشتم ازتون عذر میخوام و اگه دوس داشتین بهم اعلام کنید تا برطرفش کنم.و از دوستانی هم که مشوق این مقاله بودن تشکر میکنم.❤️❤️لینک کانال یوتوب من لطفا سر بزنید : https://www.youtube.com/c/FlutterStan</description>
                <category>Ali Hoseinpoor</category>
                <author>Ali Hoseinpoor</author>
                <pubDate>Sat, 14 Nov 2020 19:00:22 +0330</pubDate>
            </item>
                    <item>
                <title>معماری اندروید : از دالویک تا آرت</title>
                <link>https://virgool.io/devAndroid/from-dalvik-to-art-clofmpbfgzde</link>
                <description>سلام به همه دوستان.اول از همه چیز بگم که از هم اکنون میتوانید ویدیوهای مربوط به آموزش برنامه نویسی مخصوصا فلاتر و دارت رو در کانال یوتوب من دنبال کنید. لینکشم میزارم همین پایین :https://www.youtube.com/c/FlutterStanمن میخواستم که اولین مقالم رو در رابطه با فریمورک فلاتر که این روزا سر و صدای خیلی زیادی به پا کرده منتشر کنم ولی دیدم که تا زمانی که مفاهیم اصلی و اولیه رو بلد نباشیم نمیتونیم روی چیزی کاملا مسلط شیم. به همین خاطر تصمیم گرفتم که توی اولین مقاله ساختار و معماری سیستم عامل اندروید رو به صورت کامل توضیح بدم و بعدش بریم سراغ فلاتر :)خب اول با این شروع کنیم که اندروید چی هست و چه زمانی وارد بازار شده :اگه بخوام به صورت خلاصه بگم سیستم عامل اندروید توسط گوگل در تاریخ ۵ نوامبر ۲۰۰۷ به دنیا معرفی  شد. در ابتدا نام شرکت کوچکی با بنیان گذارانی به نام های اندی رابین ریچ  ماینر نیک سیرز و کریس وایت بود. این شرکت در حوزه طراحی و ساخت نرم  افزارهای موبایل و ساخت سیستم عامل جدیدی برای رقابت با سیستم عامل موفق آن  زمان یعنی سیمبین که در گوشی های نوکیا استفاده می شد فعالیت میکرد.امتیاز این شرکت در سال ۲۰۰۵ توسط شرکت قدرتمند گوگل با مبلغ  ۵۰ میلیون دلار خریداری شد و سیستم عامل اندروید را بر پایه هسته لینوکس طراحی نمود. تقریبا هم زمان با اندروید شرکت اپل موبایل های هوشمند خود با سیستم عامل جدید خود یعنی IOS به بازار عرضه کرد و این آغاز رقابتی بزرگ در  عرصه سیستم عامل های موبایل بود.خب حالا بریم سراغ اصل کار یعنی ساختار اندروید :ساختار سیستم عامل اندروید شامل چهار لایه است که این لایه ها عبارتند از:(البته این طرز لایه بندی اونقدر ها هم رسمی نیس چون بعضی جا ها جوری دیگر لایه بندی کردند ولی خب اصل همه لایه بندی ها یه چیز است مثل تفاوت OSI و TCP/IP در شبکه میمونه)ApplicationApplication FrameworkLibrariesLinux Kernelکه میتونین در شکل زیر این ساختار را مشاهده کنید :)خب به ترتیب از اولین لایه شروع میکنیم تا برسیم به تهش.لایه Application :این لایه قابل لمس ترین لایه برای ما کاربران گوشی های اندرویدی هست. و از طریق این لایه ما میتوانیم با گوشی خود ارتباط داشته باشیم.در واقع به زبان خیلی ساده تمام برنامه هایی که ما نصب میکنیم در این لایه است از مرورگر گرفته تا برنامه مخاطبین و ... لایه Application Framework :این لایه بیشتر به درد برنامه نویسان اندروید میخوره .میتوان گفت که تمام ویژگی های مجموعه سیستم عامل اندروید از طریق API های نوشته شده در زبان جاوا در دسترس برنامه نویسان است. در واقع این لایه به برنامه ما اجازه میدهد تا به سرویس های سطح بالا تر دسترسی پیدا کنند. حالا ممکنه بگین که این سرویس ها چیا هستن ؟Activity Manager :این سرویس همه جنبه های زمان حیات نرم افزار را کنترل می کند(life cycle) و هم چنین قابلیت navigation back-stack را فراهم میکند.اما navigation back-stack چیه ؟ شما اگه کمی کدنویسی اندروید کار کرده باشید حتما میدونین جابجایی بین صفحات(activity) چگونه است. کنترل این صفحات با ساختمان داده پشته(stack) پیاده سازی میشود. مثلا اگه تو برنامه دیجی کالا روی محصول کلیک کنیم میره تو یه صفحه دیگه و اینجا این صفحه جدید push میشه تو پشته وحالا اگه این صفحه رو ببندید این صفحه onDestroy() اش فراخوانی میشه و باعث میشه که از پشته pop بشه و اکتیویتی قبلی دوباره بیاد بالا. تمام این کارها وظیفه Activity Manager میباشد.Content Providers :این سرویس به برنامه ها این اجازه را می ده تا داده ها را انتشار و با برنامه های دیگر به اشتراک بگذارند.مثلا ما برنامه ای نوشتیم که در آن از داده های برنامه مخاطبین استفاده کرده ایم. خب این سرویس باعث شده که ما بتونیم همچین کار باحالی رو انجام بدیم.Resource Manager :این سرویس دسترسی به منابع غیر کد مانند رشته های محلی، گرافیک و فایل های طرح بندی را فراهم می کند. مثلا اگه ما تو کد زدن با جاوا واسه اندروید یک فایل واسه رشته هامون در نظر گرفته باشیم میتونیم با زدن کد R.id.string_name به اون رشته دسترسی داشته باشیم که اینجا R به همون resource های ما اشاره میکنه.Notifications Manager :این سرویس به برنامه ها این اجازه را می دهد تا اعلان ها و هشدار ها را به کاربر نمایش دهند حتی هنگام بسته بودن اپ.View System :از مجموعه توسعه پذیر  view ها برای طراحی ظاهر نرم افزار استفاده می کند.لایه Libraries : بسیاری از اجزای اصلی سیستم عامل اندروید و خدمات مانند ART(که جلوتر میگم چی هس) و ... از کد native ساخته شده اند که نیاز به کتابخانه های native در C و C ++ دارند. پلتفرم اندروید API های جاوا را فراهم می کند تا برخی از این کتابخانه های native را به برنامه ها بیفزایند. برای مثال، ما میتونیم OpenGL ES را از طریق API جاوا برای اضافه کردن پشتیبانی از نقاشی و دستکاری گرافیک های 2D و 3D در برنامه خودمون اضافه کنیم.چندتا از کتابخونه های مهم دیگه عبارتند از :Android.appAndroid.contentAndroid.openglAndroid.databaseAndroid.os Android.text Android.view Android.widget Android.webkit بخش Android Runtime : این بخش خیلی مهمیه ما برنامه اندروید رو به صورت کلاس های جاوایی مینویسیم ولی این کد جاوا چجوری روی گوشی اجرا میشه ؟ روال کار اینجوریه که ما کلاس های جاوایی خودمون رو مینویسیم بعد که ما کد جاوا رو کامپایل میکنیم به bytecode تبدیل میشن بر خلاف زبان هایی مثل c یا c++ که مستقیم به زبان ماشین تبدیل میشوند. خب حالا ما بایت کد را داریم( که اصطلاحا به بایت کد، کد قابل حمل هم میگن که از یک سری کدهای عددی فشرده، آدرس های عددی و ثابت ها تشکیل شده است) اما این کافی نیس برای اجرا روی اندروید خب پس باید چکار کرد ؟ قبل از اندروید ۵ گوگل از ماشین مجازی جاوایی به اسم دالویک برای اجرا برنامه ها استفاده میکرد اما سوال بعدی این است که دالویک چیست ؟دالویک : برخلاف ماشین های مجازی جاوا، که پشته ای هستند، دالویک ماشینی مبتنی بر معماری ریجستری است. ماشین‌های مجازی پشته‌ایی باید از دستورات برای بارگذاری داده‌های در پشته و اعمال تغییرات بر روی آن‌ها استفاده نماید بنابراین نسبت به ماشین‌های مبتنی بر رجیستر به دستورات بیشتری برای کد سطح بالاتر نیاز دارد ولی دستورات در ماشین رجیستری باید به صورت مبدأ و مقصد باشد که منجر به بزرگ شدن دستورات خواهد شد.این ماشین مجازی توسط دن برونستین جهت اجرا شدن برنامه ها بر روی دستگاه  های مختلف با منابع محدود ساخته شد. این ماشین بر روی دستگاه های موبایلی  استفاده می شود که منابع محدود با توان پردازش پایین و عمر باتری کم و حافظه اندک هستند ماشین مجازی دالویک فایل های با پسوند .dex اجرا می کند.خب تا اینجا ما یک بایت کد که حاصل از کامپایل کد جاوا هست و یک دالویک داریم که .dex رو اجرا میکنه.ابزارهایی وجود دارد که بایت کد ما را به فایلی با پسوند dex تبدیل میکند مانند dx و Jack که این ابزار ها توی خود اندروید هستن. حالا که کد جاوا ما به dex تبدیل شد این کد به دالویک داده میشه و دالویک اون رو اجرا میکنه به همین سادگی :)به این کارهایی که گفتم تا در نهایت به کدی برسیم که دالویک بتونه اون رو اجرا کنه JIT compile( اختصار شده Just In Time) میگن. یعنی هر باری که ما یک اپ را روی گوشی باز میکنیم تمام کارهای گفته شده در بالا انجام میشود واسه همین بش میگن Just In Time که یعنی همون موقعی که ما اپ رو باز میکنیم کدهای ما توسط دالویک به زبان ماشین تبدیل میشه تاکید میکنم که درست در همون موقع.ولی مشکل این دالویک چیه ؟ کد تولیدی ما باید در هر بار اجرا روی دستگاه، از یک ران‌تایم(همون فرایند دالویک) عبور کرده و  پس‌از تفسیر، اجرا شود. این روش بهینه نیست و در هر بار اجرا، کل منابع سخت افزاری را درگیر می‌کند، ولی در عوض امکان تولید آسان نرم‌افزار برای دستگاه‌ها و معماری‌های مختلف را می‌دهد.ولی گوگل که دست رو دست نمیزاره واسه همین توی نسخه ۴.۴ ،اندروید آرت(ART) رو معرفی کرد و به صورت آزمایشی در کنار دالویک در گوشی ها استفاده کرد، به گونه ای که کاربر حق انتخاب بین این دو را داشت.اما در نسخه ۵ اندروید اون رو دیگه جایگزین دالویک کرد.نحوه عملکرد آرت : آرت به روشی کاملاً متفاوت از دالویک، نرم افزارها رو اجرا می‌کند.در آرت بار اولی که یک نرم‌افزار  نصب می‌شود، کد بایتی (ByteCode) آن را به کد ماشین (MachineCode) تبدیل  می‌کند تا در واقع آن نرم‌افزار به یک نرم افزار native تبدیل شود.با این روش جدید که کامپایل جلوتر از زمان (Ahead-Of-Time یا AOT) نام دارد، نیاز به فعال‌سازی هر باره یک ماشین مجازی یا یک کد مفسر(همون کار دالویک که هر بار باید کد ما به dex تبدیل میشد تا دالویک اون رو اجرا کنه) از بین خواهد رفت و اجرای اپلیکیشن‌ها  بسیار سریع‌تر خواهد شد. طبق بررسی‌های انجام شده، زمان بازشدن نرم‌افزارها به‌طور میانگین به نصف کاهش یافته، زمان پاسخگویی دستگاه سریع‌تر شده و عمر  باتری آن نیز بالا رفته‌است.مزایای آرت : استفاده از AOT و JIT به صورت تلفیقی همچنین دارای garbage collection بهبود یافته تر و مواردی دیگر مانند پشتیبانی از اشکال زدایی بهتر و تشخیص بهتر استثنا ها و گزارش دهی بهتر crash ها.معایب آرت : افزایش نسبی زمان اولیه برای نصب هر نرم‌افزار و همچنین افزایش فضای مورد نیاز برای ذخیره‌سازی نرم‌افزارها.JIT vs AOT :پس به صورت خلاصه JIT شد اینکه هر بار که ما اپی رو اجرا میکنیم یک ماشین مجازی دالویک اجرا میشه و کد جاوا ما تبدیل میشه به .dex و بعد تازه کد ما اجرا میشه ولی در AOT کد ما قبل از اجرا به زبان ماشین تبدیل میشه و دیگر برای هر بار اجرا نیاز به ماشین مجازی برای تفسیر کد نداریم.لایه Linux Kernel : کرنل، هسته سیستم عامل می باشد که تمام منابع سیستم را مانند پردازنده،  حافظه و ... را به برنامه های دیگر اختصاص می دهد. سورس کد کرنل لینوکس  شامل بیش از 21 میلیون خط کد و یکی از پر استفاده ترین سورس کد ها در دنیا  می باشد.کرنل لینوکس یک هسته سیستم عامل اپن سورس است که در سال 1991 توسط  لینوس توروالدز ساخته شد و پس از او هسته لینوکس به کمک توسعه دهندگان دیگر  در سراسر جهان پیشرفت داده شد. از وظایف کرنل لینوکس می توان به موارد زیر  اشاره کری: ذخیره سازی اطلاعات:  حافظه  با دسترسی تصادفی (رم) به منظور خواندن ونوشتن متغیر وداده ها در حافظه و  دسترسی به حافظه دائمی برای ذخیره سازی و بازیابی اطلاعات بر روی ابزار های  ذخیره سازی دائمی مانند هارد دیسک مدیریت ابزار ها: مدیریت ابزار های خارجی مانند : USB، دوربین، بلوتوس، وای فای زمان بندی کارها: تقسیم کردن زمان پردازشگر بین پردازش های مختلف و اولویت بندی کردن کار ها برای پردازشو ...پایه و اساس پلتفرم اندروید هسته لینوکس است. به عنوان مثال، ART به هسته لینوکس برای ویژگی هایی مانند نخ ها (threads) و مدیریت سطح پایین حافظه وابسته است.خب دوستان این بود معرفی ساختار اندروید امیدوارم که به دردتون خورده باشه و تونسته باشم به اطلاعاتتون چیزی رو اضافه کنم.با مقاله های بعدی من در رابطه با فلاتر همراه باشد(البته اونم از بیس شروع میکنیم.):)لینک کانال یوتوب من لطفا سر بزنید : https://www.youtube.com/c/FlutterStan</description>
                <category>Ali Hoseinpoor</category>
                <author>Ali Hoseinpoor</author>
                <pubDate>Mon, 03 Jun 2019 05:20:42 +0430</pubDate>
            </item>
            </channel>
</rss>