محتويات الدورة

Flutter / Dart والعناصر والملكية المحلية / الدرس 4

الحالة المحلية وملكية دورة الحياة

احفظ الحالة خارج build، واطلب إعادة البناء بتغيير متزامن، وحرّر الموارد عند حدود مالكها.

وقت القراءة التقريبي: 5 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • افصل الوصف عن حالته المستمرة
  • أبق التهيئة خارج build
  • نفّذ انتقال الحالة بصورة متزامنة

افصل الوصف عن حالته المستمرة

يبقى StatefulWidget إعدادا ثابتا. ويحتفظ كائن State المنفصل بالمعلومات القابلة للتغيير عبر عمليات البناء المتوافقة للهوية نفسها. غالبا ما ينتمي إليه علم التوسيع أو اختيار مسودة محلي، لأن عمره يتبع ظهورا معينا في الواجهة. أما المعلومات المشتركة بين الشاشات فقد تحتاج إلى مالك أوسع. اختر المالك بحسب من يحتاج المعلومة ومدة بقائها، لا بقاعدة تجعل كل القيم عامة. لا تنشئ إعادة البناء بحد ذاتها State جديدة، لكن إزالة الهوية قد تنهي عمر الحالة.

أبق التهيئة خارج build

هيّئ الموارد التي تُنشأ مرة واحدة عند الحد المناسب لدورة الحياة، واقرأ إعداد العنصر الحالي عبر widget. تفيد initState في التهيئة التي لا تعتمد على بحث موروث يحتاج إلى تتبع الاعتماديات. وتعالج didChangeDependencies تلك الاعتماديات، بينما تستجيب didUpdateWidget عندما يزوّد عنصر متوافق كائنا خارجيا مختلفا. لكل خطاف سبب مختلف. بدء المؤقت نفسه داخل build يسجل مؤقتا إضافيا بعد تغيرات مرئية غير مرتبطة. وعندما يتبع الاشتراك كائن مدخل، خطط لفك الارتباط بالقديم والارتباط بالجديد.

نفّذ انتقال الحالة بصورة متزامنة

يبدّل المقتطف الأول قيمة منطقية محلية فقط. تنفّذ setState رد النداء بصورة متزامنة وتخبر Flutter بأن الحالة تحتاج إلى إعادة البناء؛ ولا تنفذ عملية المجال نيابة عنك. اجعل رد النداء صغيرا وخاليا من await. وإذا احتاج المعالج إلى عمل غير متزامن، فانتظره خارج رد النداء ثم اعتمد الحالة الناتجة عن قصد. تُشتق التسمية من القيمة المنطقية بدلا من تخزين نص ثان قابل للتعديل، مما يمنع اجتماع لوحة مفتوحة مع زر ما زال يقول Show.

dart مقتطف توضيحي من Dart أو Flutter؛ يتطلب بيئة SDK المبيّنة.
class DetailsPanel extends StatefulWidget {
  const DetailsPanel({super.key});
  @override State<DetailsPanel> createState() => _DetailsPanelState();
}
class _DetailsPanelState extends State<DetailsPanel> {
  bool open = false;
  @override Widget build(BuildContext context) => Column(children: [
    TextButton(onPressed: () => setState(() { open = !open; }),
      child: Text(open ? 'Hide' : 'Show')),
    if (open) const Text('Local details'),
  ]);
}
الناتج المتوقع
Initially Show; after activation Hide and Local details.

توفر البيئة استيراد Material وسياق التطبيق. ويطلب كل تفعيل عرض الوصف الجديد المشتق من الحالة.

حرّر الموارد عند مالكها

يمكن لوحدة تحكم أن تعيش أكثر من عملية بناء واحدة، لكن لا ينبغي أن تعيش بعد State المالكة دون نقل صريح للملكية. ينشئ المقتطف الثاني TextEditingController مرة واحدة ويحرّره عند مغادرة المالك للشجرة. وإذا وفّر الأب وحدة التحكم فلا ينبغي عادة للابن تحريرها، لأن ملكيتها ما زالت للأب. طبّق المنطق نفسه على عقد التركيز ووحدات الرسوم والمؤقتات والاشتراكات مستخدما طرق الإلغاء الموثقة. يتعلق التنظيف بإيقاف العمل وتحرير المراجع، لا بمجرد إخفاء استثناء بعد اختفاء الشاشة.

dart مقتطف توضيحي من Dart أو Flutter؛ يتطلب بيئة SDK المبيّنة.
class DraftField extends StatefulWidget {
  const DraftField({super.key});
  @override State<DraftField> createState() => _DraftFieldState();
}
class _DraftFieldState extends State<DraftField> {
  final text = TextEditingController();
  @override void dispose() { text.dispose(); super.dispose(); }
  @override Widget build(BuildContext context) => TextField(controller: text);
}
الناتج المتوقع
A text field retains its draft across owner rebuilds and releases its controller on disposal.

إنشاء وحدة التحكم داخل build سيستبدل مالك الإدخال مرارا. وتحرير وحدة تحكم مستعارة يخرق ملكية المستدعي.

تعامل مع mounted كفحص للعمر

بعد انتظار عمل قد تكون State حُرّرت بالفعل. يمنع فحص mounted قبل تعديل حالة الواجهة اعتماد نتيجة لدى مالك انتهى عمره، لكنه لا يلغي العمل ولا يثبت أن النتيجة ما زالت حديثة. قد تبقى شاشة البحث موجودة بينما يصبح استعلامها القديم متقادما. تضيف دروس لاحقة هوية الطلب لهذه الحالة. ألغ المورد أو الاشتراك عند حد دورة حياته متى أمكن. ولا تحتفظ بسياق لمجرد عرض رسالة مؤجلة؛ تحقّق أولا من بقاء مالك الواجهة ومن استمرار ملكيته للتفاعل.

اشتق القيم التي لا تحتاج إلى ملكية مستقلة

ينبغي عادة اشتقاق المجموع الفرعي من البنود الحالية، لا الاحتفاظ به كقيمة متغيرة تحدّثها كل الأزرار. تخلق الحالة المكررة التزامات مزامنة. وفي المقابل تستحق مسودة قابلة للتحرير تختلف عمدا عن البيانات المحفوظة مالكا وقواعد حفظ أو إلغاء مستقلة. اسأل أي مصدر هو المعتمد وأي حدث يعتمد التغيير. توضح هذه الأسئلة هل تكفي setState أم يلزم نموذج مستقل. لا تجيب حزمة إدارة الحالة تلقائيا عن أسئلة الملكية؛ فهي توفر آليات بعد تقرير عقد التطبيق.

الأخطاء الشائعة وإصلاحها

تُصفّر المسودة كلما أعاد الأب البناء

لماذا يحدث؟
يعاد إنشاء وحدة التحكم أو المسودة داخل build.
كيف تصلحه؟
احفظ المورد لدى State المالكة وحرّره في dispose.
كيف تتجنب تكراره؟
اختبر إعادة بناء الأب والحقل يحوي مدخلا.

تُستدعى setState بعد dispose

لماذا يحدث؟
عاش رد نداء مؤجل بعد State.
كيف تصلحه؟
ألغ العمل المملوك وافحص العمر قبل اعتماد الواجهة.
كيف تتجنب تكراره؟
اختبر مغادرة الشاشة أثناء العمل المعلق.

تتعطل وحدة تحكم مستعارة في موضع آخر

لماذا يحدث؟
حرّر ابن موردا لا يملكه.
كيف تصلحه؟
دع المالك المنشئ يحرره ووثّق الاستعارة.
كيف تتجنب تكراره؟
أظهر ملكية الموارد صراحة في عقود العناصر.

اختبر فهمك

1. هل ينبغي أن تبقى لوحة مساعدة محلية مفتوحة بعد تسجيل الخروج؟

اعرض الحل المشروح

حدّد العمر المقصود أولا. إذا كانت اللوحة تخص الشاشة الحالية، فيمكن أن تنتهي حالتها المحلية بمغادرة الشاشة. وإذا كان استعادتها مطلبا للمنتج، فاحفظ تفضيلا محدود النطاق عمدا بدلا من الاحتفاظ عرضا بالشاشة القديمة أو سياقها.

2. يُخزّن المجموع منفصلا عن قائمة البنود. ما التناقض الممكن؟

اعرض الحل المشروح

قد يحدّث تعديل القائمة وينسى تحديث المجموع، فتظهر حقيقتان متنافستان. احسب المجموع من البنود الحالية الثابتة متى أمكن. وإذا لزم التخزين المؤقت فأبطله لدى المالك نفسه وتحقّق من كل مسار تغيير.

أهم ما تعلمته

  • افصل الوصف عن حالته المستمرة
  • اشتق القيم التي لا تحتاج إلى ملكية مستقلة

المراجع الرسمية