ما هو SwitchListTile
؟
SwitchListTile
هو نسخة مريحة من ListTile
تحتوي على مفتاح تبديل (Switch) مدمج. تُستخدم عادة لعرض خيار قابل للتشغيل/الإيقاف داخل قائمة (مثل صفحة الإعدادات). يسمح لك بعرض title
, subtitle
, وsecondary
جنبًا إلى جنب مع الـ Switch
، والصف كله قابل للنقر لتغيير الحالة (إذا وُفِّر onChanged
).
البنية الأساسية (constructor)
SwitchListTile({
Key? key,
required bool value,
required ValueChanged<bool>? onChanged,
Widget? title,
Widget? subtitle,
Widget? secondary,
bool isThreeLine = false,
bool dense,
bool selected = false,
Color? activeColor,
Color? activeTrackColor,
Color? inactiveThumbColor,
Color? inactiveTrackColor,
ImageProvider? activeThumbImage,
ImageProvider? inactiveThumbImage,
MouseCursor? mouseCursor,
bool autofocus = false,
Color? focusColor,
Color? hoverColor,
MaterialTapTargetSize? materialTapTargetSize,
ListTileControlAffinity controlAffinity = ListTileControlAffinity.trailing,
EdgeInsetsGeometry? contentPadding,
})
Dartيوجد أيضاً SwitchListTile.adaptive(...)
— يتكيّف شكله مع النظام (مثلاً iOS غالبًا يعطي مظهر مختلف).
أهم الخصائص وشرحها سريعًا
value
(bool
) — الحالة الحالية للمفتاح.onChanged
(ValueChanged<bool>?
) — تُستدعى عند تغيّر الحالة؛ إذا كانتnull
يصبح العنصر معطلاً.title
/subtitle
— نصوص العرض الأساسية.secondary
— ويدجت ثانوية (غالبًاIcon
أوCircleAvatar
) تظهر في طرف العنصر الآخر.controlAffinity
— يحدد مكان المفتاح:ListTileControlAffinity.trailing
(افتراضي) → المفتاح على الطرف النهائي (يمين في RTL، يسار في LTR حسب الاتجاه).ListTileControlAffinity.leading
→ المفتاح قبل المحتوى.ListTileControlAffinity.platform
→ سلوك حسب النظام.
activeColor
— لون الـ thumb عند التفعيل (يفضل استخدام SwitchTheme للتخصيص الشامل).activeTrackColor
— لون مسار المفتاح عند التفعيل.inactiveThumbColor
/inactiveTrackColor
— الألوان عند التعطيل.activeThumbImage
/inactiveThumbImage
— صورة توضع فوق الـ thumb.isThreeLine
— يسمح بثلاثة أسطر (title + subtitle يمكن أن يلتف لأسطر متعددة).dense
— يقلل المسافات داخل الصف.selected
— يغيّر نمط النص/أيقونة ليظهر كمحدد.contentPadding
— التحكم بهوامش الداخل.autofocus
,mouseCursor
,focusColor
,hoverColor
,materialTapTargetSize
— لميزات الوصول والسطح/ويب.
سلوك مهم
- النقر على أي مكان في الصف (ليس فقط على الزر) يغيّر الحالة — طالما
onChanged
ليسnull
. هذا يجعل التجربة بديهية للمستخدم. - إذا أردت أن يكون فقط الـ switch هو القابل للنقر (وليس الصف كله)، عندها يجب بناء
ListTile
مخصّص ووضَعSwitch
فيtrailing
مع منع التبديل عبر نقر الصف.
أمثلة عملية
1 — أبسط استخدام
bool notifications = false;
SwitchListTile(
title: Text('الإشعارات'),
subtitle: Text('تشغيل/إيقاف الإشعارات'),
value: notifications,
onChanged: (val) => setState(() => notifications = val),
);
Dart2 — مع أيقونة ثانوية (secondary) والمفتاح على اليسار
SwitchListTile(
title: Text('الوضع الليلي'),
secondary: Icon(Icons.brightness_2),
value: isDark,
controlAffinity: ListTileControlAffinity.leading,
onChanged: (v) => setState(() => isDark = v),
);
Dart3 — تعطيل العنصر
SwitchListTile(
title: Text('مزامنة الخلفية'),
value: false,
onChanged: null, // معطّل
);
Dart4 — نسخة متكيّفة (تغيّر الشكل عند iOS)
SwitchListTile.adaptive(
title: Text('مزامنة الصور'),
value: syncPhotos,
onChanged: (v) => setState(() => syncPhotos = v),
);
Dart5 — صورة على الـ thumb
SwitchListTile(
title: Text('خاصية مع صورة'),
value: myVal,
activeThumbImage: AssetImage('assets/on_icon.png'),
inactiveThumbImage: AssetImage('assets/off_icon.png'),
onChanged: (v) => setState(() => myVal = v),
);
Dart6 — التبديل غير متزامن (نمط optimistic + revert on failure)
SwitchListTile(
title: Text('ميزة سحابة'),
value: enabled,
onChanged: (v) async {
// تحديث واجهة المستخدم مبدئيًا
setState(() => enabled = v);
try {
await myApi.updateFeatureEnabled(v); // نداء الشبكة
} catch (e) {
// إذا فشل، ارجع للحالة القديمة وأخبر المستخدم
setState(() => enabled = !v);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('فشل التحديث')));
}
},
);
Dartالتيمينغ (تخصيص مظهر مركزي)
- لتخصيص ألوان كل الـ Switch في التطبيق:
MaterialApp(
theme: ThemeData(
switchTheme: SwitchThemeData(
thumbColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) return Colors.white;
return Colors.grey.shade200;
}),
trackColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) return Colors.blue;
return Colors.grey;
}),
),
listTileTheme: ListTileThemeData(
iconColor: Colors.blueGrey,
textColor: Colors.black87,
),
),
home: ...
)
DartSwitchListTile
يرث مظهر الـ Switch
من switchTheme
، ونمط النص/أيقونة من listTileTheme
.
التكامل مع نماذج الحالة (State management)
- Provider / Riverpod / Bloc: بدّل
value
وonChanged
لقراءة/تحديث الحالة من الـ provider أو bloc بدلاً منsetState
.
مثال بسيط مع Provider:
class SettingsModel with ChangeNotifier {
bool _notifications = true;
bool get notifications => _notifications;
set notifications(bool v) { _notifications = v; notifyListeners(); }
}
// في الواجهة
Consumer<SettingsModel>(
builder: (_, model, __) => SwitchListTile(
title: Text('الإشعارات'),
value: model.notifications,
onChanged: (v) => model.notifications = v,
),
);
Dartدمجه داخل Form
لا يوجد SwitchListTileFormField
مدمج، لكن يمكنك لفه بـ FormField<bool>
لتوفر validation وsave:
FormField<bool>(
initialValue: initialValue,
builder: (state) {
return SwitchListTile(
title: Text('مشاركة الموقع'),
value: state.value ?? false,
onChanged: (v) {
state.didChange(v);
},
);
},
validator: (val) => val == true ? null : 'يرجى تفعيل المشاركة',
);
Dartنصائح وملاحظات عملية
- التفاعل الكلي للصف: قابلية النقر في كامل الصف مفيدة في شاشات الإعداد — لكن إذا كنت تريد زر إعدادات ثانوي قابل للنقر (مثل زر المعلومات) تعامل مع ذلك بشكل منفصل.
- استخدم
.adaptive
للمطابقة مع تصاميم النظام (iOS vs Android) بسهولة. - الأداء: في قوائم طويلة استخدم
ListView.builder
وconst
حيث أمكن. إن كان لديك كثير منSwitchListTile
، حاول أن تجعل كل عنصرconst
/خالي من الحسابات الثقيلة أو استخدم keys مناسبة لمنع إعادة بناء غير ضرورية. - تجنّب عمليات ثقيلة في
onChanged
مباشرة — إذا كانت تستدعي الشبكة، استعمل نمط optimistic أو أظهر مؤشر تحميل واضح ولا تحظر الواجهة. قد ترد بعمل disable مؤقت للعنصر أثناء العملية. - الوصول (Accessibility):
title
وsubtitle
يُقرأان للقارئ الشاشة. إذا تحتاج وصفاً خاصاً أكثر استخدمSemantics
حول الـSwitchListTile
لتزويدlabel
أوhint
. - التحكم في موقع المفتاح عبر
controlAffinity
إذا أردت المفتاح في اليسار (مثلاً في قائمة RTL خاصة أو لتصميم معين). - التخصيص الشامل: لتغيير سلوك التينت في Material 3، انتبه إلى
surfaceTintColor
وswitchTheme
لأن بعض القيم قد تُطبّق افتراضيًا من الثيم.
أخطاء شائعة وحلولها
- خطأ: لا يتغير الـ Switch عند النقر
- السبب:
onChanged
=null
أوvalue
لا يُحدّث (نسيتsetState
أو لم تحدث القيمة في الـ provider). - الحل: تأكد أن
onChanged
يعطي قيمة وأنك تُحدِّث الحالة فعليًا.
- السبب:
- أريد أن أسمح فقط بالنقر على الـ thumb وليس على الصف
- الحل: لا تستخدم
SwitchListTile
مباشرة؛ بدلاً من ذلك اصنعListTile
عادي وضعSwitch
فيtrailing
مع ضبطonTap
الخاص بالـListTile
إذا أردت سلوك مخصص.
- الحل: لا تستخدم
- التباعد/القياس غير المناسب في القوائم
- الحل: استخدم
dense
,contentPadding
, وisThreeLine
للتحكم بالارتفاع والمسافات.
- الحل: استخدم