State: چیست، مدیریت حالت و اصل کار

نویسنده: IT Sectr منتشر شده: 2026-07-01 زمان مطالعه: 9 دقیقه

State — شیء مرکزی مدیریت داده‌ها در Flutter، مرتبط با StatefulWidget و مسئول ذخیره‌سازی اطلاعات قابل تغییر و ساخت رابط کاربری. طبق مستندات رسمی Flutter (Flutter.dev, 2026)، State در طول کل چرخه حیات ویجت وجود دارد و از بازسازی‌های آن جان سالم به در می‌برد و سازگاری داده‌ها را بین به‌روزرسانی‌های UI تضمین می‌کند. برخلاف خود ویجت، State می‌تواند فیلدهای خود را تغییر دهد و از طریق فراخوانی setState بازسازی را آغاز کند.

نکات اصلی

  • State — شیء ذخیره‌کننده داده‌های قابل تغییر StatefulWidget و مدیریت بازسازی آن از طریق setState
  • چرخه حیات — State مراحل initState، didChangeDependencies، build، didUpdateWidget و dispose را طی می‌کند، هر مرحله با هدف مشخص
  • mounted — پرچمی که نشان می‌دهد State هنوز در درخت ویجت‌ها قرار دارد و می‌تواند با خیال راحت setState را فراخوانی کند
  • widget — ارجاع به StatefulWidget مرتبط، قابل دسترسی از طریق ویژگی State برای خواندن پارامترهای والد
  • ایزوله بودن — State از سایر Stateها جدا است؛ برای تبادل داده از InheritedWidget یا ابزارهای خارجی مدیریت حالت استفاده می‌شود

State در Flutter چیست؟

State — شیء در معماری Flutter است که داده‌های قابل تغییر StatefulWidget را ذخیره می‌کند و نحوه نمایش این داده‌ها در رابط کاربری را تعیین می‌کند. هر StatefulWidget هنگام قرارگیری در درخت دقیقاً یک شیء State از طریق متد createState ایجاد می‌کند. State مستقل از ویجت وجود دارد: اگر والد StatefulWidget را با پارامترهای جدید بازسازی کند، State قبلی باقی می‌ماند و ویجت به‌روزرسانی شده را از طریق ویژگی widget دریافت می‌کند.

طبق Flutter Architectural Overview (Google, 2026)، جداسازی Widget و State یک تصمیم معماری آگاهانه است که به فریمورک امکان استفاده مجدد از عناصر درخت را می‌دهد. ویجت (توصیف سبک) می‌تواند چندین بار ایجاد و نابود شود، اما State (شیء سنگین با داده‌ها) تا زمانی که عنصر در درخت قرار دارد در حافظه باقی می‌ماند. این کار از از دست رفتن داده‌ها در بازسازی‌های مکرر ویجت‌های والد جلوگیری می‌کند.

State رابط StatefulWidget را از طریق جنریک پیاده‌سازی می‌کند: class _MyState extends State<MyWidget>. جنریک State را با نوع خاصی از StatefulWidget مرتبط می‌کند و دسترسی نوع‑امن به فیلدهای آن را از طریق ویژگی widget فراهم می‌کند.

State کجا ذخیره می‌شود؟

شیء State در StatefulElement ذخیره می‌شود — لایه میانی بین Widget و RenderObject. StatefulElement State را از طریق createState ایجاد می‌کند، ارجاع به آن را نگه می‌دارد و State را به عنوان مالک منتقل می‌کند. Element تنها زمانی نابود می‌شود که ویجت از درخت حذف شود — تا آن لحظه State در حافظه زنده می‌ماند.

چرخه حیات State

چرخه حیات State قطعی است و از یک توالی دقیق از فراخوانی‌ها تشکیل شده است. درک این توالی اساس کار صحیح با منابع و جلوگیری از نشت حافظه است.

initState — مقداردهی اولیه

initState اولین بار هنگام ایجاد State فراخوانی می‌شود. در این متد کنترلرها، اشتراک‌های جریان داده، تایمرها و مقادیر اولیه فیلدها مقداردهی می‌شوند. فراخوانی super.initState() در خط اول الزامی است. در مرحله initState درخت ویجت هنوز به طور کامل نصب نشده است، بنابراین متدهایی مانند MediaQuery.of(context) ممکن است نادرست کار کنند.

didChangeDependencies

didChangeDependencies بعد از initState و با هر تغییر در وابستگی‌های InheritedWidget فراخوانی می‌شود. دقیقاً در اینجا، نه در initState، باید MediaQuery.of(context) یا Theme.of(context) فراخوانی شود، زیرا در این لحظه درخت قبلاً نصب شده است. این متد همچنین اگر ویجت به زمینه دیگری منتقل شود که InheritedWidget مقادیر متفاوتی ارائه می‌دهد، فراخوانی می‌شود.

build — ساخت UI

build — متد اصلی State که درخت ویجت را برمی‌گرداند. بعد از initState، بعد از didChangeDependencies و بعد از هر setState فراخوانی می‌شود. متد build نباید عوارض جانبی داشته باشد — فقط رابط کاربری را بر اساس مقادیر فعلی فیلدهای State توصیف می‌کند.

didUpdateWidget

didUpdateWidget زمانی فراخوانی می‌شود که والد StatefulWidget را با پارامترهای جدید بازسازی می‌کند. State از طریق oldWidget به ویجت قدیمی دسترسی پیدا می‌کند و می‌تواند آن را با ویجت جدید مقایسه کند. اگر پارامترها تغییر کرده باشند، می‌توان حالت را به‌روز کرد، داده‌های جدید بارگیری کرد یا انیمیشن را دوباره راه‌اندازی کرد.

dispose — آزادسازی منابع

dispose — متد پایانی که در آن همه منابع آزاد می‌شوند: کنترلرها، اشتراک‌ها، تایمرها. بعد از dispose State به عنوان مرده علامت‌گذاری می‌شود: mounted false برمی‌گرداند، فراخوانی setState استثنا پرتاب می‌کند. فراخوانی super.dispose() در آخرین خط متد الزامی است.

متدزمان فراخوانیsuper الزامی
initStateهنگام ایجاد Stateبله، در خط اول
didChangeDependenciesبعد از initState و هنگام تغییر InheritedWidgetبله
buildبعد از initState، didChangeDependencies، setStateخیر
didUpdateWidgetبا ویجت جدید از والدبله
setStateبا فراخوانی برنامه‌نویسخیر
disposeهنگام حذف از درختبله، در خط آخر

State چگونه کار می‌کند؟

مکانیزم کار State بر سه اصل کلیدی استوار است: ارتباط با Element، واکنش‌گرایی از طریق setState و دسترسی به والد از طریق ویژگی widget. وقتی Flutter درخت عناصر را می‌سازد و به StatefulElement برخورد می‌کند، createState ویجت مرتبط را فراخوانی می‌کند. State ایجاد شده در عنصر ذخیره می‌شود و تا زمانی که عنصر حذف نشود وجود دارد.

هنگام فراخوانی setState، State خود را به عنوان «کثیف» (dirty) علامت‌گذاری می‌کند و بازسازی را برای فریم بعدی برنامه‌ریزی می‌کند. مهم: setState بلافاصله build را فراخوانی نمی‌کند — فقط نیاز به بازسازی را ثبت می‌کند. Flutter تمام عناصر کثیف فریم جاری را جمع‌آوری کرده و آنها را به صورت دسته‌ای بازسازی می‌کند که عملکرد را بهینه می‌کند. پس از فراخوانی build، State به حالت «تمیز» (clean) بازمی‌گردد.

ویژگی widget به State اجازه می‌دهد پارامترهای ارسال شده به سازنده StatefulWidget را بخواند. از آنجا که StatefulWidget تغییرناپذیر است (مانند StatelessWidget)، فیلدهای آن تغییر نمی‌کنند — هنگام تغییر پارامترها، والد یک ویجت جدید ایجاد می‌کند و State آن را از طریق didUpdateWidget دریافت می‌کند. این تضمین می‌کند که State همیشه با داده‌های به‌روز والد کار می‌کند.

نمونه کد در Dart

نمونه پایه State با فیلدی که توسط تایمر تغییر می‌کند. initState، setState و dispose را نشان می‌دهد:

dart
class _TimerWidgetState extends State<TimerWidget> {
  int _seconds = 0;
  Timer? _timer;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(
      const Duration(seconds: 1),
      (_) => setState(() => _seconds++),
    );
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Text('$_seconds ثانیه سپری شد');
  }
}

نمونه استفاده از ویژگی widget برای دسترسی به پارامترهای والد و واکنش به تغییرات آنها از طریق didUpdateWidget:

dart
class _GreetingState extends State<GreetingWidget> {
  String _displayName = '';

  @override
  void initState() {
    super.initState();
    _displayName = _formatName(widget.name);
  }

  @override
  void didUpdateWidget(GreetingWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.name != oldWidget.name) {
      setState(() {
        _displayName = _formatName(widget.name);
      });
    }
  }

  String _formatName(String name) => name.trim().isEmpty ? 'Guest' : name;

  @override
  Widget build(BuildContext context) {
    return Text('سلام، $_displayName!');
  }
}

