<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>پست‌های انتشارات Persian Flutter</title>
        <link>https://virgool.io/flutter-community/feed</link>
        <description>اجتماع فلاتر برای برنامه نویسان فلاتر. مجموعه ای از آموزش ها ، رویدادها و تجربیات برنامه نویسی فلاتر.</description>
        <language>fa</language>
        <pubDate>2026-06-15 23:03:19</pubDate>
        <image>
            <url>https://files.virgool.io/upload/publication/klgwjc3aj9cf/hdk1qu.jpeg</url>
            <title>Persian Flutter</title>
            <link>https://virgool.io/flutter-community</link>
        </image>

                    <item>
                <title>راه‌اندازی Mirror برای Flutter/Dart Pub</title>
                <link>https://virgool.io/flutter-community/%D8%B1%D8%A7%D9%87-%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C-mirror-%D8%A8%D8%B1%D8%A7%DB%8C-flutterdart-pub-a2yygoj8jayu</link>
                <description>مسئله چیست؟در فرآیند توسعه با Flutter و Dart، استفاده از پکیجها و بستهها اهمیت زیادی دارد. با این حال، در برخی مواقع ممکن است به دلیل مشکلات شبکه , پروکسی دسترسی به pub.dev (منبع اصلی پکیجها) با اختلال مواجه شود. این مشکلات میتواند ناشی از محدودیتهای اینترنتی، فایروالها یا حتی سرورهای غیرفعال باشد.قطعا با وجود ابزار های دور زدن فیلترینگ تجربه خطاهای ناموفق عدم دانلود پکیج های فلاتری را داشتید و حسابی کلافه شدید خواستید خودتون رو از بالا پرت کنید پایین.یک راه حل کاربردی و امتحان شده برای زمانی که نشت آی پی ایران دارید.اگر هنگام flutter pub get با خطاهایی مثل Authentication error (403) یا مشکلات شبکه روبهرو میشوید، استفاده از Mirror قابلاعتماد میتواند دریافت وابستگیها را پایدارتر کند.راهکار بهصورت خلاصهاستفاده از یک Mirror برای pub به ما این امکان را میدهد تا به جای مراجعه به pub.dev، از یک منبع محلی یا نزدیکتر استفاده کنیم. برای این کار تنها نیاز به یک سوییچ یکفرمانی داریم که به ما اجازه میدهد تا بهراحتی به Mirror متصل شویم.سوییچ سریع بین آینه و منبع رسمی را تنها با یک دستور خواهید داشت.macOS + zsh — پیکربندی دائمی با توضیحات مرحلهبهمرحله1) بررسی شل فعالدر macOS پیشفرض شل zsh است. برای اطمینان:echo $SHELLخروجی معمول: /bin/zsh2) پشتیبانگیری از تنظیمات فعلی شلقبل از هر تغییری از ~/.zshrc بکاپ بگیرید:cp ~/.zshrc ~/.zshrc.bak-$(date +%Y%m%d)3) باز کردن فایل تنظیمات ~/.zshrcبا ویرایشگر دلخواه باز کنید. مثلا با TextEdit:open -e ~/.zshrcیا اگر VS Code دارید:code ~/.zshrc4) افزودن Mirror + سوییچ سریعاین بلوک را عیناً به انتهای فایل اضافه کنید:# &gt;&gt;&gt; PUB MIRROR SWITCH &gt;&gt;&gt;

export PUB_HOSTED_URL=&quot;https://pub.flutter-io.cn&quot;
export FLUTTER_STORAGE_BASE_URL=&quot;https://storage.flutter-io.cn&quot;

alias pub-cn=&#039;export PUB_HOSTED_URL=&quot;https://pub.flutter-io.cn&quot;; export FLUTTER_STORAGE_BASE_URL=&quot;https://storage.flutter-io.cn&quot;; echo &quot;Using mirror: $PUB_HOSTED_URL&quot;&#039;
alias pub-dev=&#039;export PUB_HOSTED_URL=&quot;https://pub.dev&quot;; export FLUTTER_STORAGE_BASE_URL=&quot;https://storage.googleapis.com&quot;; echo &quot;Using upstream: $PUB_HOSTED_URL&quot;&#039;
# &lt;&lt;&lt; PUB MIRROR SWITCH &lt;&lt;&lt;توضیح خطوط:PUB_HOSTED_URL: مبدأ دریافت پکیجهای Dart/Flutter.FLUTTER_STORAGE_BASE_URL: مبدأ دریافت باینریها/آرتیفکتهای Flutter (مانند engine و SDK).pub-cn: در همان شل فعلی سوییچ به Mirror.pub-dev: در همان شل فعلی سوییچ به مبدأ رسمی.5) بارگذاری مجدد تنظیماتبرای اعمال فوری تغییرات:source ~/.zshrc6) راستیآزمایی مبدأهاecho $PUB_HOSTED_URL 
echo $FLUTTER_STORAGE_BASE_URLانتظار در حالت Mirror:https://pub.flutter-io.cnhttps://storage.flutter-io.cn7) استفادهٔ روزمرهاجرای عادی (پس از باز شدن شل جدید، Mirror پیشفرض است):flutter pub getسوییچ صریح به Mirror در همین ترمینال:pub-cn
flutter pub getسوییچ به منبع رسمی در همین ترمینال:pub-dev
flutter pub get8) نکات و رفع اشکال سریعاگر خطا ادامه داشت:flutter clean سپس flutter pub getسوییچ بین Mirror/رسمی: pub-cn یا pub-devبررسی دسترسی ها:curl -I https://pub.dev/api/packages/archive/advisories
curl -I https://pub.flutter-io.cn/api/packages/archive/advisoriesاین تنظیمات تضمین «صفر خطا» نیست؛ آینهها ممکن است گهگاهی کند یا در دسترس نباشند.9) بازگردانی/حذف تغییرات(اختیاری)فایل ~/.zshrc را باز کرده و بلوک افزودهشده را پاک/کامنت کنید، سپس:source ~/.zshrcدر صورت نیاز میتوانید بکاپ را برگردانید:mv ~/.zshrc.bak-YYYYMMDD ~/.zshrc
source ~/.zshrcWindows — پیکربندی دائمی + سوییچ سریعمیتوانید از PowerShell (توصیهشده) یا CMD استفاده کنید. در هر دو حالت، برای دائمیکردن، متغیرهای محیطی سطح کاربر را تنظیم میکنیم.گزینه PowerShell (پیشنهادی)1) تنظیم دائمی (User) به آینهPowerShell را بهصورت عادی باز کنید (بدون نیاز به ادمین برای سطح کاربر):setx PUB_HOSTED_URL &quot;https://pub.flutter-io.cn&quot;
setx FLUTTER_STORAGE_BASE_URL &quot;https://storage.flutter-io.cn&quot;نکته: اثر setx در نشست فعلی دیده نمیشود؛ یک PowerShell جدید باز کنید. اگر میخواهید هماکنون هم اعمال شود:$env:PUB_HOSTED_URL = &quot;https://pub.flutter-io.cn&quot;
$env:FLUTTER_STORAGE_BASE_URL = &quot;https://storage.flutter-io.cn&quot;2) افزودن توابع سوییچ سریع به پروفایل PowerShellساخت/باز کردن پروفایل:New-Item -Type File -Path $PROFILE -Force
notepad $PROFILEاین توابع را در پروفایل بچسبانید تا هر بار با باز شدن PowerShell در دسترس باشند:
function Use-PubMirror {
  $env:PUB_HOSTED_URL = &quot;https://pub.flutter-io.cn&quot;
  $env:FLUTTER_STORAGE_BASE_URL = &quot;https://storage.flutter-io.cn&quot;
  Write-Host &quot;Using mirror: $env:PUB_HOSTED_URL&quot;
}

function Use-PubUpstream {
  $env:PUB_HOSTED_URL = &quot;https://pub.dev&quot;
  $env:FLUTTER_STORAGE_BASE_URL = &quot;https://storage.googleapis.com&quot;
  Write-Host &quot;Using upstream: $env:PUB_HOSTED_URL&quot;
}

function Set-PubMirrorPermanent {
  setx PUB_HOSTED_URL &quot;https://pub.flutter-io.cn&quot; | Out-Null
  setx FLUTTER_STORAGE_BASE_URL &quot;https://storage.flutter-io.cn&quot; | Out-Null
  Write-Host &quot;User env set to MIRROR. Open a new PowerShell.&quot;
}

function Set-PubUpstreamPermanent {
  setx PUB_HOSTED_URL &quot;https://pub.dev&quot; | Out-Null
  setx FLUTTER_STORAGE_BASE_URL &quot;https://storage.googleapis.com&quot; | Out-Null
  Write-Host &quot;User env set to UPSTREAM. Open a new PowerShell.&quot;
}بارگذاری مجدد پروفایل (یا باز کردن پنجره جدید):. $PROFILE3) راستیآزماییدر نشست فعلی:$env:PUB_HOSTED_URL
$env:FLUTTER_STORAGE_BASE_URLمقدار دائمی (User):[Environment]::GetEnvironmentVariable(&quot;PUB_HOSTED_URL&quot;,&quot;User&quot;)
[Environment]::GetEnvironmentVariable(&quot;FLUTTER_STORAGE_BASE_URL&quot;,&quot;User&quot;)4) استفادهٔ روزمرهflutter pub get        # با مقادیر دائمی
Use-PubMirror         # سوییچ فوری به میرور در همین نشست
Use-PubUpstream       # سوییچ فوری به منبع رسمی5) بازگردانی/حذف مقادیر دائمی(اختیاری)[Environment]::SetEnvironmentVariable(&quot;PUB_HOSTED_URL&quot;, $null, &quot;User&quot;)
[Environment]::SetEnvironmentVariable(&quot;FLUTTER_STORAGE_BASE_URL&quot;, $null, &quot;User&quot;)سپس یک PowerShell جدید باز کنید.گزینه Command Prompt &#40;CMD&#41;1) تنظیم دائمی (User) به Mirrorsetx PUB_HOSTED_URL &quot;https://pub.flutter-io.cn&quot;
setx FLUTTER_STORAGE_BASE_URL &quot;https://storage.flutter-io.cn&quot;یک CMD جدید باز کنید تا اثر کند.2) تنظیم موقت در همان نشستset PUB_HOSTED_URL=https://pub.flutter-io.cn
set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn3) سوییچ سریع با فایلهای batch (اختیاری)یک فایل به نام pub-cn.cmd بسازید و محتوای زیر را در آن قرار دهید:@echo off
set PUB_HOSTED_URL=https://pub.flutter-io.cn
set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
echo Using mirror: %PUB_HOSTED_URL%یک فایل به نام pub-dev.cmd بسازید:@echo off
set PUB_HOSTED_URL=https://pub.dev
set FLUTTER_STORAGE_BASE_URL=https://storage.googleapis.com
echo Using upstream: %PUB_HOSTED_URL%اگر مسیر این فایلها در PATH باشد، میتوانید در همان CMD اجرا کنید:pub-cn
flutter pub get

pub-dev
flutter pub get4) راستیآزماییecho %PUB_HOSTED_URL%
echo %FLUTTER_STORAGE_BASE_URL%5) بازگردانی/حذف مقادیر دائمی(اختیاری)setx حذف مستقیم ندارد. از PowerShell استفاده کنید:[Environment]::SetEnvironmentVariable(&quot;PUB_HOSTED_URL&quot;, $null, &quot;User&quot;)
[Environment]::SetEnvironmentVariable(&quot;FLUTTER_STORAGE_BASE_URL&quot;, $null, &quot;User&quot;)سپس یک CMD جدید باز کنید.خوشحالم که تا اینجا مقاله را خواندی و امیدوارم کاربردی بوده باشد.نکات مهماین روش پایداری را بهتر میکند، اما تضمین ۱۰۰٪ نیست. اگر آینهها کند شدند، به منبع رسمی برگردید.در CI/CD بهتر است متغیرها را بهطور صریح در محیط همان Job تنظیم کنید تا قابل تکرار باشد.پس از تغییر مقادیر دائمی با setx، همیشه ترمینال جدید باز کنید.اگر پشت پراکسی هستید، تنظیمات پراکسی سیستم/ترمینال را بررسی کنید.می دونید که با ❤️ کردن باعث انگیزه می شوید. منتظر کامنت های شما هستم.حتما تجربه های خودتون را در کامیونیتی فلاتر فارسی به اشتراک بگذارید.</description>
                <category>Persian Flutter</category>
                <author>سپهر تابعیان</author>
                <pubDate>Sat, 25 Apr 2026 05:50:36 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش کامل Drift در Flutter: از شروع تا مقابله با چالش‌ها</title>
                <link>https://virgool.io/flutter-community/drift-q4ctz5wmwqqk</link>
                <description>معرفی کوتاه Drift به عنوان یک ORM قوی در Flutter.اهمیت مدیریت دیتابیسهای محلی در توسعه اپلیکیشنها.بیان مختصر اهداف مقاله و مزایای استفاده از Drift.فریمورک قدرتمند و متن باز گوگل به نام فلاتر و توسعه داده شده با زبان دارت ، به توسعه دهندگان این امکان را میدهد که با یک بار نوشتن کد، اپلیکیشن های زیبایی برای پلتفرم های مختلف مانند اندروید، iOS، وب و دسکتاپ ایجاد کنند. Drift چیست؟Drift یک ORM (Object-Relational Mapping) قدرتمند برای کار با پایگاه داده SQLite در اپلیکیشن های Flutter و Dart است. این کتابخانه به توسعه دهندگان اجازه میدهد تا با استفاده از کدهای Dart به سادگی کوئری های SQL را نوشته و مدیریت کنند، بدون نیاز به نوشتن مستقیم SQL. Drift امکاناتی همچون کوئری های قابل ترکیب، واکنش گرایی (Reactive) در دیتابیس، و تولید خودکار کدهای مرتبط با جداول را فراهم میکند. به طور خلاصه، Drift ارتباط بین اپلیکیشن و پایگاه داده را بسیار ساده و منعطف میسازد و امکان مدیریت دادهها در یک ساختار منظم و بهینه را فراهم میکند.یکی از مزایای کلیدی Drift، سازگاری با پلتفرم های مختلف است؛ از ویندوز و اندروید گرفته تا وب، این کتابخانه به توسعه دهندگان کمک میکند تا بدون نگرانی از جزئیات پیچیده هر پلتفرم، به راحتی دیتابیس خود را مدیریت کنند.برای شروع استفاده از Drift در پروژه Flutter، ابتدا باید این کتابخانه را نصب و پیکربندی کنید. Drift به راحتی با SQLite ادغام میشود و امکاناتی مانند کوئری نویسی ساده، مدیریت جداول و تعامل واکنشگرا با دیتابیس را فراهم می آورد. در این بخش، مراحل نصب و تنظیمات اولیه Drift را بررسی میکنیم.1. اضافه کردن کتابخانههای مورد نیازابتدا باید کتابخانه های Drift و SQLite را به فایل pubspec.yaml پروژه اضافه کنید. این کار را با قرار دادن خطوط زیر انجام دهید:dependencies:
  drift: ^2.20.2
  drift_flutter: ^0.2.0
  sqlite3_flutter_libs: ^0.7.0
dev_dependencies:
  drift_dev: ^2.20.2
  build_runner: ^2.3.0drift: هسته اصلی Drift است که امکان نوشتن کوئریهای دیتابیس و مدیریت دادهها را فراهم میکند.drift_flutter: برای استفاده Drift در Flutter، به این پکیج نیاز داریم.sqlite3_flutter_libs: شامل باینری های SQLite برای استفاده در اندروید و iOS است.drift_dev و build_runner: این کتابخانه ها برای تولید خودکار کدهای مربوط به جداول و کوئریها استفاده میشوند.2. تنظیمات دیتابیسپس از نصب پکیجها، باید یک کلاس برای مدیریت اتصال به دیتابیس ایجاد کنید. این کلاس وظیفه باز کردن و مدیریت دیتابیس را بر عهده دارد.یک فایل Dart جدید به نام my_database.dart بسازید (نام اختیاری) و کد زیر را در آن وارد کنید:import &#039;dart:io&#039;;
