StatefulWidget: ما هو، دورة الحياة ومبدأ العمل

المؤلف: IT Sectr نُشر: 2026-07-01 وقت القراءة: 9 دق

StatefulWidget هو أداة Flutter ذات حالة قابلة للتغيير، تسمح لواجهة المستخدم بالتفاعل مع إجراءات المستخدم والأحداث غير المتزامنة وتدفقات البيانات. وفقًا للتوثيق الرسمي لـ Flutter (Flutter.dev, 2026)، يُستخدم StatefulWidget لجميع العناصر التفاعلية للتطبيق: نماذج الإدخال والرسوم المتحركة ومربعات الاختيار والمفاتيح والشاشات التي تحمّل البيانات من الشبكة. على عكس StatelessWidget، فإنه ينشئ كائن State منفصلًا يبقى طوال دورة حياته بالكامل ويمكن إعادة بنائه دون إعادة إنشاء الأداة نفسها.

الرئيسية

  • StatefulWidget هو أداة يمكنها تغيير حالتها أثناء التشغيل، مما يؤدي إلى إعادة بناء واجهة المستخدم عبر setState
  • دورة الحياة — يمر StatefulWidget بمراحل createState وinitState وdidChangeDependencies وbuild وdidUpdateWidget وdispose
  • كائن State هو كائن منفصل يخزن الحالة ويوجد بشكل مستقل عن الأداة طوال عمرها بالكامل
  • setState هو الطريقة الشرعية الوحيدة لإعلام Flutter بضرورة إعادة بناء الأداة بعد تغيير البيانات
  • الأداء — الاستخدام المفرط لـ StatefulWidget يزيد من استهلاك الذاكرة ووقت العرض

ما هو StatefulWidget؟

StatefulWidget هو كلاس Flutter يمكنه تغيير حالته استجابة لإجراءات المستخدم أو أحداث النظام أو العمليات غير المتزامنة. على عكس StatelessWidget، لا يتم عرض StatefulWidget مباشرة — بل ينشئ كائن State يتولى مسؤولية العرض. هذا الفصل إلى كلاسين (Widget و State) يسمح لـ Flutter بإعادة بناء واجهة المستخدم دون إعادة إنشاء الأداة نفسها، مما يوفر ميزة أداء كبيرة أثناء التحديثات المتكررة.

هندسة 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، قبل أول بناء لواجهة المستخدم. هنا يتم: تهيئة وحدات التحكم (TextEditingController و AnimationController) والاشتراك في تدفقات البيانات (StreamSubscription) وإعداد الموقتات والتهيئة الأولية للحقول. وفقًا لوثائق Flutter (Flutter.dev, 2026)، لا يمكن استدعاء BuildContext.of() في initState — الشجرة ليست مركبة بالكامل بعد.

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 بشكل مستقل في رد النداء الذي تم تمريره إلى setState. بعد اكتمال رد النداء، يستدعي Flutter build ويحدث واجهة المستخدم.

وفقًا لفريق Dart/Flutter (Dart Language Specification, 2026)، يضمن هذا الفصل أن جميع تغييرات الحالة تحدث بشكل متزامن قبل استدعاء build، مما يلغي الموقف الذي تعرض فيه واجهة المستخدم بيانات محدثة جزئيًا. هذه آلية رئيسية لاتساق الواجهة في 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: $_count'),
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Increment'),
        ),
      ],
    );
  }
}

مثال مع تحميل بيانات غير متزامن وإدارة دورة الحياة. يقوم 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('Hello, ${_user!.name}');
  }
}

في المثال الثاني، من المهم ملاحظة: initState يبدأ عملية غير متزامنة، لكن الطريقة نفسها ليست غير متزامنة. يتم تنفيذ عدم التزامن عبر async/await داخل طريقة منفصلة _loadUser، والتي تحديث الحالة عبر setState بعد اكتمال الطلب. هذا النهج يضمن أن الأداة تعرض مؤشر التحميل بشكل صحيح قبل استلام البيانات.

StatefulWidget مقابل StatelessWidget

الاختيار بين StatefulWidget و StatelessWidget ليس فقط حول وجود الحالة. StatefulWidget يوفر دورة حياة كاملة مع طرق initState و didChangeDependencies و didUpdateWidget و dispose، وهي ضرورية للعمل مع وحدات التحكم والرسوم المتحركة والتدفقات. StatelessWidget، من ناحية أخرى، لا يمتلك هذه الطرق وهو دائمًا أخف على الإطار.

توصية فريق Flutter (Flutter docs, 2026) هي تقليل عدد StatefulWidgets في التطبيق عن طريق رفع الحالة لأعلى الشجرة (State Hoisting) أو استخدام حلول إدارة الحالة (Riverpod و Bloc و Provider). كل StatefulWidget ينشئ كائن State يعيش حتى يتم إزالة العنصر — كلما زاد عدد هذه الأدوات، زاد حمل الذاكرة.

المعيارStatefulWidgetStatelessWidget
الحالةقابلة للتغييرغير قابلة للتغيير
دورة الحياة6 مراحلbuild فقط
كائن Stateيُنشأ بشكل منفصلغير مطلوب
setStateمتاحغير متاح
الاشتراكاتinitState/disposeغير مدعومة
منشئ constمحدودمدعوم بالكامل
استهلاك الذاكرةأعلىأقل

الأداء والتحسين

StatefulWidget يتطلب موارد أكثر من StatelessWidget بسبب الحاجة إلى إنشاء وصيانة كائن State. ومع ذلك، الاستخدام الصحيح لـ StatefulWidget لا يؤدي إلى مشاكل في الأداء إذا تم اتباع بعض القواعد. أولاً، تجنب التداخل العميق لـ StatefulWidget — كل مستوى يضيف عبئًا إضافيًا على اجتياز الشجرة. ثانيًا، قسم StatefulWidget المعقد إلى عدة أدوات بسيطة، كل منها مسؤول عن الجزء الخاص به من الحالة.

وفقًا لبحث أداء Flutter (Flutter.dev, فبراير 2026)، السبب الأكثر شيوعًا لانخفاض FPS هو استدعاء setState في أداة أب تعيد بناء جميع العناصر التابعة، بما في ذلك StatelessWidgets التي لم تغير عرضها. الحل هو استخراج الجزء القابل للتغيير من واجهة المستخدم إلى StatefulWidget منفصل بحيث يعيد setState بناء الحد الأدنى من الأدوات الضرورية فقط.

استخدام const داخل State هو تقنية مهمة أخرى. إذا تم التصريح عن الأدوات التابعة كـ const، فلن يعيد Flutter بناءها عند استدعاء setState في الأب. هذا يقلل العبء على الإطار ويقلل وقت عرض الإطار.

تجنب setState المتكرر

كل استدعاء لـ setState يؤدي إلى إعادة بناء كاملة للأداة. إذا كانت الحالة تتغير بتردد عالٍ (على سبيل المثال، الرسوم المتحركة أو تدفق البيانات)، فكر في استخدام AnimatedBuilder أو ValueListenableBuilder أو StreamBuilder بدلاً من استدعاء setState يدويًا. هذه الأدوات تحسن إعادة البناء، وتحديث فقط جزء واجهة المستخدم الذي تغير بالفعل.

الأخطاء الشائعة

أول خطأ شائع مع StatefulWidget هو استدعاء setState بعد dispose. عندما يتم إزالة الأداة من الشجرة، يعتبر State ميتًا، وأي استدعاء لـ setState يلقي استثناء “setState called after dispose”. يحدث هذا غالبًا عندما تكتمل عملية غير متزامنة بعد إزالة الأداة. الحل هو التحقق من العلم mounted قبل استدعاء setState أو إلغاء العمليات غير المتزامنة في dispose.

الخطأ الثاني هو إجراء عمليات حسابية ثقيلة في طريقة build. نظرًا لأن build تُستدعى في كل setState وفي كل إعادة بناء للأب، يجب أن تكون جميع العمليات الحسابية خفيفة الوزن قدر الإمكان. إذا كانت هناك حاجة لعملية مكثفة للموارد، فانقلها إلى Isolate منفصل أو خزّن النتيجة في حقل State.

الخطأ الثالث هو عدم استدعاء super.initState() و super.dispose(). عند إعادة تعريف هذه الطرق، يجب على المطور استدعاء تنفيذ الأب. إذا لم يفعل ذلك، لن يتمكن الإطار من إدارة حالة Element بشكل صحيح، مما يؤدي إلى أخطاء يصعب تتبعها.

توصيات لتجنب الأخطاء

  • تحقق دائمًا من mounted قبل setState في ردود النداء غير المتزامنة
  • لا تنس استدعاء super.initState() و super.dispose()
  • لا تقم بطلبات HTTP مباشرة في build — استخدم initState
  • ألغِ جميع الاشتراكات في dispose
  • استخدم الحد الأدنى من StatefulWidgets في مشروعك

الأسئلة الشائعة

ما الفرق بين 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 تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا