الودجات المركبة القابلة لإعادة الاستخدام
الودجات المركبة القابلة لإعادة الاستخدام
مع نمو تطبيقات Flutter، ستلاحظ تكرار نفس أنماط واجهة المستخدم عبر شاشات متعددة. بطاقة ملف شخصي هنا، وحقل نص موسوم هناك، وشارة حالة في ثلاثة أماكن مختلفة. إن نسخ أشجار الودجات ولصقها يُفضي إلى كوابيس الصيانة: تغيير تصميم واحد يستلزم تحديث كل نسخة مكررة. الحل هو استخراج أشجار واجهة المستخدم المتكررة إلى فئات ودجت مستقلة ومُسمَّاة ذات معاملات بنّاء مكتوبة بأنواع محددة.
يُعلّمك هذا الدرس كيفية تحديد فرص الاستخراج، والاختيار بين StatelessWidget وStatefulWidget، وكتابة توقيعات بنّاء نظيفة، وفهم متى يُفضَّل التركيب على الوراثة.
لماذا نستخرج إلى فئة ودجت؟
- مصدر واحد للحقيقة — غيّر التصميم مرة واحدة، وتتحدث كل الاستخدامات تلقائياً.
- قابلية القراءة —
ProfileCard(user: user)يُعبّر عن الغرض بشكل أفضل بكثير من 20 سطراً من الودجات المتداخلة المُضمَّنة. - قابلية الاختبار — الودجات الصغيرة والمحددة الغرض يسهل اختبارها بشكل معزول.
- كفاءة إعادة البناء — يمكن لـ Flutter تجاوز إعادة بناء الودجات التي لم تتغير مقارناتها، وهذا ممكن فقط مع الودجات المُسمَّاة التي تدعم المُنشئات الثابتة (
const).
Widget _buildHeader()) حين يمكن استخدام فئة مستقلة. التوابع تُعاد دائماً مع الأب؛ أما الفئة المستقلة فتمنح Flutter فرصة تجاوز إعادة البناء كلياً.StatelessWidget: العرض النقي
استخدم StatelessWidget حين يتحدد مظهر الودجت كلياً بمعاملات البنّاء وسياق البناء. ليس لديه بيانات داخلية قابلة للتغيير. كل خاصية تستقبلها من الخارج تصبح حقلاً final، وتُكشَف عبر مُنشئ يدعم const.
مثال — InfoBadge (StatelessWidget)
import 'package:flutter/material.dart';
/// شارة ملونة صغيرة لعرض تسمية حالة.
/// تتحدد كلياً بمعاملات البنّاء — لا حالة داخلية.
class InfoBadge extends StatelessWidget {
final String label;
final Color color;
final Color textColor;
const InfoBadge({
super.key,
required this.label,
this.color = Colors.blue,
this.textColor = Colors.white,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(12),
),
child: Text(
label,
style: TextStyle(
color: textColor,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
);
}
}
// الاستخدام — نفس الودجت في ثلاثة سياقات مختلفة:
InfoBadge(label: 'جديد', color: Colors.green)
InfoBadge(label: 'تخفيض', color: Colors.orange)
InfoBadge(label: 'نُفِّذ', color: Colors.grey)
لاحظ المُنشئ const. عندما يرى Flutter const InfoBadge(label: 'جديد', color: Colors.green)، يمكنه إعادة استخدام نفس الكائن عبر إعادات البناء، متجاوزاً خطوة البناء لتلك الشجرة الفرعية كلياً.
StatefulWidget: التفاعلية الذاتية المحتواة
استخدم StatefulWidget حين يجب على الودجت تتبع بياناته الداخلية القابلة للتغيير — بيانات تعيش داخل الودجت ولا تحتاج إلى مشاركتها مع الخارج. أمثلة كلاسيكية: لوحة قابلة للطي، وزر تبديل المفضلة، وعداد متحرك.
مثال — ExpandableSection (StatefulWidget)
import 'package:flutter/material.dart';
/// قسم محتوى قابل للطي يدير حالته المفتوحة/المغلقة بنفسه.
class ExpandableSection extends StatefulWidget {
final String title;
final Widget child;
const ExpandableSection({
super.key,
required this.title,
required this.child,
});
@override
State<ExpandableSection> createState() => _ExpandableSectionState();
}
class _ExpandableSectionState extends State<ExpandableSection> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () => setState(() => _isExpanded = !_isExpanded),
child: Row(
children: [
Expanded(
child: Text(
widget.title,
style: Theme.of(context).textTheme.titleMedium,
),
),
Icon(_isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
),
if (_isExpanded)
Padding(
padding: const EdgeInsets.only(top: 8),
child: widget.child,
),
],
);
}
}
// الاستخدام:
ExpandableSection(
title: 'تفاصيل الشحن',
child: Text('يصل خلال 3–5 أيام عمل.'),
)
State، استخدم widget.اسمالخاصية — على سبيل المثال widget.title أعلاه. الخاصية widget يوفرها الصنف الأساسي State<T> وترجع دائماً نسخة الودجت الحالية.التركيب مقابل الوراثة
نموذج الودجت في Flutter مبني على التركيب — تغليف الودجات الموجودة ودمجها — لا على الوراثة الكلاسيكية. نادراً ما تُشتق فئة فرعية من Container أو Row أو أي ودجت ملموس. بدلاً من ذلك، تبني فئة جديدة تحتوي تلك الودجات كجزء من تابع build الخاص بها.
- فضّل التركيب حين تريد تجميع واجهة مستخدم جديدة من العناصر الأولية الموجودة.
- استخدم الوراثة فقط حين تحتاج حقاً إلى تجاوز سلوك الإطار، كإنشاء
RenderObjectمخصص. - لمشاركة المنطق بين الودجات دون وراثة، استخرج المنطق إلى فئة Dart عادية أو mixin، ثم حقّنه عبر البنّاء.
class MyCard extends Card يبدو مُغرياً لكنه يتعطل سريعاً — الودجات الملموسة في Flutter غير مصممة كفئات أساسية مستقرة وتتغير واجهاتها الداخلية بين الإصدارات. دائماً استخدم التركيب، ولا توسّع الودجات الملموسة أبداً.معاملات البنّاء المكتوبة — أفضل الممارسات
الودجت المركّب بقدر جودة واجهته. اتّبع هذه الإرشادات عند تصميم معاملات البنّاء:
- ضع
requiredعلى كل معامل لا يمكن للودجت أن يعمل بدونه. - وفّر قيماً افتراضية معقولة لمعاملات التنسيق الاختيارية حتى لا يضطر المستدعون لتحديدها في كل مرة.
- اقبل ردود النداء (callbacks) كـ
VoidCallbackمكتوب أوValueChanged<T>بدلاً منFunctionعامة. - اقبل محتوى الأبناء كمعامل
Widget(أوList<Widget>) عوضاً عن بنائه داخلياً حين تحتاج المرونة. - أضف معاملاً
Key? keyمُمرَّراً إلىsuper.keyحتى يشارك الودجت بشكل صحيح في خوارزمية المطابقة.
الخلاصة
استخراج أشجار واجهة المستخدم المتكررة إلى فئات StatelessWidget أو StatefulWidget مُسمَّاة هو من أكثر العادات تأثيراً التي يمكنك تطويرها كمطوّر Flutter. ينتج عنه واجهات مستخدم مقروءة وقابلة للصيانة وتُعيَد رسمها بكفاءة. استخدم StatelessWidget للعرض النقي المُشغَّل بمعاملات البنّاء، وStatefulWidget حين يمتلك الودجت حالة قابلة للتغيير محتواة في نفسه. دائماً ركّب الودجات بدلاً من توسيعها، وصمّم توقيعات البنّاء لتكون صريحة ومكتوبة الأنواع وذات قيم افتراضية معقولة.