import &#039;package:drift/drift.dart&#039;;
import &#039;package:drift/native.dart&#039;;
import &#039;package:path_provider/path_provider.dart&#039;;
import &#039;package:path/path.dart&#039; as p;
part &#039;my_database.g.dart&#039;; این خط کد موقع وارد کردن با خطا هست که جلوتر حلش میکنیم
@DriftDatabase(
 tables: [Master, Details], // جداول پایگاه داده
)
class MyDatabase extends _$MyDatabase {
  MyDatabase() : super(_openConnection());
@override
  int get schemaVersion =&gt; 1;
}
LazyDatabase _openConnection() {
  return LazyDatabase(() async {
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File&#40;p.join(dbFolder.path, &#039;my_db.sqlite&#039;&#41;);
    return NativeDatabase(file);
  });
}3. ساخت جداول دیتابیسدر Drift، جداول دیتابیس با استفاده از کلاسهای Dart تعریف میشوند. برای مثال، یک جدول به نام Master به شکل زیر تعریف میشود:class Master extends Table { IntColumn get id =&gt; integer().autoIncrement()(); TextColumn get title =&gt; text().withLength(min: 1, max: 50)(); IntColumn get processID =&gt; integer()(); }پس از تعریف جداول، با اجرای دستور زیر، کدهای مورد نیاز تولید میشوند ( بخصوص my_database.g.dart):flutter pub run build_runner build4. اجرای Driftپس از تنظیمات اولیه، دیتابیس شما آماده استفاده است. حال میتوانید از کوئریهای واکنشگرا و ساده Drif برای تعامل با داده ها در اپلیکیشن Flutter خود استفاده کنید.خب خب حالا بحث شیرین چالش و باگ هااستفاده از Drift در اپلیکیشنهای Flutter که برای پلتفرمهای مختلفی همچون وب، ویندوز، اندروید و iOS طراحی شدهاند، با چالشها و باگ هایی همراه است. یکی از مهمترین چالش ها، اجرای Drift در پلتفرم وب است، زیرا وب برخلاف پلتفرم های دیگر مانند اندروید یا ویندوز، از کتابخانه های محلی (native libraries) مانند dart:ffi برای دسترسی به SQLite پشتیبانی نمیکند. در ادامه به برخی از مهمترین چالش های استفاده از Drift در وب و مشکلات احتمالی آن میپردازیم:عدم پشتیبانی از dart:ffi در وبیکی از چالش های اساسی اجرای Drift در وب، عدم پشتیبانی مرورگرها از کتابخانه های مبتنی بر FFI (Foreign Function Interface) است. Drift از SQLite استفاده میکند که نیازمند تعامل مستقیم با سیستم عامل است، اما این امکان در وب وجود ندارد. به همین دلیل، برای اجرای Drift در وب، باید از drift/web(منسوخ شده) و IndexedDB به جای SQLite استفاده شود.راهحل: در هنگام پیکربندی Drift در اپلیکیشن وب، به جای استفاده از NativeDatabase، از WebDatabase استفاده میکنیم که به IndexedDB، یک دیتابیس داخلی در مرورگرها، متصل میشود:LazyDatabase _openConnection() {
  if (kIsWeb) {
    return LazyDatabase(() async {
      return WebDatabase(&#039;app_db&#039;);
    });
  } else {
// استفاده از NativeDatabase برای پلتفرمهای موبایل و دسکتاپ
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File&#40;p.join(dbFolder.path, &#039;app_db.sqlite&#039;&#41;);
    return NativeDatabase(file);
  }
}محدودیتهای IndexedDBIndexedDB که Drift از آن در وب استفاده میکند، به عنوان یک دیتابیس مرورگر عمل میکند، اما دارای محدودیت هایی است:محدودیت حجم داده: در برخی مرورگرها (مانند Safari)، حجم دادههای ذخیره شده در IndexedDB محدود است و اگر برنامه شما حجم زیادی از داده ها را ذخیره کند، ممکن است به محدودیت برخورد کنید.سرعت پایین تر نسبت به SQLite: IndexedDB به طور کلی از لحاظ عملکرد، به ویژه در حجم بالای داده، کندتر از SQLite است. این میتواند در اپلیکیشنهایی که حجم زیادی از دادهها را پردازش میکنند، مشکل ساز شود.مشکلات همگام سازی داده بین پلتفرم هایکی دیگر از چالش ها در اجرای Drift در وب، همگامسازی داده ها بین پلتفرم های مختلف است. اگر اپلیکیشن شما در وب، موبایل و دسکتاپ اجرا شود، داده ها به طور پیشفرض در هر پلتفرم جداگانه ذخیره میشوند. این مسئله میتواند مشکل همگام سازی دادهها بین نسخه های مختلف اپلیکیشن را به همراه داشته باشد.راه حل احتمالی: استفاده از راهکارهایی مانند APIهای سمت سرور یا Firebase برای همگامسازی داده ها بین پلتفرم های مختلف میتواند این مشکل را حل کند.خطاهای سازگاری با مرورگرهای مختلفعملکرد Drift در وب ممکن است در برخی مرورگرها دچار مشکل شود. به عنوان مثال، نسخه های قدیمی مرورگرهایی مانند Safari یا Internet Explorer از IndexedDB به درستی پشتیبانی نمیکنند و ممکن است برنامه شما با خطا مواجه شود.راه حل: تست کامل برنامه در مرورگرهای مختلف و استفاده از polyfillها یا راهکارهای جایگزین برای رفع مشکلات سازگاری میتواند کمک کننده باشد.مدیریت ایمنی و امنیت دادههادر وب، امنیت و حریم خصوصی داده ها از اهمیت ویژهای برخوردار است. IndexedDB به صورت محلی در مرورگر کاربر ذخیره میشود و ممکن است داده های حساس اپلیکیشن شما در معرض خطر قرار بگیرد.راه حل: استفاده از رمزنگاری برای محافظت از داده های حساس و پیادهسازی مکانیسم های امنیتی سمت سرور برای مدیریت بهتر داده ها.محدودیت های مرورگر در توسعه وب اپلیکیشن هامرورگرها به دلیل محدودیت های ذاتی که دارند، امکان اجرای کدهای سنگین و عملیات پیچیده را به همان شکلی که در دسکتاپ یا موبایل وجود دارد، نمیدهند. این مسئله میتواند برای Drift، که با دیتابیس های حجیم سر و کار دارد، محدودیتهایی ایجاد کند._____________________________________________________________________________________________در کل، اگرچه Drift ابزار قدرتمندی برای مدیریت دیتابیسها است، اما در پلتفرم وب به دلیل محدودیتهای موجود باید با دقت و تغییراتی خاص مورد استفاده قرار گیرد.قربان شما ; سید سعید غفوری</description>
                <category>Persian Flutter</category>
                <author>Saeed Ghafoori</author>
                <pubDate>Sat, 12 Oct 2024 11:19:10 +0330</pubDate>
            </item>
                    <item>
                <title>مدیریت دکمه بک مرورگر در فلاتر با استفاده از GoRouter</title>
                <link>https://virgool.io/flutter-community/%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA-%D8%AF%DA%A9%D9%85%D9%87-%D8%A8%DA%A9-%D9%85%D8%B1%D9%88%D8%B1%DA%AF%D8%B1-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-gorouter-fnrqmmqlooa2</link>
                <description>مدیریت دکمه بک مرورگر در فلاتر با استفاده از GoRouterدر انتخاب ابزار مدیریت روتینگ در فلاتر، گزینههای زیادی وجود دارد. اما زمانی که به محبوبترین پکیجها نگاه میکنیم، GoRouter به عنوان یکی از بهترینها شناخته میشود. با وجود مزایای بسیار، GoRouter نیز با چالشهایی مواجه است و یکی از مشکلات قابل توجه آن، مدیریت دکمه بک مرورگر است. در این مقاله، به بررسی این مشکل و ارائه راهحل آن خواهیم پرداخت.مشکل:در یک برنامه معمولی، ناوبری بین صفحات به صورت پیوسته و منظم است. این ساختار اطمینان میدهد که کاربران روند خاصی را دنبال کنند. اما استفاده از دکمه بک مرورگر میتواند این روند را مختل کرده و باعث شود که برنامه به صورت غیرمنتظرهای رفتار کند یا توالی ناوبری مورد نظر از بین برود.علاوه بر این، مدیریت این مسئله در چندین پلتفرم پیچیدگی دیگری را اضافه میکند. مهم است که کد به طور یکپارچه در تمام پلتفرمها، از جمله وب، بدون مشکل کار کند. هدف داشتن راهحلی است که نه تنها دکمه بک مرورگر را در وب مدیریت کند بلکه در سایر پلتفرمها نیز به درستی عمل کند.راهحل:گام 1: ایجاد یک فایل جاوا اسکریپت برای محدود کردن دکمه بک به صفحه اول PWAاولین قدم، ایجاد یک فایل جاوا اسکریپت است که عملکرد دکمه بک را به صفحه اول برنامه وب پیشرفته (PWA) محدود کند.نام فایل:  clear_browser_history.jsfunction clearBrowserHistory(redirectUrl) {
  // جایگزین کردن وضعیت فعلی با وضعیت جدید
  history.replaceState(null, document.title, location.pathname);
  // پاک کردن تاریخچه جلو
   = function(event) {
    // هدایت به یک صفحه خاص
    .href = redirectUrl;
  };
}گام 2: درج فایل جاوا اسکریپت در HTMLدر مرحله بعد، باید این فایل جاوا اسکریپت را به فایل HTML خود اضافه کنید. خط کد زیر را درست بالای تگ &lt;/body&gt; اضافه کنید.&lt;script src=&amp;quotclear_browser_history.js&amp;quot&gt;گام 3: کپی فایل جاوا اسکریپتاطمینان حاصل کنید که فایل clear_browser_history.js در همان دایرکتوری که فایل HTML شما قرار دارد، کپی شده است.گام 4: وارد کردن پکیج js و فراخوانی تابع جاوا اسکریپت در Dartقبل از فراخوانی تابع جاوا اسکریپت، باید پکیج js را به فایل pubspec.yaml خود اضافه کنید:dependencies:
  flutter:
   sdk: flutter
  js: ^0.6.3سپس، در کد Dart خود، پکیج js را وارد کرده و تابع را فراخوانی کنید:میتوانید دکمه بک مرورگر را شرطی کنید که در حالت دیباگ و یا رلیز به کدام صفحه برگردد.js.context.callMethod(&#039;clearBrowserHistory&#039;, [
  kDebugMode
   ? &amp;quothttp://localhost:80&amp;quot // میتوانید پورت محلی خود را تنظیم کنید
      : &amp;quothttps://ea2.dev&amp;quot  //  میتوانید صفحه وب دلخواه خود را تنظیم کنید تا دکمه بک مرورگر به آن برگردد
]);گام 5: ایجاد کلاس واسط برای مدیریت محیطهای وب و نیتیواز آنجا که ما از پکیج js برای اجرای کد جاوا اسکریپت استفاده میکنیم، باید اطمینان حاصل کنیم که ایمپورت بسته به پلتفرم به صورت شرطی انجام میشود. ما یک کلاس واسط ایجاد خواهیم کرد که بسته به اینکه در محیط وب یا نیتیو اجرا میشود، رفتار متفاوتی داشته باشد. این نیاز به سه فایل برای پیادهسازی دارد:نام فایل: multiPlatforms.dartimport &#039;native.dart&#039; if (dart.library.html) &#039;web.dart&#039; as switch_value;
class MultiPlatforms {
  MultiPlatforms._();
static Future&lt;void&gt; clearBrowserHistory() {
    return switch_value.clearBrowserHistory();
  }
}نام فایل: web.dartimport &#039;dart:js&#039; as js;
import &#039;package:flutter/foundation.dart&#039;;
Future&lt;void&gt; clearBrowserHistory() async {
  js.context.callMethod(&#039;clearBrowserHistory&#039;, [
    kDebugMode
        ? &amp;quothttp://localhost:80&amp;quot // میتوانید پورت سفارشی خود را تنظیم کنید
        : &amp;quothttps://ea2.dev&amp;quot  // میتوانید صفحه وب سفارشی خود را تنظیم کنید
  ]);
}نام فایل: native.dartFuture&lt;void&gt; clearBrowserHistory() async {}مرحله 6: ادغام کد با GoRouterبرای گام آخر، باید تعیین کنید که کجا این کد را فراخوانی کنید. در GoRouter، قسمتی وجود دارد که ریدایرکت شدن را مدیریت میکند. بهترین نقطه برای فراخوانی کد پاک کردن تاریخچه مرورگر، این بخش است.در اینجا یک نمونه کد آورده شده است:final appRouter = GoRouter(
  initialLocation:  ... // این قسمت را بر اساس کد خود تکمیل کنید
  redirect: (context, state) {  // بخش مهم برای این مقاله در اینجا است
MultiPlatforms.clearBrowserHistory();
    return null;
  },
  routes: [...] // این قسمت را بر اساس کد خود تکمیل کنید
);با دنبال کردن این مراحل، میتوانید دکمه بازگشت مرورگر را به طور موثر در برنامه Flutter خود با استفاده از GoRouter مدیریت کنید و تجربه کاربری یکنواختی را در همه پلتفرمها تضمین کنید.برای هرگونه سوال یا گفتگوی بیشتر، می‌توانید در کامنت‌ها یا از طریق LinkedIn با من ارتباط برقرار کنید.</description>
                <category>Persian Flutter</category>
                <author>اسماعیل احمدی پور</author>
                <pubDate>Fri, 02 Aug 2024 01:13:52 +0330</pubDate>
            </item>
                    <item>
                <title>داربست در اپلیکیشن ؟!</title>
                <link>https://virgool.io/flutter-community/%D8%AF%D8%A7%D8%B1%D8%A8%D8%B3%D8%AA-%D8%AF%D8%B1-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-zmwyafhznsff</link>
                <description>ویجت scaffold یا داربست!به صورت لغوی Scaffold به معنی داربست هستدرواقع ما برای ساخت اپلیکیشن نیازه که یه داربست به عنوان ساختار کلی هر صفحه از اپلیکیشن قرار بدیم و بعد روی اون داربست، بقیه چیزایی که میخوایم رو پیاده کنیم برای مثال ویجت های AppBar, FloatingActionButton , BottomNavigationBar , Drawer که در ادامه باهاشون آشنا میشیم، رو میتونیم روی ویجت scaffold پیاده کنیماین داربست که ازش حرف میزنیم یه ساختار کلیه که به صورت بصری دیده نمیشه و فقط حالت یه محل برای قرار دادن ویجت های دیگه هست و اگر فقط از همین ویجت داخل هر صفحه از اپلیکیشن استفاده بشه فقط یک صفحه سفید دیده میشه ویجت AppBar :ویجت AppBar که در فریم ورک ها و زبان های برنامه نویسی دیگه به اسم header شناخته میشه ویجتی هست که به طور کلی مشخصات هر صفحه از اپلیکیشن داخلش نوشته میشه پارامتر های اصلی این ویجت title , backgroundColor هست که از اسمشون مشخصه چه کاری انجام میدن توی صحبت های بعدی بیشتر راجب این ویجت صحبت میکنیم ;) ویجت FloatingActionButton : ویجت FloatingActionButton دکمه ساده ای هست که بیشتر داخل اپلیکیشن های پیام رسان مثل تلگرام دیده میشه و هر عملی که بیشتر مواقع کاربر زیاد استفاده ازش استفاده میکنه و باید جلوی چشم باشه رو به این دکمه میسپارن پارامتر های اصلی این ویجت backgroundColor , onPressed, shape , child هست که باز توی صحبت های بعدی بیشتر باهاشون آشنا میشیم ویجت ButtomNavigationBar :ویجت ButtomNavigationBar معمولا برای مسیردهی بین صفحات اصلی اپلیکیشن استفاده میشه پارامتر اصلی این ویجت items هست که یک لیست از دکمه های این ویجت رو برامون میسازه همونطور که توی عکس مشخصه هر دکمه نیاز به یک label و یک icon داره باز راجب این ویجت جلوتر صحبت میکنیم ;) ویجت Drawer :ویجت Drawer به معنی کشو هست آیکونی که معمولا برای این ویجت استفاده میشه آیکون همبرگر هست پ.ن : رنگ این آیکون به صورت عادی مشکی رنگه که داخل این کد رنگش رو با استفاده از پارامتر iconTheme داخل AppBar به سفید تغییر دادیمراجب این ویجت هم در آینده بیشتر صحبت میکنیماین بود ویجت scaffold اگه دوست داری خیلی روون و راحت فلاتر رو یاد بگیری منو از دست ندهتا آموزشای بعدی بدرود 🖐️جامعه فلاتر فارسیYoutube:youtube.com/@PersianFlutterTwitter:twitter.com/PersianFlutterTelegram Group:@flutter_community</description>
                <category>Persian Flutter</category>
                <author>محمد مهدی دایی کریم زاده</author>
                <pubDate>Sat, 13 Jul 2024 18:43:39 +0330</pubDate>
            </item>
                    <item>
                <title>زبان Dart را بیشتر بشناسیم: Pattern</title>
                <link>https://virgool.io/flutter-community/%D8%B2%D8%A8%D8%A7%D9%86-dart-%D8%B1%D8%A7-%D8%A8%DB%8C%D8%B4%D8%AA%D8%B1-%D8%A8%D8%B4%D9%86%D8%A7%D8%B3%DB%8C%D9%85-pattern-oq9zwex6dnxt</link>
                <description>در ادامه‌ی پست های آشنایی با قابلیت‌های جدید زبان دارت، در این پست به بررسی الگوها (Patterns) می‌پردازم. الگوها در نسخه‌های بعد از دارت 3 در دسترس هستند. همونطور که از نامش پیداست، هدف این هست که تطبیق (match) الگویی از مقادیر رو با یک سری مقدار واقعی بررسی کنه. الگو قراره چه کاری انجام بده؟ به طور کلی، یک الگو بسته به شکلش (shape) ممکنه یک مقدار رو تطبیق بده یا یک مقدار رو تخریب کنه (destructure: یعنی ساختار یک مقدار رو از بین ببره) و یا هر دو کار رو انجام بده. برای شروع شاید این تعریف کوتاه واضح نباشه. در ادامه بیشتر در موردش توضیح میدم.بریم سراغ بخش اول تعریف بالا. تطبیق الگو این امکان رو به شما میده که: چک کنید آیا یک مقدار یک شکل مشخص داره یا نهبرابریش با یک مقدار ثابت مشخص رو چک کنیدبرابریش با مقدار دیگه‌ای رو چک کنیدنوع داده اش رو چک کنیدحالا تخریب الگو چیه؟ تخریب الگو با یک syntax مناسب این امکان رو برای شما فراهم می‌کنه که مقداری رو به قسمت‌های تشکیل دهنده‌اش بشکنید. تطبیقالگو همیشه یک مقدار رو تست میکنه تا تشخیص بده که آیا این مقدار اون چیزی که شما انتظار دارید هست یا نه. به عبارت دیگه دارید تطبیق یک مقدار رو با یک الگوی مشخص چک می‌کنید. این که الگو قراره چه چیزی رو چک کنه به نوع الگو بستگی داره. برای مثال الگوی مقدار ثابت (constant pattern) وقتی تطبیق  پیدا میکنه که متغیر با یک مقدار ثابت برابر باشه:switch (number){
   // Constant pattern matches if 1 == number.
   case 1:
      print(&#039;one&#039;);
}خیلی از الگوها از زیرالگوها (subpatterns) استفاده می‌کنند که بهشون الگوهای inner و outer میگیم. الگوها به صورت بازگشتی در زیرالگوهاشون تطبیق داده می‌شن. برای مثال فیلدی که الگوی نوع مجموعه (collection type) داره میتونه خودش الگوی متغیر یا الگوی ثابت داشته باشه:const a = &#039;a&#039;;
const b = &#039;b&#039;;
switch (obj){
    case [a, b]:
        print(&#039;$a, $b&#039;);
}در مثال بالا اول الگوی لیست [a, b] تطبیق داده میشه یعنی چک میشه که آیا obj یک لیست با دو فیلد هست یا نه. بعد چک میشه که آیا فیلدها با زیرالگوهای ثابت &#x27;a&#x27; و &#x27;b&#x27; مطابق هستن یا نه.تخریبوقتی یک object و الگو تطبیق پیدا کنند، الگو می‌تونه به داده های object  دسترسی پیدا کنه و اونها رو استخراج کنه. به عبارت دیگه، الگو object رو تخریب می‌کنه:var numList = [1, 2, 3];
// List pattern [a, b, c] destructures the three elements from numList...
var [a, b , c] = numList;
// ...and assigns them to new variables.
print(a + b + c);موضوع جالب دیگه اینکه می‌تونید هر نوع الگویی رو داخل الگوی تخریب قرار بدید. در مثال زیر الگوی case یک لیست دو عنصری رو تطبیق میده که عنصر اولش a یا b هست:switch (list){
   case [&#039;a&#039; || &#039;b&#039;, var c]:
       print(c);
}کجا می‌تونیم از الگوها استفاده کنیم؟در زبان دارت از الگوها در موارد زیر می‌تونید استفاده کنید:اعلان و مقداردهی متغیرهای محلیحلقه های  for و for-inدستورات if-case و switch-caseجریان های کنترلی مثل collection literalsاعلان متغیرها: از الگوی اعلان متغیر (variable declaration) می‌تونید در هرجایی که دارت اجازه‌ی تعریف متغیر محلی میده استفاده کنید. الگو، کار تطبیق مقدار با سمت راست اعلان رو انجام میده. وقتی مطابقت انجام شد، مقدار تخریب میشه و در متغیر محلی جدید قرار می‌گیره:// Declares new variables a, b, and c.
var (a, [b, c]) = (&#039;str&#039;, [1, 2]);الگوی اعلان متغیر باید با var و یا final و سپس الگو شروع بشه.مقداردهی متغیر: الگوی مقداردهی متغیر (variable assignment)، سمت چپ یک مقداردهی قرار میگیره. اول object تطبیق یافته رو تخریب میکنه. بعد مقدار رو به متغیرهای موجود تخصیص میده (به جای اینکه به یک متغیر جدید مقیدش کنه). مثال زیر رو ببینید که از الگوی مقداردهی برای جابجایی مقدار دو متغیر استفاده می‌کنه و این کار رو بدون استفاده از متغیر موقت سوم انجام میده:var (a, b) = (&#039;left&#039;, &#039;right&#039;);
(b, a) = (a, b);   // Swap
print(&#039;$a $b&#039;);   // Prints &amp;quotright left&amp;quotعبارات switch: هر case clause یک الگو داره. در یک case می‌تونید هر نوع الگویی رو استفاده کنید. الگوهای case جریان کنترل برنامه رو به این صورت تغییر می‌دن:آبجکتی که روی اون switch زدیم رو تطبیق می‌ده و تخریب می‌کنهاگر آبجکت تطبیق پیدا نکنه به اجرا ادامه می‌دهمقادیری که یک الگو تخریب می‌کنه متغیر محلی همون case هستند که تخریب در اون انجام شده. در واقع scope اونها بدنه همون case هست. switch (obj){
   //Matches if 1 == obj.
   case 1:
     print(&#039;one&#039;);

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

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

print(record.$1);   //Prints &#039;first&#039;
print(record.a);   //Prints 2
print(record.b);   //Prints true
print(record.$2);   //Prints &#039;last&#039;عملگر == و توابع hashcode به صورت خودکار برای record تعریف شده. دوتا record برابر هستند اگر شکل یکسانی داشته باشند و فیلدهای متناظرشون مقادیر یکسان داشته باشند. چون ترتیب فیلدهای دارای نام بخشی از شکل record نیست، ترتیب شون روی تساوی تاثیر نداره. </description>
                <category>Persian Flutter</category>
                <author>Shahpasand</author>
                <pubDate>Fri, 12 May 2023 19:03:19 +0330</pubDate>
            </item>
                    <item>
                <title>کد تمیز گلی از گل های گیتهاب است</title>
                <link>https://virgool.io/flutter-community/%DA%A9%D8%AF-%D8%AA%D9%85%DB%8C%D8%B2-%DA%AF%D9%84%DB%8C-%D8%A7%D8%B2-%DA%AF%D9%84-%D9%87%D8%A7%DB%8C-%DA%AF%DB%8C%D8%AA%D9%87%D8%A7%D8%A8-%D8%A7%D8%B3%D8%AA-iasgqgol7ll3</link>
                <description>به نام خداوند کدهای پاکخانه ی دل ما را از کرم، عمارت کن!              پیش از آنکه این خانه رو نهد به ویرانی(شیخ بهایی)بعد از چند ماه سلام ?اواخر تابستون بود که می خواستم یک مطلب درمورد یکی از پکیج های فلاتر بنویسم که حوادثی پیش اومد که نشد ? حدود یک ماه پیش تصمیم گرفتم در اولین فرصت به اون تصمیم جامه عمل بپوشونم که به یک پروژه برخوردم که تصمیم گرفتم موضوع صحبت رو موقتا عوض کنم ? خلاصه که این نوشته برای من مثل حبسیه اس برای ملک الشعرا.. ?پروژه کذایی از این قرار بود که باید UI یک اپلیکیشن توسعه یافته رو rebuild می کردم که کار عجیبی هم نبود.قبلا مقداری در جريان پروژه بودم که مثلا از cubit استفاده شده، نحوه پیاده سازی api ها به چه صورت فاجعه باره. اما سمت view رو ندیده بودم که ای کاااش میشد برم عقب، ای کاش ندیده بودمت !الآنی که دست به کیبورد شدم لازم دونستم یک سری نکاتی رو بگم که اگر معدود فلاتر دولوپر هایی داریم که این جوری نرم افزار توسعه میدن برن یه گوشه بشینن و به کثیف کاری هاشون تو کد فکر کنن.. ?این همه پریشانی بر سر پریشانی…خب خیلی هامون می دونیم که آنکل باب مهربان یک کتاب داره به اسم clean code که به تفصیل توضیح داده کجا و چجوری کدهای شیک بزنیم و احادیث زیادی در باب مطالعه این کتاب اومده. علاوه بر این، یک اصطلاح دیگه داریم به اسم clean architecture که اگه در جریانش هستید که آفرین! اگه هم نه که برادرمون گوگل حرف در این مورد زیاد داره. از Chat GPT هم می تونید بپرسید ?. مورد سوم هم داخل فریمورک فلاتر قابل رؤیته؛ میدونید که اساس این فریمورک بر پایه widgetها بنا شده. از ب بسم الله که شروع به توسعه می کنیم یک widget می سازیم و همین فرمون رو ادامه می دیم.با عنایت به این ۳ اصل اساسی میریم که اون کد رو نقد کنیم.. ? از شما به عنوان یک توسعه دهنه فهیم و محترم انتظار میره که جوری نرم افزار رو توسعه بدید که بعدا مورد لطف بقيه قرار نگیرید! نرم افزار و اون کدی که براش نوشته میشه همچون فرزند شماست؛ نباید جوری فرزندتون رو بار بیارید که بعدا نتونید جمعش کنید! خشت اول رو باید درست بذارید که می چرخه زمونه، اینجور نمی مونه!خب برگردیم به نقد کد..شما موقع توسعه اپ تون به ویجت هایی برمیخورید که ویژگی های مشترکی دارند و تکرار می شن و مثلا ممکنه تفاوتش در یک title باشه. مثل دکمه ای در چندین جای اپ وجود داره و تفاوتش مثلا فقط در عنوان و عملکردش هست. اینجا عقل سلیم میگه این ویجت رو extract یا استخراج کن و مثلاً عنوان و فانکشن مربوط به onTap ش رو به عنوان ورودی در سازنده دریافت کن. تا اگه یه روز خواستی یک تغییری در ظاهرش ایجاد کنی یک جا رو فقط بخوای عوض کنی. یا همون اصل DRY (Don’t Repeat Yourself). علاوه بر اون کدت هم خواناتره؛ ۴ صباح دیگه اومدی پاش نشستی بهتر یادت میاد اینا چی ان اونا کدومن ?نکته بالا تا حدی درمورد تم برنامه هم صادقه؛ اگر فرضاً تمامی TextField های برنامه borderRadius یکسان دارند این رو باید توی Theme برنامه یک بار برای همیشه تعریف کنی و خلاص! رنگ های برنامه هم قس علی هذا ..در هر زبان برنامه نویسی یک استایل برای نام گذاری ها وجود داره؛ که بهتر و زیبا تره که رعایت بشه. یعنی چی؟ مثلا میخوای به کلاسی که نوشتی اسم بدی باید به صورت UpperCamelCase باشه، به ثابت های میخوای اسم بدی lowerCamelCase طور اسم بده. شاید فکر کنین چه فرقی داره، مهم اینه کار کنه! درسته کار میکنه ولی تمیزز نیست دوست عزیز!? جهت کسب اطلاعات بیشتر به داکیومنت خود فلاتر مراجعه بنما!مطلب بعدی ترکیب چندین اصل هست؛ وقتی شما می خواهید تابعی رو بنویسید، به چندین نکته باید توجه کنید:اصل اول solid در مورد تک مسئولیتی صحبت میکنه؛ اما شما اگه عاقل باشی متوجه میشی که این اصل رو میتونی روی توابع هم پیاده کنی و تعمیم بدی؛ درکتاب کلین کد هم بهش پرداخته شده. که یک تابع باید یک کار مشخص انجام بده؛ همچنین نباید تابعی بنویسی که ۳۰ - ۴۰ خطه! عمو باب گفته حدود ۱۵ خط حداکثر باشه ?حالا ممكنه در این اثنا فاز خیلی clean بودن برداری و بیای موقع پیاده سازی ui به ازای هر ویجت یک متغیر تعریف کنی، مثلا:var nameTextField = TextFiled();
var phoneTextField = TextFiled();
//and so onبعدش بیای متد build رو پیاده سازی کنی و اینا رو بهش بدی. خدمتت عرض کنم که اینم اشتباهه ?! هیچ جا چنین کاری انجام نشده! کار عاقلانه اینه اون رو به صورت method یا یک ویجت extract کنید و تامام.در آخر، مقیاس نقد رو یکم بالاتر می برم .. در هر پروژه درست و حساب یک معماری وجود داره. مثلا اگه پروژه تون با Get نوشته شده با MVC میتونید مجلسی تر بنویسیدش. لزوم وجود معماری بر همگان واضحه و هدف این مقاله بیان اهمیت اون نیست! در ابتدایی ترین حالت یک، پروژه باید یک ساختار معقول و منطقی داشته باشه که مشخص باشه هرچیزی کجاست. چه معنى داره model هر صفحه کنار همون صفحه باشه! چرا ساخت مدل، ارسال رکوئست، دریافت پاسخ و مدیریتش همگی توی onPressed باشه ?؟! اون cubit هویجه پس؟! Datasource کشکه؟!این طلب گر از تو و از من خطاست           گر بمیرم این دم از غم هم رواست(عطار)امیدوارم این نکات براتون مفيد باشه و کمک بکنه کدهایی بنویسید که عاقبت بخیر بشید ? مرسی از توجه تون؛ اشتراک گذاری فراموش تون نشه ?یقیناً در این نشانه هایی است برای مردمی که می اندیشند(سوره روم، آیه ۲۱)</description>
                <category>Persian Flutter</category>
                <author>yalda student</author>
                <pubDate>Thu, 09 Mar 2023 01:21:43 +0330</pubDate>
            </item>
                    <item>
                <title>توضیحاتی کوتاه در مورد isolate و multi thread در فلاتر</title>
                <link>https://virgool.io/flutter-community/%D8%AA%D9%88%D8%B6%DB%8C%D8%AD%D8%A7%D8%AA%DB%8C-%DA%A9%D9%88%D8%AA%D8%A7%D9%87-%D8%AF%D8%B1-%D9%85%D9%88%D8%B1%D8%AF-isolate-%D9%88-multi-thread-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-orrzxmyf6ykb</link>
                <description>شاید برای شما این اتفاق افتاده باشه که تو یک بخشی از پروژه مجبور به انجام یک کار سنگین بشید مثلا تبدیل یه فایل با حجم بالا, به آرایه ای از Byte یا دانلود یه فایل با حجم بالا و یا انجام یک سری محاسبات پیچیده که ممکنه منابع زیادی رو اشغال کنه !خوب همه ما میدونیم که کل اپ ما تو یک thread ( حالا فارسیش میشه نخ  ولی در طول این مقاله از کلمه رشته برای اشاره به Thread استفاده میکنیم ) داره عمل میکنه که اصطلاحا بهش Main-Thread میگن و میشه گفت که اکثریت اتفاقات بر روی این رشته میفته از جمله رندر کردن UI و اگه وقفه ای تو این  عمل پیش بیاد ممکنه که به UX یا همان تجربه کاربری ما صدمه بزنه و حس خوبی به کاربر نده ...این دقیقا مشکلی بود که من تو یکی از پروژه هام باهاش روبرو شدم و بعد از خوندن چندتا مقاله و آزمون خطا بهرین راه رو ( حداقل از نظر خودم ) انتخاب کردم که همون isolate هستش !بریم یه سناریو طراحی کنیم تا تو عمل بهتر متوجه مشکل و راه حلش بشیم .تو سایت StackOverflow مثال واقعی تری در مورد isolate پیاده کردم که ممکنه تو درک بهتره این موضوع کمکتون کنهتصور کنین یه حلقه for داریم که میخوایم از ۰ تا ۴۰۰۰ رو برامون طی کنه !خب از اونجایی که این عمل تقریبا سنگین هستش, پس احتمال اینکه چند ثانیه وقفه تو اجرای برنامه داشته باشیم زیاده و اصطاحا یه Freeze کوتاه داشته باشیم ! کلاس Home خودمون رو می سازیم به ساده ترین شکل ممکن که شامل یک CircularProgressIndicator برای نشون دادن وقفه و یک دکمه برای اجرای حلقه.به این شکلو یه متد خیلی ساده برای حلقه for و متصل کردنش به onPressed دکمه خب ! پارامتر stop متد بالا رو روی ۴۰۰۰ تنظیم میکنیم بعد به دکمه وصلش میکنیم که بعد از کیلک با همچین صحنه ای روبرو میشم .. همون طور که تو gif بالا میبینید پروگرس برای چند ثانیه فریز شد و اون دقیقا لحظه کلیک روی دکمه بود .خب بریم سراغ راه حل برای حل این مشکل راه های زیادی وجود داره ولی ما از isolate استفاده میکنیم تا به هدفمون که یه نگاه کلی به isolate هستش رو برسیم. طبقه گفته خود فلاتر isolate ها مموری خودشون رو دارن و جدای از مموری اختصاص داده شده به اپ عمل میکنن و این باعث میشه که Main-Thread به مشکل نخوره ...تو این مقاله از Isolate.spawn استفاده میکنیم که ساده ترین و پایه ای ترین روش هستش بریم سراغ کد :اول کد هارو مینویسم بعد توضیح میدم !اول متد isolate رو ایجاد میکنیم به شکل زیرتو این متد ما یه نمونه از کلاس ReceiverPort میسازیم تا بتونیم به مقدار بازگشتی متدی که داخل isolate درحال اجراس دسترسی داشته باشیم !( من مثال رو یکم تغییر دادم که این حالت رو هم بتونیم بررسی کنیم و زیاد ساده نباشه )کلاس isolate یه متد static به اسم spawn داره که نوع بازگشتی isolate هستش و به صورت Asynchronous نوشته شده (Future)همون طور که تو خط کد های بالا میبینید متد for یا همون expensiveCalculate رو به عنوان پارامتر اول به spawn پاس دادیم و پارانتزی جلوش قرار ندادیم که یعنی این متد زمانی که خود isolate فراخوانی بشه اجرا میشه حتما حواستون به این باشه !و پارامتر دوم لیستی از نوع داده dynamic هستش خب حالا چرا لیست ؟ تو این روش ما نمیتونیم برای متد for بیشتر از یک پارامتر در نظر بگیریم و این در حالیه که ما علاوه بر مقدار stop  sendPort هم باید پاس بدیم این برای زمانی که مقدار بازگشتیه متد برامون مهم باشه در غیره این صورت میتونیم فقط مقدار stop حلقه رو که int هستش پاس بدیم !ولی اینجا ما میخوایم وقتی حلقه تموم شد یه مقدار String برامون برگرده پس حتما باید از port پراپرتی sendPort که یه getter از نوع کلاس SendPort هست و این getter در داخل کلاس ReciverPort در دسترس هستش و ما قبلا تعریفش کردیم رو به عنوان پارامتر پاس بدیم و همچنین مقدار stop حلقه رو... چی گفتم !؟ (((((: پس برای همین از لیست استفاده میکنیم . نکته : پارامتر دوم spawn از نوع Object که یعنی میتونیم کلاس یا مدل هم بهش پاس بدیم و کد تمیز تری بنویسیم نکته بعدی مقدار جنریک spawn هستش که اگه به قطعه کد پایین نگاه کنین دقیقا مشابه با پارامتر ورودی متد for هستش و بایدم باشه و در آخر قرار دادن نمونه گرفته شده از کلاس ReciverPort در حالت listen هستش که بتونیم مقدار نهایی رو از متد for بگیریم . این از کد بالا چیز دیگه ای برای توضیح نداره :)حالا کد های داخل متد for :داخل متد for مقادیر  stop و sendPort رو میگیریم و داخل یه متغیر ذخیره میکنیم !و در نهایت نتیجه متد رو که تو مثال ما مقدار done بود رو با sendPort به listener برمیگردونیم !نکته : حتما در آخر بعد از اینکه کارتون با isolate و port تموم شد هردو رو close کنین . نکته بعدی اینکه این متد دیگه نیاید داخل اسکپ هیچ کلاسی باشه و به اصطلاح Global-Function یا همون High-Level باید باشه با این روش دیگه عملیات های سنگین تاثیری تو UI و UX برنامتون نمیزاره و وقفه تو روند اجرای برنامه ایجاد نیمشه . خب امیدوارم که تونسته باشم حق مطلب رو درست ادا کنم و براتون مفید بوده باشه !و در نهایت ممنون از وقتی که گذاشتید برای مطالعه این مقالهموفق باشید . :) </description>
                <category>Persian Flutter</category>
                <author>ramin bagherian</author>
                <pubDate>Sat, 11 Feb 2023 23:00:07 +0330</pubDate>
            </item>
                    <item>
                <title>کاربرد پکیج Equatable در فلاتر با مثال</title>
                <link>https://virgool.io/flutter-community/%D9%BE%DA%A9%DB%8C%D8%AC-equatable-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%D8%A8%D8%A7-%D9%85%D8%AB%D8%A7%D9%84-dr3t6jqivdw9</link>
                <description>سلام امیدوارم که روزهای خوبی در انتظار همگی باشه.ممکنه بارها حین مشاهده آموزش های مختلف فلاتر به استفاده از پکیج Equatable بر خورده باشین و براتون سوال باشه کار این پکیج چیه؟چرا و چه زمانی باید ازش استفاده کنیم؟بهتره این آموزش رو با یه سناریو پیش ببریم که درک مطلب راحت تر باشه.فرض کنید شما به همراه برادر یا خواهرتون تنها عضو خانواده هستین و پدر تصمیم میگیره به شما دو نفر دوتا ماشین هدیه بده.(خوشبحالتون D:) در ابتدا فرض ما این هست که دقیقا یک مدل ماشین با یک رنگ به هردوی شما عزیزان هدیه میدن و ما قصد داریم که مقایسه کنیم این دو ماشین رو که آیا دقیقا یک مدل و یک رنگ هستند یا خیر!در محیط واقعی خدا به ما قدرت بینایی داده و نگاه میکنیم که آيا شبیه هم هستند یا نه... ولی در زبان دارت یه خورده قضیه فرق میکنه.ادامه مطلب رو همراه من باشین که در قالب مثال عملی مطلب رو پیش بریم.در زبان های برنامه نویسی برای مقایسه دو  شی مختلف از اپراتور == استفاده میکنیم .(با این اپراتور برابر بودن رو بررسی میکنیم)class GiftCar { 
final String model;
 final String color;  
const GiftCar({this.model, this.color});
}در بالا ما یک کلاس داریم به اسم GiftCar که دوتا مشخصه مدل و رنگ ماشین رو براش تعریف کردیم.ما در متد main خودمون دو شی یا نمونه از کلاس بالا میسازیم به این صورت:void main() { 
final GiftCar car1 = GiftCar(model: &#039;207&#039;, color: &#039;white&#039;);
final GiftCar car2 = GiftCar(model: &#039;207&#039;, color: &#039;white&#039;);
print(car1 == car2); // false
 }هردو نمونه ۲۰۷ سفید هست که ماشین اول مال خودمون هست و ماشین دوم متعلق به خواهر یا برادرمون ، حالا با استفاده از اپراتور == بررسی میکنیم که آیا این دو نمونه برابر هستند یا نه؟وقتی نتیجه رو پرینت میگیریم و در کنسول به ما false بر میگردونه و میگه که این دوتا نمونه برابر نیستند،درصورتی که مشاهده میکنید،هردو مقادیرشون برابر هست.به صورت پیشفرض در دارت هر نمونه ای که میگیریم یک hashcode و identity (هویت مثلا کد ملی برای هر فرد) خاص به اون نمونه اختصاص داده میشه و حتی دو نمونه با مقدارهای یکسان رو هم برابر نمیبینه.چاره چیه؟کد زیر رو مشاهده کنید.class GiftCar { 
final String? model;
 final String? color;  
const GiftCar({this.model, this.color});
 @override 
  bool operator ==(Object other) =&gt;
identical(this, other) ||
 other is GiftCar &amp;&amp; 
 runtimeType == other.runtimeType &amp;&amp; 
 model == other.model &amp;&amp;  
color == other.color;  
@override 
  int get hashCode =&gt; model.hashCode ^ color.hashCode;
}باید به شکل بالا برای مقایسه دو نمونه عمل کنیم و به دارت بفهمونیم که identity و hashcode نمونه های ما یکی هستن و وقتی نتیجه برابر بودن رو با تکه کد زیر بررسی میکنیم دارت به ما true بر میگردونه و میگه که این دو نمونه برابر هستند.void main() { 
final GiftCar car1 = GiftCar(model: &#039; 207 &#039;, color: &#039;white&#039;);
final GiftCar car2 = GiftCar(model: &#039; 207&#039;, color:&#039;white&#039;);
print(car1 == car2); // true
 } تصور کنید که ما نمونه های مختلفی داشته باشیم و بخوایم برابر بودن یا نبودنشون رو مقایسه کنیم؟آیا این همه کار تکراری و کد تکراری رو هر دفعه باید انجام بدیم؟بهتره به جای این همه کار تکراری ، تمرکزمون روی پروژه ای که داریم انجام میدیم باشه و وقتمون رو با دوباره نوشتن این همه کد تلف نکنیم.اینجاست که برادر گرامی فیلیکس آنجلو یه پکیج منتشر کرده به اسم Equatable که همین کارو بدون این همه کار اضافی برای ما انجام میده و در پس زمینه خودش این همه کد رو اضافه میکنه و از طرف ما نیازی به اضافه کردن و نوشتن این همه کد تکراری نیست.اضافه کردن پکیج و استفاده از اون در پروژه:باید پکیجش رو از اینجا به پروژه خودمون در فایل pubspec.yaml اضافه کنیم.dependencies:   
equatable: ^2.0.5 و به شکل زیر عمل کنیم.import &#039;package:equatable/equatable.dart&#039;
 class GiftCar extends Equatable { 
final String? model;
 final String? color;  
const GiftCar({this.model, this.color}); 
@override List&lt;Object&gt; get props =&gt; [model, color];
} همونطور که مشاهده کردید کد بالا رو، کلاسی که میخوایم نمونه های مختلفش رو با هم مقایسه کنیم رو از Equatable با کلید واژه Extends ارث بری میکنیم.وقتی که ما از Equatable ارث بری میکنیم به اجبار باید از تکه کدی که اخر کد بالا مشاهده میکنید استفاده کنیم.@override List&lt;Object&gt; get props =&gt; [model, color];این تکه کد یه getter هست که لیستی  از اون مشخصه هایی که میخوایم از کلاسمون رو مورد بررسی قرار بدیم رو از ما میخواد که ما در مثال خودمون دو مشخصه به اسم مدل و رنگ ماشین داریم که در بالا اضافه میکنیم.نوع لیست ما هم از نوع object هست چون مشخصه های ما میتونن شامل هر نوع داده ای string ، int و ... باشن.و وقتی کد زیر رو اجرا میکنیم به ما true برمیگردونه و میگه که نمونه های ما برابر هستند.void main() { 
final GiftCar car1 = GiftCar(model: &#039;207&#039;, color: &#039;white&#039;);
final GiftCar car2 = GiftCar(model: &#039;207&#039;, color: &#039;white&#039;);
print(car1 == car2); // true } این همه دردسر کشیدیم که بگیم ماشین های شما عزیزان یکی هستند.از اولشم میدونستیم یکین ولی چاره چیه؟برنامه نویسیه دیگه!راحت که نیست!خلاصه مبارکتون باشه ماشین هااین پکیج Equtable در جاهای مختلفی استفاده میشه،یکی از مهم ترین کاربرداش استفاده در قسمت states و events استیت منیجیمنت بلاک هست که خیلی مورد استفاده قرار میگیره.در این مطلب قصد داشتم به صورت کلی شمارو با این پکیج آشنا کنم که متوجه کاربردش بشین و وقتی مفهوم کاری که انجام میده رو درک کنیم ، راحت تر متوجه علت استفاده این پکیج در مثال ها و کدهای مختلف میشیم.امیدوارم که مطلب مورد پسندتون قرار گرفته باشه.اگر نکته خاصی هم به نظر شما جا مونده یا بهتره اضافه بشه خوشحال میشم کامنت بزارین که به مطلب اضافه کنم.با ارزوی موفقیتیاعلی</description>
                <category>Persian Flutter</category>
                <author>ایمان یاراحمدی</author>
                <pubDate>Sun, 01 Jan 2023 14:49:03 +0330</pubDate>
            </item>
                    <item>
                <title>دیتابیس Hive در فلاتر به همراه مثال</title>
                <link>https://virgool.io/flutter-community/%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3-hive-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%D8%A8%D9%87-%D9%87%D9%85%D8%B1%D8%A7%D9%87-%D9%85%D8%AB%D8%A7%D9%84-vynkker24bll</link>
                <description>با سلام و وقت بخیر خدمت شما عزیزانتوی این آموزش قصد داریم دیتابیس Hive رو در فلاتر با استفاده از یک برنامه ساده که نام شخص و نام کشور محل زندگی اون فرد رو در دیتابیس ذخیره میکنه و به ما نمایش میده رو شرح بدیم. ما توی این مثال ۴ عمل اصلی  CRUD(Create,Read,Upadate,delete) رو در دیتابیس به طور مختصر در قالب ساخت اپ توضیح میدیم.قبل از اینکه وارد جزئیات اصلی دیتابیس Hive بشیم در مورد انواع دیتابیس،تفاوت هاشون و چرایی انتخاب Hive به طور مختصر توضیح میدیم و بعد وارد توضیحات دیتابیس Hive میشیم.ذخیره کردن داده در به صورت محلی یا Local  یکی از مفاهیم پایه ای و مهم در بسیاری از اپ ها هست. برای مثال ذخیره کردن ادرس و اطلاعات یک مشتری برنامه سفارش غذای آنلاین یا مثلا ذخیره کردن مقدار امتیاز کسب شده که هر شخص در یک بازی موبایلی کسب میکنه یا مقدار متغییری که نمایش میده کاربر دارک مود یا لایت مود رو در اپلیکیشن انتخاب کرده همگی این ها نیاز به یک دیتابیس داخلی دارن و چندین کاربرد دیگه که در اپ های مختلف مشاهده کردین.با این مثال ها متوجه اهمیت وجود دیتابیس در اپلیکیشن ها شدیم.فلاتر  گزینه های مختلفی برای ذخیره سازی داده به صورت محلی در اختیار ما گذاشته که برنامه نویس بر اساس کابرد اپلیکیشنی که درحال توسعه اون هست میتونه بین این گزینه ها یکی رو انتخاب کنه برای مثال دیتابیس Shared_Preferences پکیج مناسبی برای ذخیره ساختارهای کوچک Key-Value در فلاتر هست که یک مقدار رو به عنوان کلید میگیره و یک مقدار رو به عنوان مقدار اون کلید که مثلا برای ذخیره مقدار روشن یا خاموش بودن دارک مود میتونه گزینه خوبی باشه.یکی دیگه از گزینه ها دیتابیس Sqflite هست که پکیجی هست بر پایه Sqlite که در فلاتر استفاده میشه. این دیتابیس انتخاب خوبی هست برای ذخیره کردن داده ها در جداولی که دیتاها در اون جداول با هم ارتباط دارن  و در ارتباطاتشون پیچیدگی زیادی وجود داره  و ما در چنین مواقعی از این دیتابیس استفاده میکنیم.ولی اگر ما میخوایم که از دیتابیسی استفاده کنیم که پیچیدگی کد نویسی کمتری داشته باشه و سریع و امن باشه و وابستگی نیتیو نیاز نباشه و بتونیم حتی در فلاتر وب هم از اون استفاده کنیم ،دیتابیس Hive گزینه خیلی خوبی برای ما هست.چرا دیتابیس Hive?با این گزینه های در دسترس چرا ما از دیتابیس Hive استفاده کنیم؟چون دیتابیس سبکی هست از لحاظ حجمی و راه حل سریعی هست برای استفاده از ذخیره کردن ساختار Key-Value در اپلیکیشن و اینکه Cross-Platform هست و ما میتونیم همزمان اون رو در(فلاتر موبایل،وب و دستکتاپ) استفاده کنیم که کراس پلتفرم بودنش مزیتی هست نسبت به دیتابیس Sqflite در فلاتر.و اینکه دیتابیس hive از نوع NOSQL دیتابیس برای فلاتر هست که نیازی به نوشتن دستورات sql ای در فلاتر نیست و به طور اتوماتیک با استفاده از انوتیشن ها ساختارهارو برای ما میسازه.در تصویر پایین میتونید مشاهده کنید میزان سرعت خواندن و نوشتن Hive رو نسبت به بقیه دیتابیس هادر بالا اهمیت دیتابیس در اپلیکیشن ها و مزیت Hive نسبت به بقیه دیتابیس ها برای ما روشن شد و حالا بریم سراغ استفاده از دیتابیس.نکته:دیتابیس های مختلف دیگه ای هم وجود داره و شما میتونید توی گوگل در مورد انواع اون ها و تفاوت هاشون مطالعه کنید وتصمیم بگیرید که کدوم رو استفاده کنید،و ما سعی کردیم شناخته شده ها رو معرفی کنیم.با من همراه باشید.در تصویر پایین خروجی اپلیکشنی که قرار هست با هم بسازیم رو مشاهده میکنید.ما یک پروژه جدید فلاتر میسازیم به نام hive_demo و در pubspec.yaml پروژه دو تا پکیج زیر رو اضافه میکنیم.dependencies:  hive: ^2.1.0  hive_flutter: ^1.1.0شما میتونید برای به روز بودن ورژن پکیج ها از وبسایت Pub.dev اخرین نسخه رو به پروژتون اضافه کنید.و محتوای زیر رو به جای فایل Main.dart جایگزین کنید.import &#039;package:flutter/material.dart&#039;;

main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &#039;Hive Demo&#039;,
      theme: ThemeData(
        primarySwatch: Colors.purple,
      ),
      debugShowCheckedModeBanner: false,
      home: InfoScreen(),
    );
  }
}همون طور که مشاهده میکنید با در قسمت home صفحه InfoScreen رو قرار دادیم که شما میتونید یک صفحه با همین نام بسازید برای نمایش اطلاعات کاربر،ولی قبل از اینکه وارد پیاده سازی قسمت های مختلف برنامه بشیم بهتره یه خورده در مورد مفاهیم دیتابیس Hive توضیح بدیم که درک بهتری داشته باشیم و بعد قسمت های مختلف برنامه رو پیاده سازی کنیم.مفهوم Box در دیتابیس Hiveدیتابیس Hive از یک مفهومی به اسم Boxes برای ذخیره کردن دیتا در دیتابیس استفاده میکنه، اگه قبلا با دیتابیس Sql  کار کرده باشین با مفهوم Table در اون آشنا هستین و یک Box  مفهومی شبیه به Table هست،با این تفاوت که Box ها فاقد ساختار دقیقی هستن،‌Box ها انعطاف پذیر هستن و اون ها فقط میتونن ارتباطات ساده بین دیتاها رو هندل کنن.قبل از اینکه دسترسی پیدا کنیم به داده های ذخیره شده یک جعبه ما باید اون Box یا جعبه رو باز کنیم،این کار کل محتوای داخل جعبه رو در مموری بارگزاری میکنه و باعث میشه خیلی راحت و سریع به محتوای داخل جعبه قابل دسترسی باشند.این دو خط پایین یک مثال ساده از این هست که چطور جعبه ای به نام peopleBox رو باز میکنیم و پارامتر name این جعبه رو دریافت میکنیم.var box = await Hive.openBox(&#039;peopleBox&#039;);
String name = box.get(&#039;name&#039;);غیر از ‌Box های معمولی ما مدل های مختلفی از box ها داریم که شامل Lazy box و Encrypted box  میشه: کاربرد Lazy box:همون طور که گفتیم جعبه های معمولی داده های مارو از حافظه محلی در مموری بارگیری میکنن و جعبه ما باز میمونه،اما وقتی داده های سنگینی داشته باشیم این راه خوبی برای دسترسی به داده ها نیست و حجم زیادی از داده وارد مموری ما بشه ممکنه باعث وجود خطا در برنامه بشه و حافظه زیادی رو اشغال کنه،به این خاطر ما از Lazy box استفاده میکنیم.در Lazy box ، فقط کلید ها یا Key برای خواندن و ذخیره کردن داخل مموری بارگزاری میشن، و ما میتونیم با استفاده از کلید داده مورد نظرمون رو از داخل جعبه فراخوانی کنیم.دوخط زیر رو مشاهده کنید به این طریق میتونیم از Lazy box استفاده کنیم.var lazyBox = await Hive.openLazyBox(&#x27;hugePeopleBox&#x27;);String name = await lazyBox.get(&#x27;name&#x27;);ما برای دسترسی به محتویات داخل جعبه های معمولی نیاز به استفاده از مفهوم await نداریم ولی در Lazy box باید استفاده کنیم به خاطر اینکه داده ها و محتوای ما درحال حاضر در مموری نیستند و فقط با استفاده از کلیدها قابل دسترسی هستند.ذخیره کردن داده های حساس با استفاده از Encrypted boxشما ممکنه بخواید داده حساسی از کاربر رو داخل دیتابیستون ذخیره کنید که به راحتی قابل دسترسی نباشه اینجا هست که مفهوم Encrypted ‌‌‌Box به ما کمک میکنه که این کارو انجام بدیم.دیتابیس Hive از مفهوم رمزگذاری AES-256 در خارج از Box یا جعبه پشتیبانی میکنه همراه با یک تابع کمکی برای تولید یک کلید رمزگذاری با استفاده از الگوریتم Fortuna.برای ذخیره کردن ایمن کلیدرمزگزاری شده بهتره از پکیج flutter_secure_storage استفاده کنید.اینجا یک مثال از ساخت و بازکردن یک Encrypted Box رو مشاهده میکنید.const secureStorage = FlutterSecureStorage();
final encryprionKey = await secureStorage.read(key: &#039;key&#039;);

if (encryprionKey == null) {
  final key = Hive.generateSecureKey();
  await secureStorage.write(
    key: &#039;key&#039;,
    value: base64UrlEncode(key),
  );
}

final key = await secureStorage.read(key: &#039;key&#039;);
final encryptionKey = base64Url.decode(key!);
print(&#039;Encryption key: $encryptionKey&#039;);

await Hive.openBox(
  &#039;securedBox&#039;,
  encryptionCipher: HiveAesCipher(encryptionKey),
);برای واکشی و ذخیره دیتا ها در جعبه شما میتونید از متدهای زیر استفاده کنید.final encryptedBox = Hive.box(&#039;securedBox&#039;);

_getData() {
  setState(() {
    data = encryptedBox.get(&#039;secret&#039;);
  });
  log(&#039;Fetched data&#039;);
}

_putData() async {
  await encryptedBox.put(&#039;secret&#039;, &#039;Test secret key&#039;);
  log(&#039;Stored data&#039;);
}برای مشاهده مثال کامل با استفاده از این نوع جعبه شما میتونید روی این لینک کلیک کنید.خوب الان که مفاهیم کلی دیتابیس رو درک کردیم بریم به سراغ ادامه مثال:قبل از اینکه عملیات CRUD رو توضیح بدیم باید دیتابیس خودمون رو initialize کنیم و یک ‌Box یا جعبه رو باز کنیم که این جعبه برای ذخیره داده های ما استفاده میشه.دیتابیس باید قبل از اینکه هر جعبه ای باز بشه، initialize بشه،بهترین مکان در تابع main برنامه هست.دستورات زیر رو مشاهده کنید.main() async {
  // Initialize hive
  await Hive.initFlutter();
  runApp(MyApp());
}فقط باید حواستون باشه که تابع ما باید asynchronous باشه و از await قبل از init کردن استفاده کنیم.ما در این برنامه فقط یک جعبه داریم اگر که شما قصد دارید از جعبه های مختلفی استفاده کنید باید مواظب باشید حتما قبل از استفاده اون جعبه رو باز کنید.main() async {
  // Initialize hive
  await Hive.initFlutter();
  // Open the peopleBox
  await Hive.openBox(&#039;peopleBox&#039;);
  runApp(MyApp());
}خوب الان همه چی اماده شده که ما عملیات CRUD رو که مخفف ساختن،خواندن،اپدیت،حذف کردن داده هست رو پیاده کنیم.عملیات CRUD در اپلیکیشن:ما عملیات های خودمون رو در داخل صفحه Info Screen که قبلا در موردش صحبت کردیم که یک صفحه StateFul Widget هست تعریف میکنیم.ساختار صفحه ما به شکل زیر هست:import &#039;package:flutter/material.dart&#039;;
import &#039;package:hive/hive.dart&#039;;

class InfoScreen extends StatefulWidget {
  @override
  _InfoScreenState createState() =&gt; _InfoScreenState();
}

class _InfoScreenState extends State&lt;InfoScreen&gt; {
  late final Box box;

  @override
  void initState() {
    super.initState();
    // Get reference to an already opened box
    box = Hive.box(&#039;peopleBox&#039;);
  }

  @override
  void dispose() {
    // Closes all Hive boxes
    Hive.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}کد بالا رو که نگاه کنید ما جعبه خودمون رو در متد initState در داخل یک متغیر به اسم box فراخوانی میکنیم.(جعبه ای که قبلا ساختیم رو در اینجا فقط فراخوانی میکنیم) و ما باید بعد از اتمام کارمون با جعبه و قبل از بستن اپلیکیشن ببندیم که ما در متد dispose دیتابیس خودمون رو میبندیم که باعث مشکلات حافظه نشه...خوب بریم چندتا متد تعریف کنیم برای عملیات CRUD در اپلیکیشنمون:class _InfoScreenState extends State&lt;InfoScreen&gt; {
  late final Box box;

  _addInfo() async {
    // Add info to people box
  }

  _getInfo() {
    // Get info from people box
  }

  _updateInfo() {
    // Update info of people box
  }

  _deleteInfo() {
    // Delete info from people box
  }

  // ...
}خوب ما یک Ui خیلی ساده با استفاده از کدهای زیر میسازیم برای تست و بررسی اینکه عملیات های ما به درستی کار میکنه یا نه؟!class _InfoScreenState extends State&lt;InfoScreen&gt; {
  // ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(&#039;People Info&#039;),
      ),
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            ElevatedButton(
              onPressed: _addInfo,
              child: Text(&#039;Add&#039;),
            ),
            ElevatedButton(
              onPressed: _getInfo,
              child: Text(&#039;Get&#039;),
            ),
            ElevatedButton(
              onPressed: _updateInfo,
              child: Text(&#039;Update&#039;),
            ),
            ElevatedButton(
              onPressed: _deleteInfo,
              child: Text(&#039;Delete&#039;),
            ),
          ],
        ),
      ),
    );
  }
}خروجی کد ما به این شکل هست:ذخیره کردن دیتا در Hive :برای این که ما بتونیم داده در داخل دیتابیس و باکسمون ذخیره کنیم باید اون متدی که قبلا تعریف کردیم برای فراخوانی دیتابیسمون به اسم box رو به همراه متد Put به شکل زیر استفاده کنیم،این متد زوج های کلید مقدار یا همون زوج key_value رو میپذیره.// Add info to people box
_addInfo() async {
  // Storing key-value pair
  box.put(&#039;name&#039;, &#039;John&#039;);
  box.put(&#039;country&#039;, &#039;Italy&#039;);
  print(&#039;Info added to box!&#039;);
}خوب ما متد add رو به شکل بالا مقدار دهی میکنیم،مقدار اول میشه نام کلید که در اولی name هست و رشته دوم میشه مقدار name  که John هست و خط بعدیش هم که کشور و نام اون رو میگیره.همچنین Hive کلید های integer ای رو هم ساپورت میکنه و میتونیم از کلیدهای auto-incrementing هم استفاده کنیم که خیلی کاربردی هست زمانی که ذخیره کنیم چندین مقداررو شبیه یک لیست و بخوایم با استفاده از یک شاخص خاص اون مقدار رو از داخل دیتابیس یا باکسمون بازیابی کنیم از این روش استفاده میکنیم.برای روشن تر شدن مطلب کد زیر رو مشاهده کنید.box.add(&#x27;Linda&#x27;); // index 0, key 0box.add(&#x27;Dan&#x27;);   // index 1, key 1دریافت دیتا از Box :برای خوندن دیتا از جعبه ما از متد get روی متغییری که قبلا ساختیم به اسم box استفاده میکنیم که در قسمت put هم مشاهده کردین// Read info from people box
_getInfo() {
  var name = box.get(&#039;name&#039;);
  var country = box.get(&#039;country&#039;);
  print(&#039;Info retrieved from box: $name ($country)&#039;);
}نکته اگه  از auto-incrementing values یا مقادیری که به صورت اتوماتیک و افزایشی مقدار دهی میشن  استفاده کنیم میتونیم با استفاده از index اون رو فراخوانی کنیم به شکل زیر:box.getAt(0); // retrieves the value with index 0box.getAt(1); // retrieves the value with index 1اپدیت کردن دیتا:برای اپدیت کردن داده ما میتونیم از همون متود put که برای اضافه کردن داده از اون استفاده کردیم،استفاده کنیم که وقتی در داخل اون key های قبلی مقادیر جدید قرار میدیم،جایگزین قبلی میشه،کد زیر رو مشاهده کنید:// Update info of people box
_updateInfo() {
  box.put(&#039;name&#039;, &#039;Mike&#039;);
  box.put(&#039;country&#039;, &#039;United States&#039;);
  print(&#039;Info updated in box!&#039;);
}مقدار اسم و کشور رو به Mike و UnitedState تغییر دادیم.جایگزین مقادیر قبلی میکنه.نکته: اگر  از auto-incrementing values استفاده کرده باشیم، باید از متد putAt به شکل زیر استفاده کنیم،این متد مقادیر با یک شاخصه خاص مثلا index 0 رو اپدیت میکنه به شکل زیر:box.putAt(0, &#x27;Jenifer&#x27;);حذف کردن دیتا در جعبه:برای حذف کردن داده میتونیم از متد delete به شکل زیر استفاده کنیم:// Delete info from people box
_deleteInfo() {
  box.delete(&#039;name&#039;);
  box.delete(&#039;country&#039;);
  print(&#039;Info deleted from box!&#039;);
}بعد از حذف اگر بخوایم مشخصه حذف شده رو برگردونیم یا بهتره بگیم با get مقدارش رو بخونیم به ما null بر میگردونه.نکته : اگر از auto-incrementing values استفاده کرده باشیم باید به شکل زیر اون رو حذف کنیم:box.deleteAt(0);استفاده از اشیاء شخصی سازی شده با استفاده از TypeAdapter :اگرمتوجه تیتر بالا نشدید ،ادامه مطلب رو بخونید متوجه موضوع میشین(واقعا پیدا کردن معادل فارسی بعضی از کلمات خیلی سخته D:)به طور معمول hive از همه ی primitive type ها نظیر List,Map,DateTime,Uint8List اما گاهی اوقات نیاز هست دیتا مدل های شخصی سازی شده خودمون رو داشته باشیم که باعث میشه مدیریت داده ها آسون تر بشه،برای این کار میتونیم از TypeAdapter  ها استفاده کنیم.تایپ آداپترها هم میتونن به صورت دستی تولید بشن و هم به صورت اتوماتیک.بهتره به صورت اتوماتیک این کار رو انجام بدیم که باعث باعث میشه مشکلات احتمالی که وقتی دستی انجام میدیم به حداقل برسه و همچنین روش خیلی سریعتری هست.کلاس مدل خودمون رو به شکل زیر تعریف میکنیم:class Person {
  final String name;
  final String country;

  Person({
    required this.name,
    required this.country,
  });
}ساخت Hive Adapter :ابتدا نیاز هست چند تا dependencies رو برای ساخت TypeAdapter  به فایل pubspec.yaml خودمون اضافه کنیم.dev_dependencies:  hive_generator: ^1.1.2  build_runner: ^2.1.8و بعد از اضافه کردن dependencies ها مثل کدهای زیر انوتیشن ها رو به هر متغییرمون اضافه میکنیم.import &#039;package:hive/hive.dart&#039;;
part &#039;people.g.dart&#039;;

@HiveType(typeId: 1)
class People {
  @HiveField(0)
  final String name;

  @HiveField(1)
  final String country;

  People({
    required this.name,
    required this.country,
  });
}در قسمت بالا با استفاده از کلمه کلیدی part  و اضافه کردن .g در وسط نام کلاس مدلمون مشخص میکنیم که از این کلاس مدل ما باید به صورت اتوماتیک تایپ اداپتر ساخته بشه.انوتیشن ها هم از نامشون مشخصه مثلا گفتیم فیلد جعبه ما اولیش نام هست دومیش نام کشور.خوب بعد از اینکه کلاس بالا رو ساختیم با استفاده از دستور زیر در ترمینال به طور اتوماتیک کلاس رو برای ما میسازه:flutter packages pub run build_runner buildرجیستر کردن TypeAdapter :باید قبل از اینکه جعبه رو باز کنیم TypeAdapter امون رو رجیستر کنیم وگرنه به ارور بر میخوریم و این کار رو به شکل زیر در متد main  انجام میدیم.main() async {
  // Initialize hive
  await Hive.initFlutter();
  // Registering the adapter
  Hive.registerAdapter(PersonAdapter());
  // Opening the box
  await Hive.openBox(&#039;peopleBox&#039;);

  runApp(MyApp());
}بعد از اتمام این کار بریم برای ساخت اپلیکیشن نهایی :ما سه تا صفحه داریم به اسم:۱- صفحه add یا AddScreen برای اضافه و ذخیره کردن داده در دیتابیس۲- صفحه Info یا InfoScreen برای نمایش و حذف داده های موجود در دیتابیس3- صفحه Update یا UpdateScreen برای اپدیت کردن داده در دیتابیسنیاز نیست که فایل main برنامه رو تغییر بدیم و تمام کارهای برنامه رو در همین صفحات انجام میدیم.صفحه Add:صفحه add ما شامل دوتا ورودی برای نام شخص و نام کشور هست و یک دکمه در پایین صفحه برای اضافه کردن داده های ورودی به دیتابیس مثل شکل زیر:کد صفحه بدین صورت هست:class AddScreen extends StatefulWidget {
  @override
  _AddScreenState createState() =&gt; _AddScreenState();
}
class _AddScreenState extends State&lt;AddScreen&gt; {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text(&#039;Add Info&#039;),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: AddPersonForm(),
      ),
    );
  }
}همون طور که در داخل فرزند Padding کد بالا مشاهده میکنید متد AddPersonForm ما ویجت اصلی هست زمانی که Ui ساخته میشه و همچنین شامل عملگرهای ذخیره سازیHive هست که ساختار پایه این متد به شکل زیر هست.class AddPersonForm extends StatefulWidget {
  const AddPersonForm({Key? key}) : super(key: key);
  @override
  _AddPersonFormState createState() =&gt; _AddPersonFormState();
}

class _AddPersonFormState extends State&lt;AddPersonForm&gt; {
  late final Box box;

  @override
  void initState() {
    super.initState();
    // Get reference to an already opened box
    box = Hive.box(&#039;peopleBox&#039;);
  }

  @override
  Widget build(BuildContext context) {
    return Container(); 
  }
}ما مثل مثال قبل در متد initstate رفرنس جعبه باز شده خودمون رو در متغیر box میریزیم و حالا ما باید یک GlobalKey برای فرم و جند تا کنترلر برای ادیت کردن تکست ها تعریف کنیم مثل شکل زیر:class _AddPersonFormState extends State&lt;AddPersonForm&gt; {
  final _nameController = TextEditingController();
  final _countryController = TextEditingController();
  final _personFormKey = GlobalKey&lt;FormState&gt;();

  // ...
}در کدهای زیر هم متدهایی برای ذخیره کردن دیتا در هایو و اضافه کردن یک اعتبارسنج برای متن هامون تعریف میکنیم:class _AddPersonFormState extends State&lt;AddPersonForm&gt; {
  // ...

  // Add info to people box
  _addInfo() async {
    Person newPerson = Person(
      name: _nameController.text,
      country: _countryController.text,
    );
    box.add(newPerson);
    print(&#039;Info added to box!&#039;);
  }

  String? _fieldValidator(String? value) {
    if (value == null || value.isEmpty) {
      return &#039;Field can\&#039;t be empty&#039;;
    }
    return null;
  }

  // ...
} و کد Ui برنامه ما هم به شکل زیر هست:class _AddPersonFormState extends State&lt;AddPersonForm&gt; {
  // ...

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _personFormKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(&#039;Name&#039;),
          TextFormField(
            controller: _nameController,
            validator: _fieldValidator,
          ),
          SizedBox(height: 24.0),
          Text(&#039;Home Country&#039;),
          TextFormField(
            controller: _countryController,
            validator: _fieldValidator,
          ),
          Spacer(),
          Padding(
            padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 24.0),
            child: Container(
              width: double.maxFinite,
              height: 50,
              child: ElevatedButton(
                onPressed: () {
                  if (_personFormKey.currentState!.validate()) {
                    _addInfo();
                    Navigator.of(context).pop();
                  }
                },
                child: Text(&#039;Add&#039;),
              ),
            ),
          ),
        ],
      ),
    );
  }
}صفحه Update :صفحه Update هم دقیقا در ظاهر مثل صفحه Add هست با این تفاوت ما به این صفحه شی از کلاس Person پاس میدیم که مقادیر قبلی که در دیتابیس هستن رو نمایش بده و ما بتونیم اون رو تغییر بدیم.کد زیر برای صفحه Update :class UpdateScreen extends StatefulWidget {
  final int index;
  final Person person;

  const UpdateScreen({
    required this.index,
    required this.person,
  });

  @override
  _UpdateScreenState createState() =&gt; _UpdateScreenState();
}

class _UpdateScreenState extends State&lt;UpdateScreen&gt; {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text(&#039;Update Info&#039;),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: UpdatePersonForm(
          index: widget.index,
          person: widget.person,
        ),
      ),
    );
  }
}تنها تفاوت در در متد UpdatePersonForm نسبت به Add  وجود متدهایی برای اپدیت مقادیر موجود در دیتابیس ما هست.در کد زیر مشاهده میکنید:class _UpdatePersonFormState extends State&lt;UpdatePersonForm&gt; {
  late final _nameController;
  late final _countryController;
  late final Box box;

  // ...

  // Update info of people box
  _updateInfo() {
    Person newPerson = Person(
      name: _nameController.text,
      country: _countryController.text,
    );
    box.putAt(widget.index, newPerson);
    print(&#039;Info updated in box!&#039;);
  }صفحه Info :صفحه info ما شامل اطلاعات شخص ذخیره شده در داخل دیتابیس هست.درواقع همون عملیات خواندن یا read  در دیتابیس مثل شکل زیر:ما از یک ویجت به اسم ValueListenableBuilder استفاده میکنیم که وقتی رفرش میشه صفحه که داده ای در دیتابیس تغییر کنه.صفحه ما شامل چندین عملکرد هست:۱- ضربه زدن به آیکون دیلیت برای حذف کردن اطلاعات از دیتابیس۲- ضربه زدن به هر آیتم برای آپدیت اون و سومی هم ضربه زدن به floating action button ما برای اضافه کردن اطلاعاتکد نهایی این صفحه ما:class InfoScreen extends StatefulWidget {
  @override
  _InfoScreenState createState() =&gt; _InfoScreenState();
}

class _InfoScreenState extends State&lt;InfoScreen&gt; {
  late final Box contactBox;

  // Delete info from people box
  _deleteInfo(int index) {
    contactBox.deleteAt(index);
    print(&#039;Item deleted from box at index: $index&#039;);
  }

  @override
  void initState() {
    super.initState();
    // Get reference to an already opened box
    contactBox = Hive.box(&#039;peopleBox&#039;);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(&#039;People Info&#039;),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () =&gt; Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) =&gt; AddScreen(),
          ),
        ),
        child: Icon(Icons.add),
      ),
      body: ValueListenableBuilder(
        valueListenable: contactBox.listenable(),
        builder: (context, Box box, widget) {
          if (box.isEmpty) {
            return Center(
              child: Text(&#039;Empty&#039;),
            );
          } else {
            return ListView.builder(
              itemCount: box.length,
              itemBuilder: (context, index) {
                var currentBox = box;
                var personData = currentBox.getAt(index)!;
                return InkWell(
                  onTap: () =&gt; Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) =&gt; UpdateScreen(
                        index: index,
                        person: personData,
                      ),
                    ),
                  ),
                  child: ListTile(
                    title: Text(personData.name),
                    subtitle: Text(personData.country),
                    trailing: IconButton(
                      onPressed: () =&gt; _deleteInfo(index),
                      icon: Icon(
                        Icons.delete,
                        color: Colors.red,
                      ),
                    ),
                  ),
                );
              },
            );
          }
        },
      ),
    );
  }
}خوب تبریک میگم ما تونستیم با استفاده از دیتابیس Hive یه اپ ساده که اطلاعات شخص رو شامل نام و نام کشور اون شخص هست رو میگیره و ذخیره میکنه بسازیم. و ما تونستیم هم اون اطلاعات رو اضافه کنیم هم دیلیت کنیم هم اپدیت کنیم و هم اون رو از حافظه بخونیم.(چهارعمل اصلی دیتابیس)تا جای ممکن سعی کردیم تو این مقاله هم درمورد مفاهیم دیتابیس Hive توضیح بدیم و هم کدهارو ولی هرجایی از کد یا هر مفهمومی ناآشنا بود خیلی راحت میتونید صرفا اون قسمت رو درگوگل سرچ کنید و در موردش اطلاع پیدا کنید.منبع این مقاله آموزشی ما هم بلاگ سایت logrocket هست که مقالات ارزشمند دیگه رو هم در مورد فلاتر میتونین اینجا پیدا کنید و بخونید.سورس نهایی کد رو هم از اینجا از گیت هاب نویسنده اصلی این مقاله آموزشی میتونید دانلود کنید.امیدوارم که این مقاله آموزشی مورد پسندتون قرار گرفته باشه و خوشحال میشم نظرات شمارو در رابطه با این مقاله بدونم.و ممنون از شما که تا اخر همراه من بودین.پ ن: ممنون از پیام زاهدی عزیز که گفتن خیلی پکیج hive استفاده نمیشه و میشه گفت منسوخ شده و بهتره از پکیج isar برای دیتابیس استفاده کنیم.موفق باشیدیاعلی</description>
                <category>Persian Flutter</category>
                <author>ایمان یاراحمدی</author>
                <pubDate>Sat, 17 Sep 2022 02:04:16 +0430</pubDate>
            </item>
                    <item>
                <title>نحوه ریسپانسیو کردن Ui در فلاتر</title>
                <link>https://virgool.io/flutter-community/%D9%86%D8%AD%D9%88%D9%87-%D8%B1%DB%8C%D8%B3%D9%BE%D8%A7%D9%86%D8%B3%DB%8C%D9%88-%DA%A9%D8%B1%D8%AF%D9%86-ui-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-vjslclo9hzcn</link>
                <description>با سلام و وقت بخیر خدمت شما عزیزانتوی این آموزش قصد داریم بررسی کنیم که چه ویجت هایی رو فلاتر برای ریسپانسیو کردن ui معرفی کرده...و در مورد بعضی از ویجت ها هم تا حد ممکن توضیح بدیم ، هدف ما در این آموزش آشنایی اولیه با مفاهیم و ویجت ها هست و برای درک بهتر هر ویجت، صورت جداگانه ویجت رو سرچ کنید و مثال های مختلف رو ببینید.لیست برخی از ویجت های پرکاربرد:MediaQuery - LayoutBuilder - OrientationBuilder - SafeArea - AspectRatio - Flexible - Expanded -FractionalSizedBox and ...اولین مفهومی که قصد داریم معرفی کنیم مدیا کوئری هست .با استفاده از مدیا کوئری ما میتونیم به طول ، عرض و جهت صفحه نمایش دسترسی پیدا کنیم.ما در پایین سه متغییر تعریف کردیم به نام screenHeight ,screenWidth , orientation که با استفاده از دستورات زیر ما به ترتیب عرض و طول و جهت صفحه نمایش (که صفحه نمایش ما افقی هست و یا عمودی)  رو در داخل متغییر هامون قرار میدیمdouble screenWidth = MediaQuery.of(context).size.width;double screenHeight = MediaQuery.of(context).size.height;Orientation orientation = MediaQuery.of(context).orientation;برای اینکه در قالب مثال توضیح داده باشیم من یک اپ ساده که فقط یک متن رو در وسط صفحه نمایش میده رو با استفاده از شبیه ساز ایفون اجرا کردم،و در متد بیلد سه تا متغییر رو تعریف کردم و همونطور که در تصویر بالا مشاهده میکنید ،دستور پرینت سه تا متغییر رو قرار دادیم و وقتی برنامه رو اجرا میکنیم، به ما نشون میده که عرض آیفون ما ۳۹۰ هست و ارتفاع ۸۴۴ و جهت اون هم عمودی هست. به همین راحتی تونستیم طول و عرض جهت صفحه نمایش رو نمایش بدیم.خوب حالا که دربالا متوجه شدیم میتونیم با استفاده از مدیا کوئری سایز صفحه رو بگیریم چه مزیتی برای ما داره ؟ یکی از مزیت هاش این هست که  میتونیم در بدنه برنامه شرط بزاریم که اگه طول و عرض صفحه ما از یه اندازه ای کمتر بود شما بیا و این صفحه رو نمایش بده و اگه بیشتر بود یه صفحه دیگه رو(با ویجت هایی با طول و عرض متفاوت) نمایش بده، کجا کاربرد داره؟ وقتی ما میخوایم ui خودمون رو برای تبلت ها بهینه کنیم یا سایزهای مختلف تلفن های همراه:خیلی ساده بخوام مثال بزنم ما در برنامه بالا یه شرط ساده گذاشتیم و گفتیم اگه width صفحه نمایش ما کوچکتر از ۵۰۰ بود شما بیا متن ما رو با فونت سایز ۳۰ و رنگ زرد نمایش بده در غیر این صورت و بزرگتر بودن سایز بیا و متن رو با فونت سایز ۴۰ و رنگ آبی نمایش بده،این مثال خیلی ساده بود ولی شما میتونید به صورت کامل یک ویجت رو با دو سایز مختلف برای نمایش در موبایل و تبلت بهینه کنید ما اینجا از این نوع شرط گذاری استفاده کردیمcondition ?   :  ,قبل از علامت سوال شرط رو میزاریم ، بعد علامت سوال کاری که باید انجام بشه،و بعد دو نقطه هم کاری که در صورت اینکه شرط اولیه ما اجرا نشه...خیلی بهینه تر بخوایم مثال بزنیم با استفاده از متد OrientaionBuilder هم میتونیم مثل تصویر زیر شرط بزاریم که براساس سایز صفحه نمایش کدوم View ما رو نمایش بده.خوب بریم سراغ یکی از مثال های کاربردی:ما با استفاده از مدیا کوئری میتونیم سایز ویجت هامون رو بر اساس سایز صفحه تعیین کنیم..برای مثال میتونیم تعیین کنیم که ارتفاع ویجت ما ۳۰ درصد ارتفاع صفحه باشه با استفاده از دستور زیر میتونیم این کارو انجام بدیم:MediaQuery.of(context).size.height * 0.30;نکته ( صفر اخر رو هم نزاریم بازم ۳۰ درصد رو میگیره،یا مثلا میتونیم بگیم ۰.۰۲ یعنی دو درصد کل صفحه)و یا اینکه به این صورت هم میشه تعیین کرد که ویجت ما نصف ارتفاع صفحه باشه:MediaQuery.of(context).size.height / 2;مثال زیر رو مشاهده کنید.ما در بالا دوتا container ساختیم که در اولی با استفاده از دستور double.infinity  گفتیم عرضش به اندازه کل صفحه باشه و  ارتفاعش هم ۳۰ درصد کل صفحه باشه...و در container پایین هم ارتفاعی تعیین نکردیم وگفتیم خود به خود به اندازه طول ویجت های فرزندش ارتفاعش تنظیم میشه...خوب تا جای ممکن سعی کردیم مفهوم مدیا کوئری رو توضیح بدیم چون بسیار کاربردی هست و خیلی برای رسپانسیو کردن صفحات به ما کمک میکنه....در قسمت دوم در مورد ویجت های مهم و کاربردی Expanded &amp; Flexible صحبت میکنیمقبل از اینکه وارد توضیحات مربوط به ویجت Expanded بشیم،مثال زیر رو مشاهده کنیددر تصویر بالا مشاهده میکنید که سه تا container داریم که در عرض صفحه قرار گرفتند...خوب حالا اگه که بخوایم container هارو به صورت هم اندازه طوری گسترش بدیم که کامل عرض صفحه رو بگیرن از Expanded استفاده میکنیمویجت Expanded همانطور که از اسمش پیداست ، ویجتی هست که گسترش میده یک Child  رو در داخل Row یا Column برنامه فلاتری ما...در مثال پایین متوجه کاربرد این ویجت میشیمدر بالا دومین container رو wrap کردیم با یک ویدجت Expanded  و مشخصه width  رو هم حذف کردیم خود به خود تشخیص میده و خودش رو گسترش و یا بسط میده در اون ویجتی که هست و کل صفحه رو در بر میگیره.در مثال پایین سه تا Container رو wrap میکنیم به Expanded و همونطور که نتیجه رو مشاهده میکنید سه تا container  رو به صورت برابر در عرض صفحه تقسیم میکنه،با استفاده از Expandedویجت Expanded یک مشخصه به اسم flex داره که وقتی ما یک مقدار به این flex میدیم به طور غیر یکنواخت ویجت های مارو در اون سطر یا ستون ما گسترس میده،مقدارش به طور پیش فرض یک هستمثال پایین رو مشاهده کنید.ما در اولین ویجت مقدار flex رو ۴ قرار میدیم و دومی رو برابر۲ و سومی رو هم یک قرار میدیم..که در مجموع برابر ۷ میشه،یعنی عرض صفحه ما ۷ بخش میشه و container اولی ۴ بخشش رو به خودش اختصاص میده ..نکته ای که هست اینه که اگر عددی قرار ندیم خودش مقدار پیشفرضش رو برابر یک قرار میدهویجت Flexibleویجت Flexible کاملا شبیه به Expanded هست و یک تفاوت داره که در مثال های پایین به اون اشاره میکنیم، همینطور که مثال زیر رو مشاهده میکنید یک سطر داریم که سه Container رو در اون قرار دادیم و به ترتیب مثل مثال Expanded مقدار دهی کردیم Flex های هر ویجت Flexible رو که در مجموع شدن شش قسمت و اولی سه قسمت صفحه رو پوشش داده..اگه که ما بخوایم به یکی از Container ها ارتفاع دلخواه بدیم، طبق کد پایین که با فلش مشخص کردیم،و اومدیم ارتفاع رو برابر ۱۵۰ قرار داریم،ویجت کاری نداره با مشخصه Flex  و این مقدار رو به عنوان ارتفاع این ویجت قرار میده و کاری نداره با اینکه کل صفحه رو پوشش میده یا نه...در Expanded ما نمیتونیم مثل بالا وقتی از flex استفاده میکنیم height رو بر اساس مقدار دلخواه خودمون مقدار دهی کنیم.اگه بخوایم ویجت رو مجبور کنیم کل فضای خالی صفحه رو بگیره از مشخصه fit استفاده میکنیمدوتا گزینه داره FlexFit.tight و گزینه پیشفرضی اون Flexfit.losse هست که تفاوتشون در اینه که وقتی از این مشخصه استفاده میکنیم کل صفحه رو پوشش میده و دیگه کاری با سایزی که ما میدیم به Container نداره...ولی وقتی از Flexfit.loose استفاده میکنیم میتونیم هر کدوم از ویجت های دلخواه خودمون رو مقدار دهی کنیم.در Flexible آزادی عملی برای کنترل گسترش ویجت ها نسبت به Expanded داریم.تفاوت Expanded و Flexible هم این متد Fit هست.یه مثال ساده دیگه برای درک کامل تر،در مثال پایین یه container داریم و یک Flexible  که درون خودش یک Container داره و ارتفاع هردو اون ها برابر با ۲۰۰ هستحالا وقتی ما بیایم و Container دومی رو بزرگترکنیم ارتفاعش ..Flexible ارتفاع خودش رو نادیده میگیره و براساس Container سایزش تغییر میکنه.ویجت SafeArea :همینطور که در تصویر زیر مشاهده میکنید این ویجت برای این هست که بدنه برنامه ما زیر استاتوس و ... قرار نگیره و یک مقداری از بالا فاصله داشته باشه،خیلی راحت میتونیم بدنه رو با این ویجت wrap کنیم.ویجت LayoutBuilder:ویجت کاربردی هست که با استفاده از اون میتونیم به  BoxConstraints هر ویجت دسترسی پیدا کنیم که ‌BoxConstraint  مشخص کننده طول و عرض کامل ویجت هست و برای متوجه شدن تفاوت LayoutBuilder و MediaQuery ابتدا مثال زیر رو ببینید.در مثال بالا ما یک ستون داریم که صفحه رو به دو قسمت تقسیم کردیم که مقدار مدیا کوئری توی هر دو قسمت برابر هست ولی مقدار LayoutBuilder فرق میکنه به این خاطر هست مدیا کوئری عرض کل صفحه رو حساب میکنه ولی در لایوت بیلدر مقدار عرض اون ویجتی که در اون هستیم رو نمایش میده، برای مثال عرض قسمت بنفش رنگ رو برابر ۱۵۶ قرار داده که جمعش با عرض قسمت سفید رنگ برابر میشه با عرض کل برنامه..معرفی چند منبع برای یادگیری کامل تر این مطالب: https://docs.flutter.dev/development/ui/layout/adaptive-responsivehttps://blog.codemagic.io/building-responsive-applications-with-flutter/https://www.youtube.com/watch?v=WUZVO1CLXqwhttps://medium.com/flutter-community/flutter-widgets-expanded-flexible-acc945829584ممنون که همراه من بودین تا پایان این آموزش امیدوارم که مورد پسندتون قرار گرفته باشه و لایک و کامنت شما باعث خوشحالی من میشههرجایی نکته نیاز به تصحیح داشت و یا کم گفته بودم ممنون میشم توی کامنت ها اطلاع بدین که پست رو اصلاح کنم.موفق باشیدیاعلی</description>
                <category>Persian Flutter</category>
                <author>ایمان یاراحمدی</author>
                <pubDate>Mon, 05 Sep 2022 16:07:14 +0430</pubDate>
            </item>
                    <item>
                <title>کتابخانه Drift که بود و چه کرد؟ (2)</title>
                <link>https://virgool.io/flutter-community/%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86%D9%87-drfit-%DA%A9%D9%87-%D8%A8%D9%88%D8%AF-%D9%88-%DA%86%D9%87-%DA%A9%D8%B1%D8%AF-ngzalr7jumuh</link>
                <description>نحوه نوشتن کوئری در Driftمدیریت داده در فلاترقسمت دومدر قسمت اول به نحوه تعریف جداول در Drift پرداختیم. دیگه واقعا نوبت نوشتن کوئری هاست. خب میدونید که 4 نوع عملیات داریم؛ select، insert، delete و update. علاوه بر این هم clause های مختلفی توی SQL داریم، where clause، join clause و.. . همه­ ی این کار ها به سادگی با چند تا تابع قابل انجامه. در ادامه برای هرکدوم یک ریز مثالی نوشتیم؛ تو خود حدیث مفصل بخوان از این مجمل... در قدم اول سراغ insert میریم. کد زیر یک category به جدول Category اضافه میکنه. ساده است فقط باید دقت داشته باشید که تابع insert یک کتگوری از نوع Companion دریافت میکنه.insert in Driftنحوه update یک رکورد هم به آسونی A,B,C هست. خود Drift در نظر میگیره اونجاهایی PK ها برابرند اپدیت رو انجام میده. خلاف تابع insert، این تابع ورژن Dataی کتگوری رو دریافت میکنه.update in Driftمورد بعدی درمورد حذف یک رکورد از جدول هست. میدونید دیگه برای حذف رکورد نیاز به شرط داریم که اشتباهاً همه ی رکورد ها رو حذف نکنیم. یکی از جاهایی که تابع where میتونه خودنمایی کنه دقیقاً همین جاست که حذفیات رو مشخص کنیم.Delete in Driftو در نهایت نوبت به select می رسه. کوئری که در شکل پایین می بینید برای جستجو بین Note هاست. علاوه بر اون من بنا به نیازی که توی پروژه ام بوده، دوتا جدول رو باهم دیگه join کردم؛ انواع join ها هم وجود داره. select in Driftیک نکته مهم اینه که وقتی شما دو تا جدول رو باهم join می کنید، خروجی کوئری شما هیچکدوم از اون دو جدول قبلی نیست، در چنین شرایطی Drift به شما لیستی از TypedResult برمیگردونه که خودتون زحمت باید parse کردن دیتا رو بکشید. این اتفاقات لزوماً موقع join رخ نمیده؛ ممکنه شما در یک کوئری تصمیم بگیرید یک ستون به خروجی تون (مثلاً به علت استفاده از Aggregation function ها یا ...) اضافه کنید. اینجا هم نتیجه به parse کردن نیاز داره.نحوه Parse کردن TypedResult به خروجی دلخواهمی بینید که خروجی هر دو جدول رو میشناسه و می تونید به Column های هرکدوم جداگانه و به سادگی دسترسی پیدا کنید.امیدوارم براتون مفید بوده باشه و بدانید و آگاه باشید که امکانات این کتابخانه به اندک نکاتی که طی این 2 مقاله بهش اشاره شد محدود نمیشه! راستی! من در این پروژه و این ریپازیتوری گیتهاب  از Drift استفاده کردم، دوست داشتین ببینیدش و بهش Star بدید ;)در آخر، اگر تمایل داشتید درمورد این کتابخونه جذاب و جالب بیشتر مطالعه داشته باشید، حتما به داکیومنتش سر بزنید (و أوصیکم بالقرائت داکیومنت).</description>
                <category>Persian Flutter</category>
                <author>yalda student</author>
                <pubDate>Sat, 27 Aug 2022 17:27:36 +0430</pubDate>
            </item>
                    <item>
                <title>کتابخانه Drift که بود و چه کرد؟ (1)</title>
                <link>https://virgool.io/flutter-community/%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86%D9%87-drfit-%DA%A9%D9%87-%D8%A8%D9%88%D8%AF-%D9%88-%DA%86%D9%87-%DA%A9%D8%B1%D8%AF-nen1bxtgppbh</link>
                <description>مقدمه ای بر کتابخانه Drift در Flutterقسمت اولتوی این مقاله میخوام در مورد دیتابیس Drift صحبت کنم. سیستم های مدیریت داده متعدد و متفاوتی داخل فلاتر وجود داره که ممکنه کم و بیش با اونا آشنایی داشته باشید؛ مثل Drift، Hive، Sqflite، GetX Storage و.. .بعضی از این DBMSها از نوع Relational  هستند؛ مثل Sqflite و Drift، بعضی دیگه شون هم مثل Hive از مدل NoSql هستند. هرکدوم از این ها مزیت و عیب خودشون رو دارند که فعلا مورد بحث ما نیست :)از هرچه بگذریم سخن Drift خوشترست..کتابخونه Drift (که قبلا از اسمش Moor بوده) مخصوص برنامه های Dart و Flutter هست که از همه ی پلتفرم ها هم پشتیبانی میکنه. برنامه نویس به دو صورت میتونه کوئری هایی که درنظر داره رو بنویسه؛ هم به زبان  Dart و هم SQL. اولین کار اینه که این پکیج به پروژه مون اضافه کنیم:نصب:dependencies:
  drift: ^2.0.2+1
  sqlite3_flutter_libs: ^0.5.0
  path_provider: ^2.0.0
  path: ^1.8.2

dev_dependencies:
  drift_dev: ^2.0.2
  build_runner: ^2.2.0شاید فکر کنید واقعا نصب همه ی این پکیج ها لازمه؟ باید بهتون بگم بله بله؛ drift که خود دیتابیس مون هست. drift بر مبنای sqlite3ساخته شده sqlite3_flutter_libs بیشتر برای پلتفرم های موبایل کاربرد داره، اگر شما از فلاتر استفاده نمی کنید، می تونید (با مسئولیت خودتون) نصبش نکنید. این دیتابیس هایی که گفتیم همه شون اطلاعات روی دستگاه به صورت لوکال ذخیره میکنن و طبیعتاً باید یه جای درست و حسابی برای دیتا پیدا بشه، برای همین پکیج های path_provider و path رو به پروژه اضافه می کنیم. دو تا پکیج آخر رو هم برای جنریت شدن کد ها و کوئری ها لازم داریم.حالا که Drift رو نصب کردیم، کار اصلی شروع میشه. بالاتر بهتون گفتم که به دو روش میشه دیتابیس رو طراحی کرد و کار رو پیش برد. در روش اول از زبان Dart کمک می گیریم:اولین کاری که باید انجام بدیم، پیاده سازی جدول(ها)مون هست. به مدد این کتابخونه، خیلی شیک Table ها طراحی میشن:جدول Category:جدول Categoryجدول  Note:جدول Noteباتوجه به اینکه نوع ستون های جدول چی هست می تونید از IntColumn، TextColumn، BoolColumn، DateTimeColumnو RealColumnاستفاده کنید. اگر بخواهید برای اون فیلد مقدار دیفالت تعریف کنید از تابع withDefaultاستفاده می کنید و مقداری که مدنظر دارید رو بهش پاس می دید. تابع namedزمانی عرض اندام می کنه که تمایل داشته باشید اسم یک فیلد توی پایگاه داده متفاوت از اسم getter اون باشه. بقیه موارد بنظر واضح میاد تا برسیم به مبحث کلید های اصلی و خارجی.برای تعریف کلید(ها) اصلی جدول تون می تونید primaryKeyرو override کنید و ستون های لازم رو بهش معرفی کنید؛ به همین راحتی، به همین خوشمزگی.اگر داخل جدول تون فیلدی داشته باشید که رفرنسی از یک جدول دیگه باشه (همون Foreign key خودمون)، برای اون هم یک تابع reference داریم که یک چندتا پارامتر اجباری و اختیاری داره. ورودی های اجباری اون اسم جدول مبدا و اسم ستون هست. دو تا پارامتر اختیاری هم برای Referential Actions داره که با استفاده از KeyActionمیتونید اکشن مدنظر تون رو استفاده کنید.خب حالا که جدول ها مون رو تعریف کردیم یه کلاس لازم داریم که دیتابیس مون رو مدیریت کنیم؛ این شکلی:database.dartداخل DrfitDatabase@ باید جدول ها و viewهایی(اگر داریم) که تعریف کردیم به این کلاس معرفی کنیم. در صورتی که بخوایم زمانی که دیتابیس داره ساخته میشه یا اپدیت میشه و.. کانفیگ های به خصوصی انجام بدیم، میتونیم تابع migration رو override کنیم. این تابع یک MigrationStrategyبرمیگردونه که 3 تا تابع در ورودی به صورت دلخواه دریافت میکنه؛ onCreate،  onUpgrade و  beforeOpen.یادتون نره این کامند رو باید اجرا کنید، تا فایل database.g.dart تولید بشه:flutter pub run build_runner buildبعد از اینکه این فایل جنریت شد، به ازای هر کدوم از این جدول هایی که تعریف کردیم، دوتا کلاس تولید میشه (مثلا NoteData و NoteCampanion) که هرکدوم کاربرد خودشو داره.توی پست بعدی، نحوه ی پیاده سازی کوئری ها رو بررسی میکنیم ;)  تا اینجای کار مرسی که خوندین، منتظر نظراتتون هستم *_*</description>
                <category>Persian Flutter</category>
                <author>yalda student</author>
                <pubDate>Sat, 27 Aug 2022 13:21:54 +0430</pubDate>
            </item>
                    <item>
                <title>استفاده از dartz برای ارور هندلینگ در فلاتر</title>
                <link>https://virgool.io/flutter-community/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-dartz-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A7%D8%B1%D9%88%D8%B1-%D9%87%D9%86%D8%AF%D9%84%DB%8C%D9%86%DA%AF-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-jfb8pldlykq8</link>
                <description>به عنوان یه توسعه دهنده فلاتر تاحالا براتون پیش اومده خواسته باشید ارور ها داخل اپلیکیشن خودتون کنترل کنید و بعضی هاش به کاربر نشون بدین یا بخوایید در صورت رخداد اون ارور یه فانکشن دیگه رو صدا بزنید یا کار دگیه  رو انجام بدید.همونجوری که همه ما میدونیم برای انجام یه کار تو برنامه نویسی هزار تا روش هست، امروزمن میخوام بهتون بگم که چجوری از یه پکیج جالب به اسم dartz برای هندل کردن ارور ها استفاده کنید امیدوارم که براتون مفید واقع بشه. خوب کارمون رو با اضافه کردن پکیج به پروژمون شروع میکنیم .flutter pub add dartzخوب برای ادامه کار نیاز داریم که یه مدل برای ارور هایی که بهشون بر میخوریم بسازیم این مدل هم معمولا چیز انچنانی هم نداره،بریم برای ساختن مدل ارور قدم بعدی ما تایین مدل اصلی که ما ازش دیتا رو میگیرم و داخل سرتا سر اپلیکیشن استفاده میکنیم،اینجا دیگه مرتبط میشه به اپلکیشین خودتون و دیتا هایی که باهاش در ارتباط هستید. اینجا شما متناسب بادیتاهایی که میگیرید یک کلاس مدل میسازید.اینجا اسم کلاس مدل من Note هستش.خوب تا اینجا ما تمامی موارد موردنیاز خودمون رو ساختیم، وقتشه که بریم سرغ اصل مطلب از این پکیج استفاده کنیم.خوب  اینجا باید بگم که استفاده از این پکیج صرفا برای روش خاصی نیست و شما میتونید ازش برای ارتباط با سرور یا دیتابیس داخلی استفاده کنید .برای استفاده از این پکیج شما در اول تایین مقدار برگشتی تابع خودتون که به صورت جنریک میدین باید از تایپ  Either استفاده کنید ،زمانی که شما این تایپ رو بنویسید از شما میخواد که دوباره ۲ تا مقدار به صورت جنریک به خود این Either بدین که اولی باید مدل اروری که ساختیم رو بدین و دومی مدل کلاس اصلی مدل هستش به صورت زیر خوب حالا اینجا شما باید کد های مربوط به عملیات مورد نظر خودتون بنویسید و مثل همیشه همونtry و catch قدیمی خودمون میزاریم . اینجا برای همه ما سوال پیش میاد خوب الان این فانکشن داره ۲ تا تایپ بر میگردونه از کجا بدونیم که کدوم باید کجا برگرده و ازشون استفاده کنیم ؟صبر کنید ! نردبون پله پله خوب فرض کنید که کد شما در قسمت  try عملیات به صورت کامل اجرا شده و همچیز درست و شما میتونید مقدار اصلی مدل رو برگردونید و هیچ اروری نیست، برای برگردوندن مقدار برگشتی درست ما از Right استفاده میکنیم  برای مقدار برگشتی ارور ما   Left رو برمیگردونیم. حالا این right و left چی هستن ؟ دقت کنید که ما در جنریک اول  Either  ما مدل ارور رو  دادیم و جنریک دوم مدل اپلیکیشن اگه دقت کنید میبینید که Left به معنی چپ اشاره به مدل ارور داره و right هم به مدل اصلی اشاره داره خوب تا اینجا ما فانکشن رو با استفاده از dartz پیاده کردیم حالا وقت استفاده ازش داخل کد هامون رسید.سوال بعدی که برامون پیش میاد اینه اقا چجوری میتونیم تشخیص بدیم که این فانکشن که صدا زدیم مقدار right برگردونده یاleft ??خوب اول بیاید از این کدی که نوشتیم استفاده کنیم تا بعد بهتون نشون بدم چجوری ازش استفاده کنید.خوب تا اینجا فقط ازش استفاده کردیم ،برای دریافت مقدار های برگشتی از این فانکشن ما از متد flod  استفاده میکنیم. این متد زمانی بنویسید به شما به صورت پوزیشنال ۲ تا فانکشن  با مقدار های برگشتی یکی مدل ارور و یکی  مدل اصلی میده ، اگر فانکشنی که ما بالا صدا زدیم به ارور بخورد فانکشن برگشتی با مدل ارور صدا زده میشه و کد های داخل اون صدا زده میشه اگر هم مقدار برگشتی درست باشه (right) فانکشنی صدا زده میشه که مدل اصلی برمیگردونه  و کد های داخل اون فانکشن صدا زده میشه .خوب اینجا ما ارور ها رو هندل کردیم و خیلی شیک تونستیم اونها رو کنترل کنیم و از اینجا به بعدش به خودوتن بستگی داره که چجوری از مقدار های برگشتی استفاده کنید لذتش ببرید  ، برای درک بیشتر از این پکیج و نحوه استفاده  ازش بهتر چند بار مقاله رو بخونید و طبق مراحل پیش برید ازش نهایت لذت ببریدخوب به پایان آمد این دفتر ولی مقاله های ابوالفضل همچنان ادامه داره.امیدوارم براتون جذاب بوده باشه و مفید، اگر اینطور بوده یادتون نره مقاله رو لایک کنید و برای بقیه دوستاتون هم بفرستید تا اینجوری هم حمایتی از من کرده باشید .دمتون گرم ،خدا نگهدار </description>
                <category>Persian Flutter</category>
                <author>Abolfazl mashhadi</author>
                <pubDate>Mon, 22 Aug 2022 22:07:16 +0430</pubDate>
            </item>
                    <item>
                <title>تفاوت bloc ،provider و getx در فلاتر</title>
                <link>https://virgool.io/flutter-community/%D8%AA%D9%81%D8%A7%D9%88%D8%AA-bloc-provider-%D9%88-getx-%D8%AF%D8%B1-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-mtf1pcrlnkso</link>
                <description>اگر داری این مقاله رو می‌خونی یعنی تو هم مثل من خیلی درگیر این بودی که تفاوت این سه‌تا state management یعنی bloc ،provider و getx رو بدونی، اینکه کدوم بهتره و باید از کدوم توی پروژه خودت استفاده کنی.خب بیایم یک نگاه به هر کدوم بندازیم و ببینم چه ویژگی‌ها و نقطه ضعف‌هایی دارن.شماره یک blocاحتمالا bloc بهترین راه‌حل برای state management ها است که از طریق اون می‌تونید الگوهای معماری رو هم یاد بگیرید.مزایا:۱- جدا شدن کامل منطق پروژه۲- نگهداری، تست نویسی و توسعه آسان پروژه۳- تیم حرفه‌ای توسعه دهنده۴- وجود اسپانسر برای این پکیج۵- داکیومنت قوی و وجود کدهای سمپل زیادمعایب:۱- کدنویسی بیشتر نسبت به رقیبان۲- سرعت توسعه پایین‌تر از رقیبان بخاطر دلیل شماره یک۳- یادگیری سخت‌تر بخاطر پیچیده‌تر بودن نسبت به پکیج‌های رقیب (دلیل اصلی که تازه کارهای فلاتر میرن سمت رقیب‌هاش)آمارها:تعداد ۹,۲۰۰ ستاره در githubتعداد ۴,۱۰۰ لایک در pub.devشماره دو getxاحتمالا getx به عنوان دومین راه حل محبوب برای state management ها است که یکی از بحث برانگیزترین و پر حاشیه ترین‌ها در بین پکیج‌های فلاتر است. این پکیج به دلیل مشکلاتی که در تست نویسی و اختلال در الگوهای معماری باعث شده تا گروه زیادی از توسعه دهنده‌های فلاتر از اون متنفر باشن، اما همزمان بخاطر سرعت زیاد و کوتاه بودن کدنویسی بخش زیادی از برنامه نویس‌ها هم به اون علاقه دارن.مزایا:۱- کدنویسی بسیار آسان (دلیل اصلی محبوبیت بین تازه‌کارهای فلاتر)۲- کدنویسی بسیار کوتاه۳- محبوبیت زیادمعایب:۱- در حقیقت این پکیج رو نمیشه یک state management نام گذاری کرد و بیشتر یک میکرو فریمورک است که هر چیزی رو در درون خودش داره، مثل مدیریت روت‌ها تا هر چیز دیگه. و این وابستگی پروژه شما رو به این پکیج بسیار زیاد میکنه.۲- بزرگ بودن بیش اندازه پکیج که باعث شده مشکلات زیادی رو درون خودش داشته باشه.۳- نداشتن اسپانسر که باعث میشه هر لحظه برنامه نویس اون پکیج بیخیال توسعه اون بشه.۴- فعال نبودن توسعه دهنده اون به صورت مستمر.۵-وقتی یک باگ در این پکیج پیدا میشه و به توسعه دهنده گذارش میشه، زمان زیادی طول میکشه تا اون رو برطرف کنه.۶- نداشتن مستندات واقعی و صحیح۷- شهرت بد در جامعه فلاترآمارها:تعداد ۶,۹۰۰ ستاره در githubتعداد ۹,۴۰۰ لایک در pub.devشماره سه providerپکیج provider برای state management از طریق InheritedWidget است که به روشی ساده این کار رو میکنه. باید بدونید در پکیج bloc هم از provider  استفاده شده. شما در provider میتونید به راحتی از Triple, ValueNotifier, ChangeNotifier و دیگر آبجکت‌ها استفاده کنید. این پکیج توسط تیم رسمی فلاتر توسعه داده میشه و به عنوان اولین پکیج مدیریت استیت ها از طرف فلاتر توصیه شده. مزایا:۱- کدنویسی سریع۲- کدنویسی آسان۳- محبوبیت زیاد۴- پشتیبانی و توسعه توسط تیم فلاتر. این یعنی تا فلاتر هست، provider  هم توسعه پیدا میکنه و هست. پس خیالتون ازش راحت.معایب:والا خیلی گشتم نبود. اگر شما میدونید برام بنویسید.آمارها:تعداد ۴,۳۰۰ ستاره در githubتعداد ۶,۸۰۰ لایک در pub.devنتیجه گیریخب حالا که به نقاط قوت و ضعف هر سه‌تای عزیز دل نگاه کردیم بریم سراغ منبر من. ببینید اینجای کار تصمیم گیرنده خودتونید، باید ببینید میخواید چه پروژه‌ای بزنید. اگر زمان مهمه و پروژه کوچیکه پس سرعت عمل مهمه، اما اگر پروژه بزرگه و قراره توسعه طولانی مدت داشته باشه جواب مشخصه.برای راحت شدن فکر شما هم باید بگم شخصاً بعد این همه بررسی و تجربه کار با این پکیج ها به این نتیجه رسیدم که در پروژه جدید (تقریبا اندازه دیوار) که قراره انجام بدم از provider  استفاده کنم. چون باید یک پروژه نسبتاً بزرگ رو توی کمتر از چند ماه تحویل بدم پس سرعت عمل، راحتی و توسعه در آینده برام مهمه و اینها همه در provider وجود داره.باید بدونید در خیلی از بخش های پروژه اصلا نیازی به هیچ کدوم از این پکیج ها نیست و میتونید با StateFulWidget مشکلتون رو حل کنید. در نهایت اینم بگم که هر چی ابزار به وجود میاد فقط و فقط برای اینه که به ما کمک کنن. پس از این ابزارها استفاده کنید و لذت کار با اونها رو ببرید.خوشحال میشم نظرتون رو در مورد این پکیج‌ها و مقاله من بنویسید. راستی اینم بگم این اولین مقاله من در ویرگول بود. امیدوارم که برای شما مفید واقع شده باشه.</description>
                <category>Persian Flutter</category>
                <author>mohammad masoudi</author>
                <pubDate>Wed, 06 Jul 2022 23:46:44 +0430</pubDate>
            </item>
                    <item>
                <title>بهترین منابع یادگیری فلاتر(فارسی و انگلیسی)</title>
                <link>https://virgool.io/flutter-community/%D8%A8%D9%87%D8%AA%D8%B1%DB%8C%D9%86-%D9%85%D9%86%D8%A7%D8%A8%D8%B9-%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%81%D9%84%D8%A7%D8%AA%D8%B1%D9%81%D8%A7%D8%B1%D8%B3%DB%8C-%D9%88-%D8%A7%D9%86%DA%AF%D9%84%DB%8C%D8%B3%DB%8C-aytecdlsy1v3</link>
                <description>سلام امیدوارم که حالتون خوب باشه...قبلا در مورد بهترین منابع یادگیری برنامه نویسی اندروید  یه پست منتشر کردم که میتونید اینجا پست رو ببینید...ابتدا بهتره یک توضیح کوتاه در مورد فلاتر بدیم و بعد منابع یادگیری فارسی و انگلیسی فلاتر رو معرفی کنیم.چیه این فلاتر:سال ۲۰۱۷ گوگل یک فریم ورک رونمایی کرد به اسم فلاتر که با استفاده از این فریم ورک یه بار کد نوشته میشه و با پرفورمنس بالا همزمان چند خروجی ازش گرفته میشه(ios,android,windows,mac) و حتی بعضا میشه افزونه مرورگر هم نوشت(زیبا نیست؟) و اگه بخوایم به صورت عادی این همه خروجی بگیریم باید برای هر پلتفرم زبان برنامه نویسی و محیط برنامه نویسی مخصوص اون زبان رو بلد بود مثلا برای اندروید زبان برنامه نویسی جاوا یا کاتلین  و برای ios زبان swift  رو بلد بود و ...زبان توسعه فریم ورک فلاتر دارت هست که زبان برنامه نویسی شی گرایی سطح بالا هست و به زبانی مثل جاوا شباهت داره،خیلی قدرتمنده و اگه که شما قبلا با یکی از زبان های برنامه نویسی کار کرده باشین خیلی راحت میشه این زبان رو یادگرفت...فلاتر چه مزیتی داره که بریم یادش بگیریم ؟۱- همزمان چند خروجی میشه ازش گرفت۲- سرعت توسعش خیلی بالاتره و یادگیریش راحت تره نسبت به نیتیو۳- یه سری قابلیت مثل hot reload داره که وقتی تغییری در کد میدیم خیلی سریع بدون نیاز به بیلد گرفتن طولانی که زمان زیادی میگیره خروجی کد رو میینیم...۴- سرعت رشد و توسعش خیلی بالاست و افراد متخصص زیادی توی دنیا دارن به رشد این فریمورک کمک میکنن و خیلی از برنامه نویس ها نسبت به ری اکت نیتیو ترجیحش میدن زبان های نیتیو قدرت و سرعت اجرای بیشتری دارن نسبت به فلاتر و اینکه خیلی وقت نیست فلاتر مطرح شده و هنوز خیلی از شرکت های بزرگ  سمتش نرفتن که برنامه هاشون رو با فلاتر توسعه بدن ولی روز به روز دارن بیشتر میشن اپ هایی که با فلاتر توسعه داده شدن..و فرصت شغلی زیادی همین الان توی ایران هم برای فلاتر وجود داره...میشه خیلی  بیشتر هم توضیح داد در مورد فلاتر ولی به همین بسنده میکنم... و میتونید با سرچ در موردش به  مزایا و معایبش دسترسی پیدا کنید.خوب منی که در حال حاضر دارم این مطلب رو مینویسم خودم هم در حال یادگیری با استفاده از این منابع هستم و سعی کردم بهترین منابع رو پیدا کنم و خودم یادبگیرم و به شما معرفی کنم ولی به این مطلب بسنده نکنید و خودتون هم جست و جو کنید و اگه منبع خاص دیگه ای رو برای یادگیری سراغ داشتین کامنت بزارین که دوستان استفاده کنن..اولین منبعی که خیلی کامل و جامع فلاتر رو از صفر شروع کرده و خیلی خوب توضیح میده آنجلا از سایت یودمی هست که اینجا میتونید دانلود کنید، به نظرم اگه زبان انگلیسی رو در حد ابتدایی بلد هستین از این اموزش شروع کنید چون خیلی قشنگ و خوب در قالب مثال و ساخت اپ های گوناگون فلاتر رو یاد میدهاموزش فلاتر یودمیدومین اموزشی که بعد از دیدن مجموعه بالا و یا بهتره گفت بعد از آشنایی مقدماتی با فلاتر پیشنهاد میکنم اموزش ساخت اپ نوت با فلاتر هست که مدرسش ونداد نهاوندی پور هست که به زبان انگلیسیه و خیلی کامل در مورد خیلی از چیزا مثل مقدمات دارت،مقدمات فلاتر،اتصال به فایربیس،اتصال به گیت و خیلی دیگه از مسائل صحبت میکنه که خیلی فوق العاده هست این اموزش و ۳۶ ساعت اموزش پیوسته هست که کانال معتبر freecodecamp در یوتیوب منتشرش کرده و میتونید از اینجا اموزش رو ببینیداگه ۲ اموزش بالا رو با دقت مشاهده کنید و این هارو تمرین کنید که قطعا چند ماهی زمان میبره قول میدم که راحت میتونید کار پیدا کنید و نیازی به مشاهده مجموعه اموزشی دیگه ای ندارین ولی چون هدف پست معرفی بیشتر منابع معتبر و معروف هست پس ادامه میدیم.سومین منبع میشه داکیومنت اصلی خود فلاتر رو نام برد که خیلی دقیق و کامل اموزش داده ولی شخصا خودم فقط برای بعضی از مطالب به این سایت رجوع میکنم و چون خیلی جزيی هر مطلبی رو توضیح داده،ممکنه شروع یادگیری از اینجا سخت و یه مقدار گیج کننده باشه...چند تا کانال معروف یوتیوب هست که اموزش های فلاتر رو میزارن که در پایین لینکشون رو میزارم و شما میتونید سر بزنید و ببینید اموزش های مختلفشون رو 1 - Afgprogrammer 2 -  Devefy3 -  Flutter4 - Johannes Milke5 -  Abdul Aziz Ahwan6 - The Flutter Way7 - Code With Andrea8-  Flutter Mapp9 - Santos Enoque10 - Flutter Ninjaکانال های مختلفی رو در بالا معرفی کردیم که هرکدوم اموزش های مختلفی دارن که برای یادگیری ui و یا کانال رسمی فلاتر برای مشاهده ابزارها و امکانات جدید فلاتر میتونید از این کانال ها استفاده کنید و بدون شک کانال های خوب دیگه ای هم وجود داره که ممکنه من ندیده باشم و یا فراموش کرده باشم اینجا بزارم...خوب حالا بریم سراغ اموزش های فارسی که منتشر شده و نسبت به بقیه اعتبار بیشتری دارناولین اموزش که معرفی میکنیم....اموزش فلاتر موسسه ۷ لرن هست که حقیقتا این موسسه فوق العاده هست توی هر زمینه ای و این آموزش با تدریس سعید شاهینی هست که توی پست مربوط به یادگیری اندروید هم ایشون رو معرفی کردم از هر نظر فوق العاده هستن و ایشون اخیرا اپ میلیونی تلوبیون رو با فلاتر بازنویسی کردن و الان برنامه نویس اندروید دیوار هستن و همین میتونه نوید اموزش فوق العاده و تجربه بالای ایشون باشه....لینک دوره رو  اینجا میتونید ببینید.دومین دوره دوره اموزش فلاتر موسسه اندروید لرن هست که اقای فرزاد سر سیفی مدرس هستن که من از تسلط ایشون زیاد شنیدم که اینجا میتونید به اطلاعات دوره دسترسی داشته باشینسایت های دیگه ای مثل فلاترلرن و راکت  و فرادرس هم هستن که اموزش فلاتر رو در سایتشون دارن و خیلی از سایت های ایرانی دیگه که ممکنه اموزش داشته باشن و بنده از اون ها بی اطلاع باشمبه عنوان جمع بندی پایانی مطلب اینکه سعی کردم بهترین منابعی رو که میشناسم معرفی کنم و معرفی منابع مخصوصا منابع فارسی به معنی تایید صددرصدی نیست و شما خیلی راحت میتونید اعتبار سایت منتشر کننده سوابق کسی که تدریس میکنه و نمونه ویدیو دوره رو ببینید و بعد اقدام به خرید کنید و اگه واقعا در سطح ابتدایی با زبان انگلیسی اشنایی دارین و متوجه بیشتر صحبت ها به زبان انگلیسی میشین همون دوتا منبع اول خیلی عالی هستن و واقعا نیاز به پرداخت هزینه ای نخواهید داشت.در اخر هم ممنون که این مطلب رو تا اخر مطالعه کردین و اگه منبع خوب دیگه ای میشناسین لطف کنین در قسمت کامنت ها معرفی کنید که دیگر دوستان هم استفاده کنند..موفق باشیدیاعلی</description>
                <category>Persian Flutter</category>
                <author>ایمان یاراحمدی</author>
                <pubDate>Tue, 28 Jun 2022 01:26:18 +0430</pubDate>
            </item>
                    <item>
                <title>تا حالا شده در زبان شی گرا از  class func استفاده کنی؟</title>
                <link>https://virgool.io/flutter-community/class-function-in-swift-csxrb3k3e8z9</link>
                <description>سلامخوشحالم که دوباره فرصت پیدا کردم تا یک مطلب فوق العاده دیگه باهاتون به اشتراک بگذارم.توی زبان swift ما یک چیزی داریم به اسم class func که متفاوت از سینتکس هایی است که تا حالا  در زبان های شی گرا خوندیم.می خوام این مفهوم رو در زبان های سوئیفت و دارت و کاتلین و جاوا بررسی کنم.class A {
  class func firstFunction()  -&gt; String{
      return &amp;quot first &amp;quot}
  func secondFunction ()  -&gt; String{
       return &amp;quot second &amp;quot
}
} اما قبلش یک سری تعریف های ساده رو باهم مرور می کنیم.تعریف class :داخل کلاس می تونیم متغیر ها و متدها رو تعریف کنیمتعریف enum : می تونیم به وسیله enum یک type با مقادیر دلخواه بسازیم و از اونها استفاده کنیم. enum ها را نمی توانیم به ارث ببریم.سینتکس enum هم به این شکل بود.در زبان سوئیفت :enum Gender {
    case female
    case male  
}در زبان دارت : enum Gender {
    female , male 
}در زبان کاتلین البته enum ها از نوع کلاس هستند:enum class Gender {
    FEMALE , MALE  
}در زبان جاوا :enum Gender {
    FEMALE , MALE 
 } فراخوانی enum  هم به این شکل است :Gender.female یا Gender.FEMALEبرای مدل سازی   در زبان های مختلف هم یک سری مفاهیم داریم. مدل سازی در زبان سوئیفت از طریق structure ها انجام میشه که با کلیدstruct  مشخص میشه .اینکه فرق struct با class چیست و چرا در سوئیفت برای مدل سازی از کلاس استفاده نمی کنیم نیاز به توضیحات زیادی داره که توی این پست دربارش حرف نمی زنم اما لازمه بدونید که struct ها را نمی توانیم ارث ببریم.در struct و حتی enum ها یک کپی  از آنها پاس داده می شود اما در کلاس ها رفرنس پاس داده می شود.struct Person{
      var name =  &amp;quot your name &amp;quot
      init(name:String){
            self.name=name
}
}مدل سازی در زبان کاتلین از طریق data class بجای class استفاده می کنیم.data class Person(val name : String) مدل سازی در زبان دارت  تقریبا مثل زبان جاوا است و از کلاس استفاده می کنیم.class Person {
    final name String;
    Person({
         required this.name
}
);
}خب حالا که تا اینجا اومدیم بریم دوتا مفهوم دیگه که در ادامه بهش نیاز داریم را مرور کنیم.تعریف Instance methods :به متد ها یا همون function هایی که در class , struct , enum استفاده می کنیم instance method گفته می شود.در  این متد ها می توانیم به بقیه متغیر ها و متدهای دیگه در کلاس دسترسی داشته باشیم.class Counter {
    var count = 0
    func increment() {
        count += 1
}
     func increment(by amount: Int) {
           count += amount
}
     func reset() {
           count = 0
}
}هر سه متد از نوع instance method هستند. از طریق شی  کلاس Counter به instance method ها دسترسی پیدا می کنیم و در واقع یک instance از type خاص هستند.به عبارتی این متدها متعلق به شی کلاس هستند .let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter&#039;s value is now 1
counter.increment(by: 5)
// the counter&#039;s value is now 6
counter.reset()
// the counter&#039;s value is now 0تعریف Type methods :برخلاف  instance method که شما از طریق یک type خاص آنها را فراخوانی می کردید در اینجا متدها از طریق type خودشان صدا زده می شوند. در بیشتر زبان ها برای نشان دادن این نوع متد ها کلید static استفاده می کنیم.این متد ها متعلق به کلاس هستند.class SomeClass {
  class func someTypeMethod() {
// type method implementation goes here
}
   static func someType(){
}
}
//call :
SomeClass.someTypeMethod()
SomeClass.someType()همانطور که می بینید  در سوئیفت ما به دو روش می توانیم type method بسازیم که به یک شکل metaType هم فراخوانی شده اند.اما واقعا تفاوت در چیست؟!یک تفاوت جالب وجود دارد. شما نمی توانید متد های استاتیک را توسط کلاس دیگه به ارث ببرید.یعنی متد someType قابل ارث بری نیست.پس برای اینکه هم متد استاتیک داشته باشید و هم بتوانید آنرا به فرزند کلاس بدهید باید از کلید های class func برای اینکار استفاده کنید.یادآوری کنم که اگر می خواستید یک property در type method ها استفاده کنید باید آن   property هم از نوع استاتیک تعریف کنید تا در type method به آن دسترسی داشته باشید.class SomeClass  {
   static var name=&amp;quot Sepehr&amp;quot
   class func someTypeMethod() {
// type method implementation goes here
       name=&amp;quotAhmad&amp;quot
}
    static func someType(){
          name=&amp;quot Ali&amp;quot
}
}وقتی از کلاس SomeClass ارث بری کنیم  داریم :class  Foo : SomeClass {
  override class func someTypeMethod() {
}
}طرح یک پرسش : بنظرتون از class func می توانیم در enum و struct استفاده کنfluentu.comاگر جوابت بله است پیشنهاد می کنم یکبار دیگه این پست رو از اول بخونی  اگر جوابت خیر است بهت تبریک میگم.اصلا class func  زمانی کاربرد دارد  که بخواهیم متد ما هم از نوع استاتیک و هم قابل ارث بری باشد اما در enum ها و struct ها ما ارث بری نداریم.پس در این حالت برای ما کاربردی نیستاما  هنوز می توانیم type method را از طریق کلید static  برای enum و struct تعریف کنیم.حالا که متوجه شدیم class func چه کاربردی در سوئیفت دارد بیایید بررسی کنیم که معادل class func در زبان های دارت و کاتلین و جاوا به چه صورت است؟اصلا شدنی است؟یکبار دیگه مرور کنیم که در زبان های شی گرا متد های استاتیک قابل ارث بری نیستند.پس ما نمی تونیم مثل سوئیفت عمل کنیم که هم متد را به صورت metaType صدا بزنیم و هم بتوانیم آن متد را override کنیم و عملا برای class fun معادلی در دارت و جاوا و کاتلین وجود ندارد.ممنونم که تا آخر این پست با من بودید.خوشحال می شوم اگر موردی هست با من و بقیه به اشتراک بگذارید. لطفا نظر دهید و با ❤️ کردن از من حمایت کنید.مراجع :داکیومنت سوئیفت و stackoverflow.com</description>
                <category>Persian Flutter</category>
                <author>سپهر تابعیان</author>
                <pubDate>Sat, 18 Jun 2022 02:19:28 +0430</pubDate>
            </item>
                    <item>
                <title>فلاتر ۳ منتشر شد!</title>
                <link>https://virgool.io/flutter-community/flutter-3-lw4e9j8t0vv8</link>
                <description>منابع استفاده شده برای این مقاله:https://invertase.io/blog/please-welcome-flutter-3https://medium.com/flutter/whats-new-in-flutter-3-8c74a5bc32d0https://medium.com/flutter/introducing-flutter-3-eb69151622fhttps://docs.flutter.dev/whats-newhttps://docs.flutter.dev/development/tools/sdk/release-notes/release-notes-3.0.0تیم توسعه‌‌‌‌‌ٔ فلاتر دیشب در یک اقدام پیش‌بینی نشدنی، از نسخه ۳ فلاتر همراه با دارت ۲.۱۷ در Google I/O 22 رونمایی کرد.تیم اسنیث(Tim Sneath)، مدیر محصول فلاتر این نسخه رو اینگونه در پست مدیوم‌ش توصیف کرده:The culmination of our journey to multiplatform UI development on phone, desktop, and webفلاتر ۳ همراه با دش های عزیز:) خلاصه‌ای از تغییرات این نسخه:تمامی ۶ پلتفرم پشتیبانی‌شده توسط فلاتر در کانال پایدارپرفورمنس بهتر در فلاتروبپشتیبانی بهتر از دستگاه های فولدیبلپشتیبانی از متریال ۳(Material You) به صورت پایداراکستنشن‌های بیشتر برای کلاس تم جعبه‌ابزار برای بازی‌ سازیاضافه شدن بخش games به سایت فلاترپشتیبانی از  پردازنده اپل‌سیلیکونبهبود در فایربیس برای فلاترSupercharged enumsSuper constructorName args anywhereشروع سفر به فلاتر ۳طبق حرف‌های مدیر محصول فلاتر و تجزیه و تحلیل‌های شرکت‌های تحقیقاتی مثل data.ai و گواهی‌های عمومی، در حال حاضر بیش از ۵۰۰,۰۰۰ برنامه با فلاتر ‌ساخته شده‌؛ از شبکه‌های اجتماعی مثل WeChat تا برنامه‌های مالی و بانکی مثل Betterment و NuBank، از برنامه‌های تجاری مثل SHEIN و trip.com گرفته تا برنامه‌های سبک‌زندگی مثل Fastic و Tabcorp، از برنامه‌های همراه مثل My BMW گرفته تا موسسات عمومی مثل دولت برزیل!طبق سوالات و آمارها:۹۱٪ توسعه‌دهندگان فلاتر معتقدن که فلاتر، سرعت توسعه و انتشار برنامه‌هارو کاهش میده!۸۵٪ گفتند که فلاتر بهشون در ساخت برنامه‌های زیباتری بسازن!۸۵٪ گفتند که فلاتر باعث شده برنامه‌هاشونو روی بیشتر از ۱ پلتفرم منتشر کنن!سیر تحولات ریلیز های فلاتر در ۴ سال گذشته از فلاتر بتا تا نسخه ۳ پشتیبانی از تمامی پلتفرم‌ها در کانال پایدار(Stable)اندروید،‌ iOS، وب و ویندوز مدتیه که در کانال پایدار اضافه شدن و حالا وقت اضافه شدن بقیه پلتفرم‌های دسکتاپ بود، در نهایت ما Linux و macOS رو در لیست پشتیبانی در کانال پایدار داریم. فلاتر ۳ یکی از مهم‌ترین ریلیز های این فریمورک از ابتدای کاره، چون حالا تمامی ۶ پلتفرم پشتیبانی شده توسط فلاتر،‌ در کانال پایدار قرار دارن!6+1 Platform :)ویژگی های جدید در فلاتر وبچرخه‌حیات اپلیکیشن در وبیک API جدید برای کنترل کامل روی محتوا، فریمورک فلاتر و انجین اون، برای مثال  preloading محتوا حین نمایش صفحه لاگین یا یک پروگرس بار.رمزگشایی سریع‌تر تصاویر و اسکرولفلاتر ۳ تغییر بزرگی در اسکرول و رمزگشایی تصاویر داشته، به ویژه اگر از نسخه‌های ۹۹ به بالای کروم استفاده کنید. در واقع فلاتر برای این تغییر از API جدید web-codec در مروگر های جدید برای رمزگشایی سریع تصاویر استفاده کرده و اسکرول بهتری رو با شانس بالای ۶۰ فریم‌بر‌ثانیه حین رمزگشایی تصاویر معرفی می‌کنه.شخصی‌سازی لودینگ اولیهآیا شما یک توسعه‌دهنده وب هستید و می‌خواید کنترل بیشتری روی روند لودینگ اولیه وب‌‌اپتون داشته باشید؟‌ صفحه جدید داکیومنت فلاتر رو بخونید!تمامی این ویژگی هارو در فلاتر گالری میتونید ببینید: https://gallery.flutter.dev/ متریال ۳(Material You)گوگل در سال پیش از نسخه‌ جدید دیزاین سیستم Material رونمایی کرد و از همون روزها تیم فلاتر مشغول پشتیبانی ازش هستند(روند اون رو میتونید در این issue در گیت‌هاب ببینید)پشتیبانی از تمامی ویجت های متریال  ۳ در فلاترویجت‌هایی که برای متریال۳ به فلاتر اضافه شدن:Adaptive colorsNew buttonsNew app menuVariable font supportبرای استفاده از متریال ۳،‌ به کلاس ThemeData یک پارامتر به نام useMaterial3 اضافه شده:return MaterialApp(
      theme: ThemeData(useMaterial3: true),    
// ... );جعبه‌ابزار فلاتر برای بازی‌سازانفلاتر با استفاده از پشتیبانی‌گرافیکیش که شتاب سخت‌افزاری خوبی ارائه می‌ده و همچنین موتور های بازی‌سازی متن‌باز مثل Flame شروع کار رو برای توسعه‌دهندگان بازی‌های معمولی راحت‌تر می‌کنه!طبق گفته های اسنیث،‌ با اینکه فلاتر برای بازی های اکشن سه‌بعدی طراحی نشده ولی بعضی از این بازی ها برای رابط‌کاربری غیر بازی به فلاتر رو آوردند، از جمله بازی‌های محبوبی مثل PUBG Mobile که صدها میلیون کاربر دارند.هر ساله گوگل در مراسم Google I/O یک برنامه ساخته شده توسط توسعه‌دهندگان فلاتر رونمایی میکنه و امسالم از این قاعده مثتثنی نیست و یک بازی به نام PinBall معرفی کرده‌.این بازی چهار طلسم مورد علاقه گوگل رو در جدول سفارشی‌سازیش ارائه میکنه:Flutter&#x27;s Dash, Firebase&#x27;s Sparky, Android&#x27;s Robot و Chrome&#x27;s Dinosaur!تغییرات های دیگر فلاتر:Enhance code lintsImprove desktop accessibilityCascading menusDesktop system menu supportThe simplified release model for iOSCJK desktop supportSigned executablesدارت ۲.۱۷دارت ۲.۱۷ همراه با فلاتر ۳ با بسیاری از ویژگی های مورد انتظار که توسعه دهندگان منتظر اون بودند، معرفی شد. دارت درواقع سس مخفی پشت فلاتره و هر پیشرفتی که در دارت بشه به بهبود توسعه فلاتر کمک می‌کنه.بهبود در Enumهایکی از عظیم‌ترین بروزرسانی ها برای enumها که اونارو شدیدا تقویت و جذاب می‌کنه! enum Persons {  hadi(&amp;quotHadi&amp;quot, 20),
  mohammad(&amp;quotMohammad&amp;quot, 30),
  ehsan(&amp;quotEhsan&amp;quot, 18);

  final String name;
  final int age;
  const Persons(this.name, this.age);
  String introduction() {
     return &amp;quotMy Name is $name, I have $age&amp;quot
   }
}

void main(){
   print(Persons.hadi.age);
   print(Persons.mohammad.introduction());
}Super constructors برای استفاده از super در نسخه های قبلی ما چنین کدی می‌زدیم:class Person{
  final String name;
  final String family;
  const Person(this.name, this.family);
}

class Hadi extends Person{
  Hadi(String name, String family, this.age) : super(name, family);
  final int family;
}ولی حالا در نسخه ۲.۱۷ ما Super constructors رو داریم:class Person{
  final String name;
  final String family;
  const Person(this.name, this.family);
}
class Hadi extends Person{
  Hadi(super.name, super.family, this.age);
  final int age;
}آرگومان‌ها در همه‌جاقبلاً باید آرگومان های نام رو در آخرین موقعیت سازنده اضافه می‌کردیم. از دارت ۲.۱۷ دیگه نیازی به انجام این کار نیست. می‌تونیم آرگومان های نامگذاری شده خودمون رو در هر جایی از سازنده قرار بدیم.class Person{
  final String name;
  final String family;
  const Person(this.name, this.family);
}
class Hadi extends Person{
  Hadi(super.name, super.family, {required this.age});
  final int age;
  
  void fullName() {
    print(&amp;quot$name $family, $age&amp;quot);
  }
}void main(){
  Hadi(age: 14, &amp;quotHadi&amp;quot, &amp;quotAzarnasab&amp;quot).fullName();
}ویدیوهای یوتیوب و مقالات مدیوم محبوب برای این نسخه(نیازمند به VPN): https://medium.com/flutter/introducing-flutter-3-5eb69151622f  https://medium.com/dartlang/dart-2-17-b216bfc80c5d  https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d  https://www.youtube.com/watch?v=w_ezWG1yKQQ  https://www.youtube.com/watch?v=MEYQUhJdv9c https://www.youtube.com/watch?v=gZQ0b6FsNpQ https://www.youtube.com/watch?v=FGjtxGqSqnc در پایان برای اطلاعات بیشتر می‌تونید منابع و لینک های بالا رو مطالعه کنید.</description>
                <category>Persian Flutter</category>
                <author>هادی</author>
                <pubDate>Thu, 12 May 2022 22:18:10 +0430</pubDate>
            </item>
                    <item>
                <title>اصل پنجم پیاده سازی SOLID با کدهای دارت برای فریم ورک Flutter</title>
                <link>https://virgool.io/flutter-community/%D8%A7%D8%B5%D9%84-%D9%BE%D9%86%D8%AC%D9%85-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-solid-%D8%A8%D8%A7-%DA%A9%D8%AF%D9%87%D8%A7%DB%8C-%D8%AF%D8%A7%D8%B1%D8%AA-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%81%D8%B1%DB%8C%D9%85-%D9%88%D8%B1%DA%A9-flutter-gn3mu9bdpbp3</link>
                <description>Dependency Inversion Principle“اصل معکوس سازی وابستگی“ که آنرا  “اصل وارونگی وابستگی” هم می نامند برگرفته از  عبارات لاتین (Dependency Inversion Principle) یا به طور خلاصه (DIP) ، پنجمین  اصل از SOLID است و می گوید که:  “ماژول‌های سطح بالا نباید به ماژول‌های سطح پایین وابسته باشند” .در واقع،  ماژول ها به جای دسترسی مستقیم به منابعی که وابستگی ایجاد می کنند (مثلا کلاس ها) ، باید  از طریق interface ها، به آنها دسترسی داشته باشند. در این صورت  تغییرات کلاس های ما ، توسط interface ها به درون خود کلاس ها محدود می شود و لایه های بالاتر را درگیر تغییر نمی کنند.در وصف اصل وارونگی وابستگی ، گفته می شود که : اصل وارونگی وابستگی، ترکیبی ناب ، از اصل باز/بسته و اصل جایگزینی لیسکوف است.برای اینکه مفهوم را در مثال عملی کاملتر متوجه شویم آن را به دو شیوه صحیح و غلط آن پیاده سازی میکنیم.۱-  مثال زیر DIP را نقض می کند.فرض کنیم که قرار است  برای یک فروشگاه کتاب نرم افزاری کتابخوان بسازیم . از ما خواسته شده برای کاربری امکانات زیر را فراهم کنیم:۱- بخشی از کتاب مدنظر را ببیند.۲-از نظر سایر کاربران درباره کتاب مطلع شود.۳- اگر مایل بود، آن کتاب را به لیست کتابهای موجود در کتابخانه خود اضافه کند.برای شروع کلاس کتاب و کلاس  کتابخانه را به صورت زیر پیاده سازی می کنیم:class Book {
  void seeReviews() {
    // logic book reviews
  }

  void readSample() {
    // logic read book-sample
  }
}

class Library {
  late Book book;
  void addBook(Book book) {
    // logic add book
  }
}همانطور که مشاهده می کنید کلاس Book به ما اجازه خواندن sample و مشاهده review های کتاب را می دهد. همینطور کلاس Library برای ما امکان افزودن کتاب جدید را فراهم کرده است.به ظاهر کار تمام است اما مشکلی وجود دارد. کلاس سطح بالا (Library) به کلاسی سطح پایین  (Book) وابسطه شده است. به این ترتیب اصل وارونگی وابستگی نقض می شود.علاوه بر این اگر فروشگاه از ما بخواهد برای کاربران علاوه بر کتاب امکان استفاده از کتاب صوتی را  نیز فراهم کنیم به وضوح اصل دیگری از SOLID را هم نقض کرده ایم.( اصل باز/بسته )class AudioBook {
  void seeReviews() {
    // logic audio-book reviews
  }

  void listenSample() {
    // logic listen audio-book-sample
  }
}اکنون بیایید این بار با رعایت اصل اصل وارونگی وابستگی ، ایرادات برنامه فوق را برطرف کنیم.۲-  مثال زیر DIP  را رعایت می کند.برای حل مشکل،  یک interface برای کلاس های سطح پایین (کتاب و کتاب صوتی) ایجاد می کنیم. بدین منظور از یک interface به نام Product استفاده می کنیم، که باید هر دو کلاس آن را ایمپلمنت کنند.برای درک بهتر کد زیر را دنبال کنید:abstract class Product {
  void seeReviews();
  void getSample();
}

class Book implements Product {
  @override
  void seeReviews() {
    // logic book reviews
  }

  @override
  void getSample() {
    // logic book-sample
  }
}

class AudioBook implements Product {
  @override
  void seeReviews() {
    // logic audio-book reviews
  }

  @override
  void getSample() {
    // logic audio-book-sample
  }
}همانطور که کد ریفکتور شده کتابخانه را در ادامه خواهید دید، در کلاس Library به جای ارجاع مستقیم به Book و AudioBook از اینترفیس Product استفاده می کنیم. به این صورت امکان افزودن محصولات جدیدی (مثلا مجله و .. ) نیز فراهم می شود و کاربران می توانند آنها را به کتابخانه خود اضافه کنند.class Library {

    late Product product;

    void addProduct(Product product) {
          // logic add product (can be book or audio-book)
    }
}کد بالا از اصل جایگزینی Liskov نیز پیروی می کند، چون  Product را می توان با هر دو زیرشاخه آن بدون شکستن برنامه جایگزین کرد. همزمان، ما اصل وارونگی وابستگی را نیز پیاده‌سازی کرده‌ایم، زیرا در کد ریفکتور شده ، کلاس‌های سطح بالا به کلاس‌های سطح پایین وابسته نیستند.در مقالات بعدی جزئیات پیاده سازی هر بخش رو با کدهای دارت، با همدیگر بررسی می کنیم.مقدمه ای بر SOLID توضیح اصل اول : Single Responsibility Principleتوضیح اصل دوم : Open Closed Principle توضیح اصل سوم : Liskov Substitution Principleتوضیح اصل چهارم : Interface Segregation Principle توضیح اصل پنجم : Dependency Inversion Principle </description>
                <category>Persian Flutter</category>
                <author>اسماعیل احمدی پور</author>
                <pubDate>Fri, 01 Apr 2022 13:33:33 +0430</pubDate>
            </item>
                    <item>
                <title>اصل چهارم  پیاده سازی SOLID با کدهای دارت برای فریم ورک Flutter</title>
                <link>https://virgool.io/flutter-community/%D8%A7%D8%B5%D9%84-%DA%86%D9%87%D8%A7%D8%B1%D9%85-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-solid-%D8%A8%D8%A7-%DA%A9%D8%AF%D9%87%D8%A7%DB%8C-%D8%AF%D8%A7%D8%B1%D8%AA-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%81%D8%B1%DB%8C%D9%85-%D9%88%D8%B1%DA%A9-flutter-gxzmh12iqws5</link>
                <description>Interface Segregation Principle“اصل جداسازی اینترفیس ها “یا (Interface Segregation Principle) یا به طور خلاصه (ISP) ، چهارمین اصل از SOLID است و می گوید که: “کلاینت ها نباید مجبور شوند به اینترفیس هایی وابسته شوند که از آنها استفاده نمی کنند”. به زبانی بسیار ساده تر، باید اینترفیس را طوری طراحی کنیم که وقتی کلاسی از آن استفاده می کند، ملزم نباشد متدی که از آن استفاده نمی کند را پیاده‌سازی ( یا override ) کند.خیلی خلاصه تر و در یک جمله کوتاه  &quot;به چیزهایی که نیاز ندارید وابسته نباشید&quot;.برای اینکه مفهوم را در مثال عملی کاملتر متوجه شویم آن را به دو شیوه صحیح و غلط آن پیاده سازی میکنیم.۱-  مثال زیر ISP را نقض می کند.بیایید فرض کنیم که یک آژانس مسافرتی وجود دارد که شامل چندین روش برای رزور بلیط هواپیما می باشد. مثلا مشتریان آنلاین، تلفنی و حضوری دارد. همچنین برای رسیدگی به پرداخت های آنها، درگاه آنلاین برای (مشتریان آنلاین) و پرداخت از درگاه POS را برای (مشتریان حضوری و تلفنی) دارد. برای شروع یک اینترفیس یا کلاس abstract  به نام TravelAgencyInterface ایجاد می کنیم .abstract class TravelAgencyInterface {

  void acceptWalkInReserveTicket();

  void acceptOnlineReserveTicket ();

  void acceptTelephoneReserveTicket();

  void payOnline();

  void payByPOS();
}همانطور که مشاهده می کنید ما سه متد برای مشتری حضوری، مشتری تلفنی و مشتری آنلاین داریم. مشتری حضوری و تلفنی با دستگاه POS و مشتری آنلاین با استفاده از درگاه پرداخت آنلاین هزینه بلیط را پرداخت خواهند کرد.بیایید با ایمپلمنت کردن TravelAgencyInterface برای مشتریان آنلاین کار را شروع کنیم. نام کلاس را  OnlineCustomerImpl در نظر می گیریم .class OnlineCustomerImpl implements TravelAgencyInterface {

    @override
  void acceptWalkInReserveTicket() {
        // Not Applicable
    throw Exception();
  }

  @override
  void acceptOnlineReserveTicket() {
    // Logic for online customer
  }

  @override
  void acceptTelephoneReserveTicket() {
    // Not Applicable
    throw Exception();
  }

    void payOnline(){
      // Logic for online payment
    }

  void payByPOS(){
    // Not Applicable
    throw Exception(); 
  }
}از آنجایی که OnlineCustomerImpl   برای مشتریان آنلاین است، ما باید یک Exception برای متدهایی که برای مشتریان آنلاین نیست اجرا کنیم. در اینجاست که می‌توانیم نقض آشکار اصل جداسازی اینترفیس ها را مشاهده کنیم که علاوه بر آن اصل مسئولیت واحد را نیز زیر پا گذاشته است!اکنون بیایید این بار با رعایت اصل جداسازی اینترفیس ها ، ایرادات برنامه فوق را برطرف کنیم.۲-  مثال زیر ISP  را رعایت می کند.برای حل مشکل،  اقدام به کوچک تر کردن اینترفیس OnlineCustomerImpl کرده و آن را به دو اینترفیس  OrderInterface  و PaymentInterface تقسیم می کنیم.abstract class OrderInterface  {
  void reserveOrder();
}

abstract class PaymentInterface {
  void payForOrder();
}حالا، برای هر سه نوع مشتری، اینترفیس های جدید را ایمپلمنت می کنیم:class OnlineCustomerImpl implements OrderInterface, PaymentInterface {

  @override
  void reserveOrder() {
    // logic for online reserve
  }

  @override
  void payForOrder() {
    // logic to do online payment
  }
}

class WalkInCustomerImpl implements OrderInterface, PaymentInterface {
  @override
  void reserveOrder() {
    // logic for in-person reserve
  }

  @override
  void payForOrder() {
    // logic to do pos payment
  }
}

class TelephoneCustomerImpl implements OrderInterface, PaymentInterface {
  @override
  void reserveOrder() {
    // logic for telephonic reserve
  }

  @override
  void payForOrder() {
    // logic to do pos payment
  }
}از مزایایی که اصل جداسازی اینترفیس ها برای ما فراهم کرده میتوانیم به موارد زیر اشاره کنیم : ۱- افزایش خوانایی کد۲- پیاده سازی آسان تر۳-نگهداری آسان تر۴-سازماندهی بهتر کد۵- نیازی نیست از throw Exception های بیهوده استفاده کنید.نکته پایانی اینکه، هر دو اصل جداسازی اینترفیس ها و اصل تک مسئولیتی تقریباً یک هدف دارند: اطمینان از اجزای نرم افزاری کوچک، متمرکز و بسیار منسجم.و تفاوت آنها نیز در این است که، اصل تک مسئولیتی مربوط به کلاس ها است، در حالی که اصل جداسازی اینترفیس ها مربوط به اینترفیس ها می باشد.در مقالات بعدی جزئیات پیاده سازی هر بخش رو با کدهای دارت، با همدیگر بررسی می کنیم.مقدمه ای بر SOLID توضیح اصل اول : Single Responsibility Principleتوضیح اصل دوم : Open Closed Principle توضیح اصل سوم : Liskov Substitution Principleتوضیح اصل چهارم : Interface Segregation Principle توضیح اصل پنجم : Dependency Inversion Principle </description>
                <category>Persian Flutter</category>
                <author>اسماعیل احمدی پور</author>
                <pubDate>Thu, 31 Mar 2022 22:04:06 +0430</pubDate>
            </item>
            </channel>
</rss>