State — شیء مرکزی مدیریت دادهها در Flutter، مرتبط با StatefulWidget و مسئول ذخیرهسازی اطلاعات قابل تغییر و ساخت رابط کاربری. طبق مستندات رسمی Flutter (Flutter.dev, 2026)، State در طول کل چرخه حیات ویجت وجود دارد و از بازسازیهای آن جان سالم به در میبرد و سازگاری دادهها را بین بهروزرسانیهای UI تضمین میکند. برخلاف خود ویجت، State میتواند فیلدهای خود را تغییر دهد و از طریق فراخوانی setState بازسازی را آغاز کند.
نکات اصلی
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 در StatefulElement ذخیره میشود — لایه میانی بین Widget و RenderObject. StatefulElement State را از طریق createState ایجاد میکند، ارجاع به آن را نگه میدارد و State را به عنوان مالک منتقل میکند. Element تنها زمانی نابود میشود که ویجت از درخت حذف شود — تا آن لحظه State در حافظه زنده میماند.
چرخه حیات State قطعی است و از یک توالی دقیق از فراخوانیها تشکیل شده است. درک این توالی اساس کار صحیح با منابع و جلوگیری از نشت حافظه است.
initState اولین بار هنگام ایجاد State فراخوانی میشود. در این متد کنترلرها، اشتراکهای جریان داده، تایمرها و مقادیر اولیه فیلدها مقداردهی میشوند. فراخوانی super.initState() در خط اول الزامی است. در مرحله initState درخت ویجت هنوز به طور کامل نصب نشده است، بنابراین متدهایی مانند MediaQuery.of(context) ممکن است نادرست کار کنند.
didChangeDependencies بعد از initState و با هر تغییر در وابستگیهای InheritedWidget فراخوانی میشود. دقیقاً در اینجا، نه در initState، باید MediaQuery.of(context) یا Theme.of(context) فراخوانی شود، زیرا در این لحظه درخت قبلاً نصب شده است. این متد همچنین اگر ویجت به زمینه دیگری منتقل شود که InheritedWidget مقادیر متفاوتی ارائه میدهد، فراخوانی میشود.
build — متد اصلی State که درخت ویجت را برمیگرداند. بعد از initState، بعد از didChangeDependencies و بعد از هر setState فراخوانی میشود. متد build نباید عوارض جانبی داشته باشد — فقط رابط کاربری را بر اساس مقادیر فعلی فیلدهای State توصیف میکند.
didUpdateWidget زمانی فراخوانی میشود که والد StatefulWidget را با پارامترهای جدید بازسازی میکند. State از طریق oldWidget به ویجت قدیمی دسترسی پیدا میکند و میتواند آن را با ویجت جدید مقایسه کند. اگر پارامترها تغییر کرده باشند، میتوان حالت را بهروز کرد، دادههای جدید بارگیری کرد یا انیمیشن را دوباره راهاندازی کرد.
dispose — متد پایانی که در آن همه منابع آزاد میشوند: کنترلرها، اشتراکها، تایمرها. بعد از dispose State به عنوان مرده علامتگذاری میشود: mounted false برمیگرداند، فراخوانی setState استثنا پرتاب میکند. فراخوانی super.dispose() در آخرین خط متد الزامی است.
| متد | زمان فراخوانی | super الزامی |
|---|---|---|
| initState | هنگام ایجاد State | بله، در خط اول |
| didChangeDependencies | بعد از initState و هنگام تغییر InheritedWidget | بله |
| build | بعد از initState، didChangeDependencies، setState | خیر |
| didUpdateWidget | با ویجت جدید از والد | بله |
| setState | با فراخوانی برنامهنویس | خیر |
| dispose | هنگام حذف از درخت | بله، در خط آخر |
مکانیزم کار 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 همیشه با دادههای بهروز والد کار میکند.
نمونه پایه State با فیلدی که توسط تایمر تغییر میکند. initState، setState و dispose را نشان میدهد:
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:
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 دو کلاس متفاوت در معماری 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 منتقل کنید.
جداسازی 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 سازگار هستند و نیازی به کنار گذاشتن چرخه حیات استاندارد ندارند.
اولین اشتباه — فراموش کردن بررسی 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((){}) بعدی لیست را تغییر میدهد اما صفحه ثابت میماند.
الگوی ایمنی برای عملیات ناهمزمان در State:
Future<void> _fetchData() async {
final data = await ApiService.fetch();
if (mounted) {
setState(() => _data = data);
}
}
بررسی mounted تضمین میکند که setState فقط برای State زنده فراخوانی میشود و از استثنای «setState called after dispose» جلوگیری میکند.
سوالات متداول
StatefulWidget — پیکربندی تغییرناپذیر ویجت است، در حالی که State یک شیء قابل تغییر است که دادهها را ذخیره کرده و چرخه حیات را مدیریت میکند. ویجت میتواند دوباره ایجاد شود، State — نه. StatefulWidget State را از طریق createState ایجاد میکند.
دقیقاً یک. متد createState یک بار هنگام اولین قرارگیری StatefulWidget در درخت فراخوانی میشود. حتی اگر والد چندین بار بازسازی شود، شیء State یکسان باقی میماند، تا زمانی که نوع یا Key ویجت تغییر نکند.
mounted — یک پرچم بولی است که نشان میدهد آیا State در درخت ویجتها قرار دارد. پس از فراخوانی dispose، mounted false میشود. برای بررسی قبل از setState در回调های ناهمزمان استفاده میشود تا از استثنا جلوگیری کند.
خیر. State همیشه به یک StatefulWidget خاص از طریق جنریک متصل است: State<T extends StatefulWidget>. ایجاد State مستقیم، بدون ارتباط با ویجت، از نظر معماری غیرممکن است.
استثنا پرتاب میشود: «setState called after dispose». پس از فراخوانی dispose، State مرده محسوب میشود و هرگونه تلاش برای بازسازی UI از طریق setState ممنوع است. راهحل — قبل از هر setState mounted را بررسی کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید