StatefulWidget: چیست، چرخه حیات و اصل کار

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

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

نکات اصلی

  • StatefulWidget — ویجتی که می‌تواند حالت خود را در حین کار تغییر دهد و با setState بازسازی UI را آغاز کند
  • چرخه حیات — StatefulWidget مراحل createState، initState، didChangeDependencies، build، didUpdateWidget، dispose را طی می‌کند
  • شیء State — یک شیء جداگانه که حالت را ذخیره می‌کند و مستقل از ویجت در طول عمر آن وجود دارد
  • setState — تنها راه قانونی برای اطلاع‌رسانی به Flutter درباره نیاز به بازسازی ویجت پس از تغییر داده‌ها
  • عملکرد — استفاده بیش از حد از StatefulWidget مصرف حافظه و زمان渲染 را افزایش می‌دهد

StatefulWidget چیست؟

StatefulWidget — کلاسی در Flutter است که می‌تواند حالت خود را در پاسخ به اقدامات کاربر، رویدادهای سیستمی یا عملیات ناهمگام تغییر دهد. بر خلاف StatelessWidget، StatefulWidget مستقیماً نمایش داده نمی‌شود — بلکه یک شیء State ایجاد می‌کند که مسئول rendering است. تقسیم به دو کلاس (Widget و State) به Flutter اجازه می‌دهد UI را بدون ایجاد مجدد خود ویجت بازسازی کند که در به‌روزرسانی‌های مکرر مزیت عملکردی قابل توجهی ایجاد می‌کند.

معماری StatefulWidget از الگوی «جداسازی تغییرپذیر و تغییرناپذیر» پیروی می‌کند: خود ویجت تغییرناپذیر می‌ماند (مانند StatelessWidget)، و تمام حالت تغییرپذیر در یک شیء State جداگانه ذخیره می‌شود. این به Flutter اجازه می‌دهد ویجت‌ها را با مقایسه بر اساس نوع و Key دوباره استفاده کند و در عین حال حالت جاری را بین بازسازی‌ها حفظ کند.

بر اساس Google (Flutter Architectural Overview, 2026)، StatefulWidget برای سناریوهایی که حالت بیش از یک بار در طول عمر ویجت تغییر می‌کند بهینه است: فیلدهای متنی، انیمیشن‌ها، تایمرها، جریان‌های داده، بارگذاری‌های ناهمگام. برای مقداردهی اولیه یک بار، StatelessWidget کافی است.

چه زمانی StatefulWidget ضروری است

StatefulWidget زمانی اجباری است که ویجت باید به رویدادهای خارجی واکنش نشان دهد: کلیک دکمه، تکمیل درخواست HTTP، به‌روزرسانی داده از پایگاه داده، اشتراک WebSocket. همچنین برای ویجت‌های دارای انیمیشن، فیلدهای متنی با کنترلر و کامپوننت‌های مدیریت فوکوس ضروری است. اگر ویجت فقط داده‌ها را نمایش می‌دهد و رویداد تولید نمی‌کند — از StatelessWidget استفاده کنید.

ساختار داخلی

StatefulWidget از دو کلاس تشکیل شده است: خود StatefulWidget (سبک، تغییرناپذیر) و State (سنگین، تغییرپذیر). فریم‌ورک State را از طریق متد createState() که یک بار هنگام قرارگیری در درخت فراخوانی می‌شود ایجاد می‌کند. State از طریق ویژگی widget به ویجت ارجاع می‌گیرد و می‌تواند در هر لحظه از چرخه حیات به فیلدهای آن دسترسی داشته باشد.

چرخه حیات StatefulWidget

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

createState

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

initState

initState — بلافاصله پس از ایجاد State، قبل از اولین ساخت UI فراخوانی می‌شود. در اینجا انجام می‌شود: مقداردهی کنترلرها (TextEditingController، AnimationController)، اشتراک جریان‌های داده (StreamSubscription)، تنظیم تایمرها و مقداردهی اولیه فیلدها. طبق مستندات Flutter (Flutter.dev, 2026)، در initState نمی‌توان BuildContext.of() را فراخوانی کرد — درخت هنوز به طور کامل سوار نشده است.

didChangeDependencies

didChangeDependencies — پس از initState و هر بار که وابستگی‌های InheritedWidget تغییر می‌کند فراخوانی می‌شود. این مکان مناسبی برای فراخوانی MediaQuery.of(context) یا اشتراک Theme است — مقادیری که ممکن است در حین کار برنامه تغییر کنند. اگر ویجت از InheritedWidget استفاده می‌کند، منطق مقداردهی باید اینجا باشد، نه در initState.

build و didUpdateWidget

build — متد اصلی که درخت ویجت را برمی‌گرداند. پس از initState، پس از didChangeDependencies و پس از هر setState فراخوانی می‌شود. didUpdateWidget زمانی فراخوانی می‌شود که والد بازسازی می‌شود و StatefulWidget را با پارامترهای جدید ارسال می‌کند. در اینجا می‌توان فیلدهای قدیم و جدید ویجت را مقایسه کرد و در صورت نیاز حالت را به‌روزرسانی کرد.

dispose

dispose — مرحله نهایی چرخه حیات. در اینجا تمام منابع آزاد می‌شوند: لغو اشتراک از جریان‌ها، حذف کنترلرها، لغو تایمرها. عدم فراخوانی dispose منجر به نشت حافظه می‌شود. پس از dispose، State مرده محسوب می‌شود — فراخوانی setState در داخل آن یک استثنا ایجاد می‌کند.

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

مکانیزم کار 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 است.

نمونه کد در Dart

یک StatefulWidget ساده — شمارنده کلیک دکمه را در نظر بگیرید. این الگوی پایه را نشان می‌دهد: ایجاد State، مقداردهی فیلد در initState، تغییر از طریق setState:

dart
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 داده را از شبکه بارگذاری کرده و وضعیت بارگذاری را نمایش می‌دهد:

dart
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 و StatelessWidget فقط موضوع وجود حالت نیست. StatefulWidget یک چرخه حیات کامل با متدهای initState، didChangeDependencies، didUpdateWidget و dispose ارائه می‌دهد که برای کار با کنترلرها، انیمیشن‌ها و جریان‌ها ضروری است. StatelessWidget به نوبه خود این متدها را ندارد و همیشه برای فریم‌ورک سبک‌تر است.

توصیه تیم Flutter (Flutter docs, 2026) — تعداد StatefulWidget را در برنامه به حداقل برسانید، حالت را با بالا بردن در درخت (State Hoisting) یا با استفاده از راه‌حل‌های مدیریت حالت (Riverpod، Bloc، Provider) مدیریت کنید. هر StatefulWidget یک شیء State ایجاد می‌کند که تا حذف عنصر زنده می‌ماند — هر چه تعداد این ویجت‌ها بیشتر باشد، بار حافظه بالاتر است.

معیارStatefulWidgetStatelessWidget
حالتتغییرپذیرتغییرناپذیر
چرخه حیات۶ مرحلهفقط 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 یک بازسازی کامل ویجت را آغاز می‌کند. اگر حالت با فرکانس بالا تغییر می‌کند (مثلاً انیمیشن یا جریان داده)، به جای فراخوانی دستی 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 را مدیریت کند که منجر به باگ‌های دشوار می‌شود.

توصیه‌هایی برای جلوگیری از خطاها

  • همیشه قبل از setState در callbackهای ناهمگام mounted را بررسی کنید
  • فراخوانی super.initState() و super.dispose() را فراموش نکنید
  • درخواست HTTP را مستقیماً در build انجام ندهید — از initState استفاده کنید
  • از تمام اشتراک‌ها در dispose لغو اشتراک کنید
  • از حداقل تعداد StatefulWidget در پروژه استفاده کنید

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

تفاوت StatefulWidget با StatelessWidget چیست؟

StatefulWidget می‌تواند حالت خود را از طریق setState تغییر دهد، دارای چرخه حیات (initState، dispose) است و یک شیء State جداگانه ایجاد می‌کند. StatelessWidget نمی‌تواند حالت را تغییر دهد و متدهای چرخه حیات ندارد — فقط داده‌های ارسالی را نمایش می‌دهد.

createState چند بار فراخوانی می‌شود؟

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

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

منابع آزاد نمی‌شوند: کنترلرها در پس‌زمینه به کار ادامه می‌دهند، اشتراک جریان‌ها فعال می‌مانند، تایمرها لغو نمی‌شوند. این منجر به نشت حافظه می‌شود و می‌تواند باعث فراخوانی setState پس از dispose شود که یک استثنا ایجاد می‌کند.

آیا StatefulWidget می‌تواند const باشد؟

بله، سازنده StatefulWidget می‌تواند const باشد. اما این مزیت مشابه StatelessWidget را ندارد — شیء State همچنان در اولین قرارگیری ایجاد می‌شود. const فقط روی خود ویجت (پوشش سبک) تأثیر می‌گذارد، نه روی State.

متد didUpdateWidget برای چیست؟

didUpdateWidget زمانی فراخوانی می‌شود که والد StatefulWidget را با پارامترهای جدید ارسال می‌کند. این برای همگام‌سازی حالت با داده‌های جدید لازم است — مثلاً اگر userId در پارامترها تغییر کند، باید پروفایل کاربر جدید بارگذاری شود.

خلاصه

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

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

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

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

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