در نمونه دوم، State تغییر پارامتر ورودی name را ردیابی می‌کند و نمایش را فقط در صورت تغییر واقعی دوباره قالب‌بندی می‌کند. بدون بررسی widget.name != oldWidget.name، متد در هر بازسازی والد، حتی اگر نام تغییر نکرده باشد، فراخوانی می‌شد — این کار اضافی برای فریمورک است.

State در مقابل StatefulWidget

State و StatefulWidget دو کلاس متفاوت در معماری Flutter هستند که نقش‌های مختلفی ایفا می‌کنند. StatefulWidget یک پوشش سبک تغییرناپذیر است که پیکربندی ویجت را توصیف کرده و State ایجاد می‌کند. State یک شیء سنگین است که داده‌های قابل تغییر را ذخیره می‌کند، اشتراک‌ها را مدیریت می‌کند و UI را می‌سازد. این جداسازی به Flutter اجازه می‌دهد ویجت‌ها را بدون از دست دادن حالت نابود و ایجاد کند.

همه فیلدهای StatefulWidget باید final باشند و در سازنده تنظیم شوند — آنها پس از ایجاد تغییر نمی‌کنند. State، در مقابل، می‌تواند فیلدهای خود را در هر لحظه تغییر دهد، اما همه تغییرات باید با فراخوانی setState همراه شوند تا Flutter از نیاز به بازسازی مطلع شود. این تفاوت کلیدی است: StatefulWidget یعنی «چه چیزی نشان داده شود»، State یعنی «چگونه نشان داده شود و از چه داده‌هایی استفاده شود».

طبق Flutter source code analysis (Flutter SDK, 2026)، StatefulWidget فقط یک فیلد اجباری دارد — createState، در حالی که State به BuildContext دسترسی دارد، می‌تواند در جریان‌ها مشترک شود، انیمیشن‌ها و کنترلرها را مدیریت کند. توصیه می‌شود StatefulWidget را تا حد امکان ساده نگه دارید و تمام منطق را به State منتقل کنید.

چرا StatefulWidget نمی‌تواند State باشد؟

جداسازی Widget و State یک تصمیم معماری است که تغییرناپذیری پیکربندی را تضمین می‌کند. اگر StatefulWidget خود حالت را ذخیره می‌کرد، با هر بازسازی والد حالت از دست می‌رفت. با خارج کردن حالت به یک شیء جداگانه، Flutter تضمین می‌کند که داده‌ها از بازسازی‌ها جان سالم به در می‌برند و ویجت‌ها سبک و قابل مقایسه باقی می‌مانند.

مدیریت حالت بین ویجت‌ها

شیء State ایزوله است — دسترسی مستقیم به State سایر ویجت‌ها ندارد. برای تبادل داده بین ویجت‌ها از InheritedWidget یا ابزارهای خارجی مدیریت حالت استفاده می‌شود: Provider، Riverpod، Bloc، Redux. هر رویکرد مسئله را به روش خود حل می‌کند: InheritedWidget از طریق درخت ویجت‌ها کار می‌کند، Provider — از طریق کانتینر DI، Bloc — از طریق جریان‌های رویداد.

انتخاب ابزار به مقیاس پروژه بستگی دارد. برای یک برنامه کوچک، InheritedWidget و State محلی کافی است. برای پروژه متوسط و بزرگ، Riverpod یا Bloc توصیه می‌شود — آنها قابلیت تست، پیش‌بینی‌پذیری و جداسازی منطق از UI را فراهم می‌کنند. State در این مورد فقط برای داده‌های محلی ویجت (فوکوس، اسکرول، انیمیشن) استفاده می‌شود.

طبق Flutter Community Survey 2025 (Flutter Foundation، دسامبر 2025)، Riverpod محبوب‌ترین راه‌حل برای مدیریت حالت در پروژه‌های جدید است (38٪)، پس از آن Bloc (31٪) و Provider (22٪). هر سه ابزار با State سازگار هستند و نیازی به کنار گذاشتن چرخه حیات استاندارد ندارند.

حالت محلی در مقابل سراسری

  • محلی — در State ویجت خاص (موقعیت اسکرول، حالت فوکوس)
  • سراسری — در ذخیره‌ساز خارجی (داده‌های کاربر، تنظیمات، کش)
  • قاعده: اگر داده‌ها فقط توسط یک ویجت استفاده می‌شوند — در State ذخیره کنید
  • اگر داده‌ها توسط ۲+ ویجت استفاده می‌شوند — به Riverpod/Bloc/Provider منتقل کنید

