StatefulWidget — ویجت Flutter با حالت قابل تغییر که به UI اجازه میدهد به اقدامات کاربر، رویدادهای ناهمگام و جریانهای داده واکنش نشان دهد. بر اساس مستندات رسمی Flutter (Flutter.dev, 2026)، StatefulWidget برای تمام عناصر تعاملی برنامه استفاده میشود: فرمهای ورود، انیمیشنها، جعبههای انتخاب، کلیدهای تغییر وضعیت و صفحههایی که دادهها را از شبکه بارگذاری میکنند. بر خلاف StatelessWidget، یک شیء State جداگانه ایجاد میکند که در طول کل چرخه حیات حفظ میشود و میتواند بدون ایجاد مجدد خود ویجت بازسازی شود.
نکات اصلی
StatefulWidget — کلاسی در Flutter است که میتواند حالت خود را در پاسخ به اقدامات کاربر، رویدادهای سیستمی یا عملیات ناهمگام تغییر دهد. بر خلاف StatelessWidget، StatefulWidget مستقیماً نمایش داده نمیشود — بلکه یک شیء State ایجاد میکند که مسئول rendering است. تقسیم به دو کلاس (Widget و State) به Flutter اجازه میدهد UI را بدون ایجاد مجدد خود ویجت بازسازی کند که در بهروزرسانیهای مکرر مزیت عملکردی قابل توجهی ایجاد میکند.
معماری StatefulWidget از الگوی «جداسازی تغییرپذیر و تغییرناپذیر» پیروی میکند: خود ویجت تغییرناپذیر میماند (مانند StatelessWidget)، و تمام حالت تغییرپذیر در یک شیء State جداگانه ذخیره میشود. این به Flutter اجازه میدهد ویجتها را با مقایسه بر اساس نوع و Key دوباره استفاده کند و در عین حال حالت جاری را بین بازسازیها حفظ کند.
بر اساس Google (Flutter Architectural Overview, 2026)، StatefulWidget برای سناریوهایی که حالت بیش از یک بار در طول عمر ویجت تغییر میکند بهینه است: فیلدهای متنی، انیمیشنها، تایمرها، جریانهای داده، بارگذاریهای ناهمگام. برای مقداردهی اولیه یک بار، StatelessWidget کافی است.
StatefulWidget زمانی اجباری است که ویجت باید به رویدادهای خارجی واکنش نشان دهد: کلیک دکمه، تکمیل درخواست HTTP، بهروزرسانی داده از پایگاه داده، اشتراک WebSocket. همچنین برای ویجتهای دارای انیمیشن، فیلدهای متنی با کنترلر و کامپوننتهای مدیریت فوکوس ضروری است. اگر ویجت فقط دادهها را نمایش میدهد و رویداد تولید نمیکند — از StatelessWidget استفاده کنید.
StatefulWidget از دو کلاس تشکیل شده است: خود StatefulWidget (سبک، تغییرناپذیر) و State (سنگین، تغییرپذیر). فریمورک State را از طریق متد createState() که یک بار هنگام قرارگیری در درخت فراخوانی میشود ایجاد میکند. State از طریق ویژگی widget به ویجت ارجاع میگیرد و میتواند در هر لحظه از چرخه حیات به فیلدهای آن دسترسی داشته باشد.
چرخه حیات StatefulWidget از شش مرحله اصلی تشکیل شده است که هر کدام یک متد قابل بازنویسی برای انجام وظایف خاص ارائه میدهد. درک این مراحل برای کار صحیح با منابع و جلوگیری از نشت حافظه حیاتی است.
createState — اولین متد چرخه حیات که هنگام قرارگیری StatefulWidget در درخت فراخوانی میشود. باید یک نمونه جدید از State مرتبط با این ویجت را برگرداند. این متد دقیقاً یک بار در طول عمر عنصر فراخوانی میشود. مهم است که عملیات سنگین در اینجا انجام نشود — createState باید تا حد امکان سبک باشد.
initState — بلافاصله پس از ایجاد State، قبل از اولین ساخت UI فراخوانی میشود. در اینجا انجام میشود: مقداردهی کنترلرها (TextEditingController، AnimationController)، اشتراک جریانهای داده (StreamSubscription)، تنظیم تایمرها و مقداردهی اولیه فیلدها. طبق مستندات Flutter (Flutter.dev, 2026)، در initState نمیتوان BuildContext.of() را فراخوانی کرد — درخت هنوز به طور کامل سوار نشده است.
didChangeDependencies — پس از initState و هر بار که وابستگیهای InheritedWidget تغییر میکند فراخوانی میشود. این مکان مناسبی برای فراخوانی MediaQuery.of(context) یا اشتراک Theme است — مقادیری که ممکن است در حین کار برنامه تغییر کنند. اگر ویجت از InheritedWidget استفاده میکند، منطق مقداردهی باید اینجا باشد، نه در initState.
build — متد اصلی که درخت ویجت را برمیگرداند. پس از initState، پس از didChangeDependencies و پس از هر setState فراخوانی میشود. didUpdateWidget زمانی فراخوانی میشود که والد بازسازی میشود و StatefulWidget را با پارامترهای جدید ارسال میکند. در اینجا میتوان فیلدهای قدیم و جدید ویجت را مقایسه کرد و در صورت نیاز حالت را بهروزرسانی کرد.
dispose — مرحله نهایی چرخه حیات. در اینجا تمام منابع آزاد میشوند: لغو اشتراک از جریانها، حذف کنترلرها، لغو تایمرها. عدم فراخوانی dispose منجر به نشت حافظه میشود. پس از dispose، State مرده محسوب میشود — فراخوانی setState در داخل آن یک استثنا ایجاد میکند.
مکانیزم کار StatefulWidget بر اساس هماهنگی سه موجودیت است: Widget (توضیح سبک)، Element (لایه میانی) و State (ذخیرهساز داده). وقتی Flutter با StatefulWidget در توضیح مواجه میشود، StatefulElement ایجاد میکند که createState را فراخوانی کرده و ارجاع به شیء State را ذخیره میکند. هنگام بازسازی والد، Flutter ویجت جدید را با Element جاری مقایسه میکند — اگر نوع و Key مطابقت داشته باشند، Element بهروزرسانی میشود و State ثابت میماند.
حالت فقط از طریق فراخوانی setState تغییر میکند که به فریمورک درباره نیاز به بازسازی اطلاع میدهد. مهم است بدانید: setState به طور خودکار حالت را تغییر نمیدهد — فقط ویجت را به عنوان «کثیف» علامتگذاری میکند. توسعهدهنده خودش فیلدهای State را در callback ارسالشده به setState بهروزرسانی میکند. پس از پایان callback، Flutter build را فراخوانی کرده و UI را بهروزرسانی میکند.
بر اساس تیم Dart/Flutter (Dart Language Specification, 2026)، این جداسازی تضمین میکند که تمام تغییرات حالت قبل از فراخوانی build به صورت همزمان رخ میدهد و وضعیتی که UI دادههای نیمه بهروزرسانی شده را نمایش میدهد حذف میکند. این مکانیزم کلیدی سازگاری رابط در Flutter است.
یک StatefulWidget ساده — شمارنده کلیک دکمه را در نظر بگیرید. این الگوی پایه را نشان میدهد: ایجاد State، مقداردهی فیلد در initState، تغییر از طریق setState:
class CounterScreen extends StatefulWidget {
const CounterScreen({super.key});
@override
State<CounterScreen> createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('تعداد: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('افزایش'),
),
],
);
}
}
نمونه با بارگذاری ناهمگام داده و مدیریت چرخه حیات. StatefulWidget داده را از شبکه بارگذاری کرده و وضعیت بارگذاری را نمایش میدهد:
class UserProfilePage extends StatefulWidget {
final String userId;
const UserProfilePage({super.key, required this.userId});
@override
State<UserProfilePage> createState() => _UserProfilePageState();
}
class _UserProfilePageState extends State<UserProfilePage> {
UserModel? _user;
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadUser();
}
Future<void> _loadUser() async {
final user = await UserService.fetchUser(widget.userId);
setState(() {
_user = user;
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
if (_isLoading) return const CircularProgressIndicator();
return Text('سلام، ${_user!.name}');
}
}
در مثال دوم توجه به این نکته مهم است: initState عملیات ناهمگام را شروع میکند، اما خود متد ناهمگام نیست. ناهمگامی از طریق async/await در داخل متد جداگانه _loadUser پیادهسازی میشود که پس از تکمیل درخواست، حالت را از طریق setState بهروزرسانی میکند. این رویکرد تضمین میکند که ویجت نشانگر بارگذاری را قبل از دریافت داده به درستی نمایش دهد.
انتخاب بین StatefulWidget و StatelessWidget فقط موضوع وجود حالت نیست. StatefulWidget یک چرخه حیات کامل با متدهای initState، didChangeDependencies، didUpdateWidget و dispose ارائه میدهد که برای کار با کنترلرها، انیمیشنها و جریانها ضروری است. StatelessWidget به نوبه خود این متدها را ندارد و همیشه برای فریمورک سبکتر است.
توصیه تیم Flutter (Flutter docs, 2026) — تعداد StatefulWidget را در برنامه به حداقل برسانید، حالت را با بالا بردن در درخت (State Hoisting) یا با استفاده از راهحلهای مدیریت حالت (Riverpod، Bloc، Provider) مدیریت کنید. هر StatefulWidget یک شیء State ایجاد میکند که تا حذف عنصر زنده میماند — هر چه تعداد این ویجتها بیشتر باشد، بار حافظه بالاتر است.
| معیار | StatefulWidget | StatelessWidget |
|---|---|---|
| حالت | تغییرپذیر | تغییرناپذیر |
| چرخه حیات | ۶ مرحله | فقط build |
| شیء State | جداگانه ایجاد میشود | نیاز نیست |
| setState | در دسترس | در دسترس نیست |
| اشتراکها | initState/dispose | پشتیبانی نمیشود |
| const سازنده | محدود | کاملاً پشتیبانی میشود |
| مصرف حافظه | بیشتر | کمتر |
StatefulWidget به دلیل نیاز به ایجاد و نگهداری شیء State منابع بیشتری نسبت به StatelessWidget مصرف میکند. اما استفاده صحیح از StatefulWidget اگر چند قانون رعایت شود به مشکلات عملکردی منجر نمیشود. اولاً، از تودرتو عمیق StatefulWidget خودداری کنید — هر سطح هزینه اضافی برای پیمایش درخت اضافه میکند. ثانیاً، StatefulWidget پیچیده را به چند ویجت ساده تقسیم کنید که هر کدام مسئول بخشی از حالت هستند.
بر اساس تحقیق Flutter Performance (Flutter.dev, فوریه ۲۰۲۶)، شایعترین علت افت FPS فراخوانی setState در ویجت والد است که تمام فرزندان از جمله StatelessWidgetهایی را که نمایش خود را تغییر ندادهاند بازسازی میکند. راه حل — بخش تغییرپذیر UI را به یک StatefulWidget جداگانه منتقل کنید تا setState فقط حداقل ویجتهای لازم را بازسازی کند.
استفاده از const در داخل State یکی دیگر از تکنیکهای مهم است. اگر ویجتهای فرزند به عنوان const اعلام شوند، Flutter هنگام فراخوانی setState در والد آنها را بازسازی نمیکند. این بار فریمورک را کاهش میدهد و زمان rendering فریم را کاهش میدهد.
هر فراخوانی setState یک بازسازی کامل ویجت را آغاز میکند. اگر حالت با فرکانس بالا تغییر میکند (مثلاً انیمیشن یا جریان داده)، به جای فراخوانی دستی setState از AnimatedBuilder، ValueListenableBuilder یا StreamBuilder استفاده کنید. این ویجتها بازسازی را بهینه میکنند و فقط بخشی از UI را که واقعاً تغییر کرده بهروزرسانی میکنند.
اولین خطای رایج با StatefulWidget — فراخوانی setState پس از dispose است. وقتی ویجت از درخت حذف میشود، State مرده محسوب میشود و هر فراخوانی setState یک استثنای «setState called after dispose» ایجاد میکند. این بیشتر از همه زمانی اتفاق میافتد که یک عملیات ناهمگام پس از حذف ویجت تکمیل میشود. راه حل — بررسی پرچم mounted قبل از فراخوانی setState یا لغو عملیات ناهمگام در dispose.
دومین خطا — انجام محاسبات سنگین در متد build است. از آنجایی که build در هر setState و هر بازسازی والد فراخوانی میشود، تمام محاسبات باید تا حد امکان سبک باشند. اگر نیاز به انجام عملیات منابعبر دارید — آن را به یک Isolate جداگانه منتقل کنید یا نتیجه را در فیلد State ذخیره کنید.
سومین خطا — عدم فراخوانی super.initState() و super.dispose() است. هنگام بازنویسی این متدها، توسعهدهنده موظف است پیادهسازی والد را فراخوانی کند. اگر این کار انجام نشود، فریمورک نمیتواند به درستی حالت Element را مدیریت کند که منجر به باگهای دشوار میشود.
mounted را بررسی کنیدsuper.initState() و super.dispose() را فراموش نکنیدسوالات متداول
StatefulWidget میتواند حالت خود را از طریق setState تغییر دهد، دارای چرخه حیات (initState، dispose) است و یک شیء State جداگانه ایجاد میکند. StatelessWidget نمیتواند حالت را تغییر دهد و متدهای چرخه حیات ندارد — فقط دادههای ارسالی را نمایش میدهد.
createState دقیقاً یک بار برای هر نمونه StatefulElement فراخوانی میشود. حتی اگر والد بارها بازسازی شود، تا زمانی که نوع و Key ویجت تغییر نکند، createState فراخوانی نمیشود — از شیء State موجود استفاده میشود.
منابع آزاد نمیشوند: کنترلرها در پسزمینه به کار ادامه میدهند، اشتراک جریانها فعال میمانند، تایمرها لغو نمیشوند. این منجر به نشت حافظه میشود و میتواند باعث فراخوانی setState پس از dispose شود که یک استثنا ایجاد میکند.
بله، سازنده StatefulWidget میتواند const باشد. اما این مزیت مشابه StatelessWidget را ندارد — شیء State همچنان در اولین قرارگیری ایجاد میشود. const فقط روی خود ویجت (پوشش سبک) تأثیر میگذارد، نه روی State.
didUpdateWidget زمانی فراخوانی میشود که والد StatefulWidget را با پارامترهای جدید ارسال میکند. این برای همگامسازی حالت با دادههای جدید لازم است — مثلاً اگر userId در پارامترها تغییر کند، باید پروفایل کاربر جدید بارگذاری شود.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید