Theme وThemeData — ما هما، المبادئ وتكوين الأنماط

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

Theme وThemeData — الآليتان المركزيتان للتنسيق في Flutter، تتيحان تعيين الألوان والخطوط والهوامش وأنماط المكونات بشكل عام للتطبيق بأكمله. ThemeData هو كائن تكوين يتم تمريره إلى MaterialApp عبر معلمة theme. وفقًا للوثائق الرسمية لـ Flutter (2026)، فإن التكوين الصحيح لـ ThemeData يقلل كود الأنماط المكرر بنسبة 70–80% ويضمن مظهرًا مرئيًا موحدًا عبر الشاشات. يتيح نظام الوراثة المدمج إمكانية تجاوز الموضوع على مستوى الأدوات الفردية عبر Theme.of(context).

الخلاصة

  • ThemeData — كائن تكوين بلوحة ألوان وخطوط وأنماط المكونات
  • Theme.of(context) — الوصول إلى الموضوع الحالي من أي أداة عبر BuildContext
  • Light/Dark — دعم مدمج للموضوع الفاتح والداكن عبر theme وdarkTheme
  • Theme — أداة ترث الموضوع للعناصر التابعة وتسمح بتجاوزه
  • ColorScheme — مجموعة من الألوان المتناسقة التي تولد أنماطًا لنظام Material بأكمله

ما هما Theme وThemeData

ThemeData هو فصل يحتوي على الوصف الكامل للنمط المرئي لتطبيق Material. يتم تمريره إلى MaterialApp عبر معلمة theme أو darkTheme ويُورّث لجميع الأدوات التابعة. تستخدم كل أداة من Material Design ThemeData للحصول على أنماطها: ألوان الأزرار، أحجام AppBar، أنماط النص، هوامش البطاقات والأيقونات.

في جوهر ThemeData يوجد ColorScheme، الذي يحدد الألوان الأساسية والثانوية والسطحية والخطأ وغيرها. من هذه الألوان تُحسب تلقائيًا الحالات (hover، focus، pressed) لجميع مكونات Material. على سبيل المثال، يستخدم ElevatedButton اللون الأساسي للخلفية وonPrimary للنص — بدون تكوين إضافي.

أداة Theme هي InheritedWidget تنشر الموضوع إلى أسفل شجرة الأدوات. يمكن لأي أداة الوصول إلى الموضوع الحالي عبر Theme.of(context)، ويمكنها أيضًا إنشاء موضوع جديد بناءً على الموضوع الحالي بقيم متجاوزة عبر Theme.copyWith أو ThemeData.copyWith.

الخلاصة: ThemeData هو المصدر الوحيد لأنماط التطبيق. ابدأ التطوير بتعريف ThemeData — فهذا يحدد اللغة المرئية للمشروع بأكمله ويمنع الأنماط المتناثرة عبر الشاشات المختلفة.

ColorScheme: أساس لوحة الألوان

ColorScheme هو العنصر المركزي للتنسيق في Material 3. يحل محل primarySwatch وprimaryColor القديمين، ويوفر مجموعة كاملة من الألوان لجميع الحالات والمكونات.

يحتوي ColorScheme على 12 لونًا رئيسيًا: primary، onPrimary، secondary، onSecondary، surface، onSurface، error، onError، background، onBackground، outline وshadow. لكل لون تُحسب تلقائيًا المتغيرات الفاتحة والداكنة، بالإضافة إلى ألوان الحالات hover وfocus وpressed وdragged. عند تحديد primary فقط، يقوم الإطار تلقائيًا بتوليد الألوان المتبقية عبر ColorScheme.fromSeed.

طريقة ColorScheme.fromSeed (Material 3) تأخذ لونًا أساسيًا seedColor وتُولد لوحة متناسقة كاملة بناءً على خوارزمية Material Design 3. هذا يلغي الحاجة إلى الاختيار اليدوي للألوان التكميلية والمتشابهة. يكفي تمرير لون علامة تجارية واحد رئيسي، ويقوم ColorScheme.fromSeed بإنشاء لوحة متماسكة لنظام Material بأكمله.

الخلاصة: في المشاريع الجديدة، استخدم ColorScheme.fromSeed لتوليد اللوحة. للتوافق مع Material 2 — استخدم primarySwatch. ColorScheme هو الحد الأدنى المطلوب لـ Material 3.

الطباعة وTextTheme: خطوط التطبيق

TextTheme هو جزء من ThemeData يحدد أنماط النص لجميع مستويات العناوين والعناوين الفرعية والنص الرئيسي والتسميات. يحدد Material Design 15 نمطًا نصيًا: headlineLarge، headlineMedium، headlineSmall، titleLarge، titleMedium وغيرها.

تُحدد الطباعة إما عبر GoogleFonts للتحميل التلقائي للخطوط، أو عبر Theme.of(context).textTheme المدمج مع كائنات TextStyle مخصصة. توفر GoogleFonts مئات الخطوط عبر حزمة google_fonts، التي تخزنها مؤقتًا محليًا بعد التحميل الأول. البديل هو تضمين الخطوط في أصول المشروع عبر pubspec.yaml.

للطباعة المتجاوبة، يدعم TextTheme MediaQuery.textScaleFactor — عند زيادة حجم خط النظام، يتم تغيير حجم جميع أنماط النص بشكل متناسب. بالإضافة إلى ذلك، يمكن تعيين أحجام خطوط مختلفة لعوامل الشكل المختلفة باستخدام isTest اعتمادًا على MediaQuery.size.width.

الخلاصة: حدد TextTheme في ThemeData واستخدم Theme.of(context).textTheme لجميع عناصر النص. هذا يضمن تناسق الخطوط ويبسط التغييرات العامة في الطباعة.

وراثة الموضوع وتجاوزه

Theme هو InheritedWidget ينشر الموضوع من الأب إلى الأبناء. يمكن لأي أداة تابعة قراءة الموضوع الحالي عبر Theme.of(context) وإنشاء نسختها الخاصة مع تغييرات عبر ThemeData.copyWith.

آلية الوراثة: إذا كانت الأداة مغلفة في Theme ببيانات جديدة، فإن جميع توابعها ترى الموضوع المتجاوز، بينما الأدوات خارج هذا الغلاف ترى الأصلي. يتيح هذا، على سبيل المثال، عزل الأنماط لشاشة معينة: رأس يستخدم موضوعًا مخصصًا مع AppBarTheme معدل، بينما باقي التطبيق يستخدم الموضوع القياسي.

يمكن أن يكون تداخل Theme غير محدود. يستخدم Flutter بحثًا هرميًا: عند استدعاء Theme.of(context)، يصعد الإطار لأعلى الشجرة إلى أول Theme ويعيد موضوعه. إذا لم يتم العثور على أي Theme — يتم طرح استثناء. لذلك، يغلف MaterialApp دائمًا التطبيق في Theme.

الخلاصة: وراثة Theme هي آلية مرنة لعزل الأنماط. استخدم تجاوز الموضوع لشاشات فردية دون تكرار التكوين العام.

الموضوع الداكن: الوضع المظلم للتطبيق

الموضوع الداكن هو الإمكانية المدمجة في Flutter للتبديل بين الموضوع الفاتح والداكن. يُخزن ThemeData في معلمات theme (فاتح) وdarkTheme (داكن) لـ MaterialApp. تتحكم معلمة themeMode في الوضع: ThemeMode.light (فاتح دائمًا)، ThemeMode.dark (داكن دائمًا) أو ThemeMode.system (يتبع إعدادات النظام).

عند تنفيذ الموضوع الداكن، لست بحاجة إلى إنشاء جميع الأنماط من جديد. يكفي تمرير ColorScheme.fromSeed(seedColor, brightness: Brightness.dark) إلى darkTheme. سيضبط الإطار الألوان تلقائيًا للخلفية الداكنة: يصبح السطح رماديًا داكنًا، ويصبح النص فاتحًا. تُورث الموضوعات الفرعية الأخرى (AppBarTheme، CardTheme، BottomNavigationBarTheme) من النسخة الفاتحة ما لم يتم تجاوزها صراحةً.

للانتقال السلس بين الموضوعات، يوفر Flutter رسومًا متحركة عبر AnimatedTheme. عند تغيير themeMode، تتحرك الألوان والأنماط، مما يُنشئ انتقالًا مرئيًا سلسًا. يستخدم AnimatedTheme Duration وCurve لتكوين سرعة الحركة.

الخلاصة: يُنفذ الموضوع الداكن عبر darkTheme وColorScheme.fromSeed مع brightness: Brightness.dark. استخدم ThemeMode.system لاتباع إعدادات النظام للمستخدم.

أمثلة كود مع Theme وThemeData

مثال 1 — تكوين الموضوع العام عبر MaterialApp باستخدام ColorScheme.fromSeed.

dart
final ThemeData appTheme = ThemeData(
  useMaterial3: true,
  colorScheme: ColorScheme.fromSeed(
    seedColor: Colors.indigo,
    brightness: Brightness.light,
  ),
  appBarTheme: const AppBarTheme(
    centerTitle: true,
    elevation: 0,
  ),
  cardTheme: const CardTheme(
    elevation: 2,
    margin: EdgeInsets.all(8),
  ),
);

return MaterialApp(
  theme: appTheme,
  themeMode: ThemeMode.system,
  home: const MyHomePage(),
);

يُنشئ هذا المثال ThemeData مع Material 3، نظام ألوان قائم على النيلي، وأنماط AppBar و Card مخصصة. يُفعّل useMaterial3 نظام الموضوعات الجديد لـ Material 3، ويُولد ColorScheme.fromSeed اللوحة الكاملة.

مثال 2 — الوصول إلى الموضوع وتجاوزه لجزء من التطبيق.

dart
Widget build(BuildContext context) {
  final theme = Theme.of(context);

  return Column(
    children: [
      Text(
        'Standard headline',
        style: theme.textTheme.headlineMedium,
      ),
      Theme(
        data: theme.copyWith(
          colorScheme: theme.colorScheme.copyWith(
            primary: Colors.orange,
          ),
        ),
        child: const SpecialSection(),
      ),
    ],
  );
}

يُظهر المثال الثاني استخدام Theme.of(context) لقراءة الأنماط وTheme() مع copyWith لتجاوز الموضوع في جزء من الشجرة دون تغيير التكوين العام. ستستلم SpecialSection داخل Theme اللون الأساسي البرتقالي، بينما يحتفظ باقي التطبيق بالنيلي الأصلي.

الخلاصة: يُحدد الموضوع العام في MaterialApp، ويُوصل إليه عبر Theme.of(context)، ويُتجاوز عبر Theme مع copyWith. لـ Material 3، استخدم ColorScheme.fromSeed.

الأخطاء الشائعة عند تكوين الموضوع

الخطأ 1: استخدام primarySwatch بدلاً من ColorScheme في Material 3. primarySwatch قديم وغير مدعوم في Material 3. عند استخدام useMaterial3: true مع primarySwatch، يصدر Flutter تحذيرًا. الحل: الانتقال إلى ColorScheme.fromSeed لتوليد اللوحة.

الخطأ 2: ترميز الألوان بشكل ثابت في الأدوات بدلاً من استخدام Theme.of(context). يكتب المطورون Colors.blue مباشرة في Container بدلاً من اللون الأساسي أو الثانوي من الموضوع. عند تغيير الموضوع، تظل هذه الألوان كما هي، مما يكسر التناسق البصري. الحل: استخدم دائمًا Theme.of(context).colorScheme.primary أو ألوانًا أخرى من الموضوع.

الخطأ 3: عدم وجود darkTheme. إذا كان التطبيق لا يُعرف darkTheme، عند تفعيل الموضوع الداكن على الجهاز، يحاول Flutter تكييف الموضوع الفاتح تلقائيًا، مما يعطي غالبًا نتيجة منخفضة الجودة. الحل: مرر دائمًا darkTheme إلى MaterialApp، حتى لو لم يكن الموضوع الداكن أولوية — فهذه تجربة مستخدم مهمة.

الخطأ 4: إنشاء موضوع جديد في كل build. إذا تم إنشاء ThemeData داخل طريقة build، فإن كل إعادة بناء للأداة تُنشئ كائن موضوع جديد، مما يكسر تحسين InheritedWidget. الحل: نقل ThemeData إلى حقل static أو مُنشئ const خارج build.

الخلاصة: التنسيق الصحيح يتطلب نهجًا مركزيًا. انقل ThemeData إلى ثوابت، واستخدم أنظمة الألوان من الموضوع، وحدد دائمًا كلا الموضوعين — الفاتح والداكن.

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

ما الفرق بين Theme وThemeData؟

ThemeData هي بيانات التكوين (الألوان، الخطوط، الأنماط). Theme هو InheritedWidget ينقل ThemeData إلى أسفل شجرة الأدوات. يُعيد Theme.of(context) ThemeData الذي ينشره أقرب Theme في السياق الحالي.

كيف يتم تبديل موضوع التطبيق ديناميكيًا؟

تحكم في themeMode في MaterialApp عبر حالة التطبيق. غيّر الحالة من ThemeMode.light إلى ThemeMode.dark، وسيعيد Flutter بناء الشجرة بأكملها بالموضوع الجديد. لرسوم متحركة سلسة، استخدم AnimatedTheme.

ما هو ColorScheme.fromSeed؟

ColorScheme.fromSeed هي طريقة من Material 3 تأخذ لونًا أساسيًا واحدًا (seedColor) وتُولد لوحة متناسقة كاملة: primary، secondary، tertiary، surface، error ومتغيراتها on. تتيح الحصول على نظام ألوان مترابط من لون علامة تجارية واحد.

كيفية استخدام الخطوط المخصصة مع Theme؟

عبر TextTheme داخل ThemeData: حدد TextStyle مع fontFamily لكل مستوى. لـ Google Fonts، استخدم حزمة google_fonts، مع تمرير fontFamily مباشرة إلى TextTheme. للخطوط المحلية — قم بتضمينها عبر pubspec.yaml في قسم fonts.

هل يمكن تجاوز الموضوع لشاشة واحدة؟

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

الملخص

  • ThemeData — كائن تكوين بلوحة ألوان وخطوط وأنماط لجميع مكونات Material
  • ColorScheme.fromSeed — الطريقة الرئيسية لتوليد لوحة ألوان في Material 3 من لون علامة تجارية واحد
  • Theme.of(context) — الوصول إلى الموضوع الحالي لقراءة الأنماط بدون تكرار الكود
  • Theme — InheritedWidget لوراثة وتجاوز الموضوع في شجرة فرعية من الأدوات
  • الموضوع الداكن — ThemeData منفصل مع brightness: Brightness.dark، يُتحكم فيه عبر themeMode
  • توصية: انقل ThemeData إلى static أو const، وحدد دائمًا theme وdarkTheme في MaterialApp

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

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

اقرأ أيضًا