اشتباهات رایج

اولین اشتباه — فراموش کردن بررسی mounted قبل از setState در回调 ناهمزمان. وقتی ویجت از درخت حذف شده است (مثلاً کاربر صفحه را ترک کرده)، اما عملیات ناهمزمان (درخواست HTTP) هنوز در حال اجراست، پس از اتمام آن State قبلاً مرده است. فراخوانی setState در State مرده استثنا پرتاب می‌کند. بررسی if (mounted) setState(...) مشکل را حل می‌کند.

دومین اشتباه — مقداردهی وابستگی‌های InheritedWidget در initState به جای didChangeDependencies. در initState زمینه هنوز نصب نشده است، بنابراین MediaQuery.of(context) استثنا پرتاب می‌کند. همه وابستگی‌های InheritedWidget باید در didChangeDependencies یا build پیکربندی شوند.

سومین اشتباه — تغییر فیلدها بدون فراخوانی setState. اگر برنامه‌نویس فیلد State را بدون setState تغییر دهد، Flutter از تغییر مطلع نمی‌شود و UI به‌روز نمی‌شود. مثال: _list.add(item) بدون setState((){}) بعدی لیست را تغییر می‌دهد اما صفحه ثابت می‌ماند.

بررسی mounted قبل از setState

الگوی ایمنی برای عملیات ناهمزمان در State:

dart
Future<void> _fetchData() async {
  final data = await ApiService.fetch();
  if (mounted) {
    setState(() => _data = data);
  }
}

بررسی mounted تضمین می‌کند که setState فقط برای State زنده فراخوانی می‌شود و از استثنای «setState called after dispose» جلوگیری می‌کند.

سوالات متداول

State چه تفاوتی با StatefulWidget دارد؟

StatefulWidget — پیکربندی تغییرناپذیر ویجت است، در حالی که State یک شیء قابل تغییر است که داده‌ها را ذخیره کرده و چرخه حیات را مدیریت می‌کند. ویجت می‌تواند دوباره ایجاد شود، State — نه. StatefulWidget State را از طریق createState ایجاد می‌کند.

برای یک StatefulWidget چند شیء State ایجاد می‌شود؟

دقیقاً یک. متد createState یک بار هنگام اولین قرارگیری StatefulWidget در درخت فراخوانی می‌شود. حتی اگر والد چندین بار بازسازی شود، شیء State یکسان باقی می‌ماند، تا زمانی که نوع یا Key ویجت تغییر نکند.

mounted در State چیست؟

mounted — یک پرچم بولی است که نشان می‌دهد آیا State در درخت ویجت‌ها قرار دارد. پس از فراخوانی dispose، mounted false می‌شود. برای بررسی قبل از setState در回调های ناهمزمان استفاده می‌شود تا از استثنا جلوگیری کند.

آیا می‌توان از State بدون StatefulWidget استفاده کرد؟

خیر. State همیشه به یک StatefulWidget خاص از طریق جنریک متصل است: State<T extends StatefulWidget>. ایجاد State مستقیم، بدون ارتباط با ویجت، از نظر معماری غیرممکن است.

اگر setState در dispose فراخوانی شود چه اتفاقی می‌افتد؟

استثنا پرتاب می‌شود: «setState called after dispose». پس از فراخوانی dispose، State مرده محسوب می‌شود و هرگونه تلاش برای بازسازی UI از طریق setState ممنوع است. راه‌حل — قبل از هر setState mounted را بررسی کنید.

خلاصه

  • State — شیء مدیریت داده‌های StatefulWidget، ذخیره فیلدهای قابل تغییر و شروع بازسازی UI از طریق setState
  • چرخه حیات شامل متدهای اجباری initState، didChangeDependencies، build، didUpdateWidget و dispose، هر کدام با هدف خود
  • mounted — پرچم ایمنی حیاتی که از فراخوانی setState پس از حذف ویجت از درخت جلوگیری می‌کند
  • widget — ویژگی State برای دسترسی به پارامترهای StatefulWidget مرتبط، به‌روزرسانی شده از طریق didUpdateWidget
  • ایزوله بودن — State به Stateهای دیگر دسترسی ندارد؛ تعامل بین ویجت‌ها از طریق InheritedWidget یا ابزارهای خارجی انجام می‌شود
  • setState — build را بلافاصله فراخوانی نمی‌کند، فقط State را برای بازسازی در فریم بعدی به عنوان کثیف علامت‌گذاری می‌کند
  • قاعده — از State برای داده‌های محلی ویجت استفاده کنید؛ حالت سراسری را به لایه‌های خارجی منتقل کنید (Riverpod، Bloc)

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید