تنسيق النصوص في Flutter: TextStyle و Text.rich
تعرّفت على Text في درس الـ Widgets وكتبت بها نصوصاً بسيطة. لكن التطبيقات الحقيقية تحتاج أكثر: عنوان عريض، وسعر قديم مشطوب، وجملة فيها كلمة ملونة، ووصف طويل يُقص بثلاث نقاط. في هذا الدرس ستتعلم كل ما تحتاجه للتحكم في شكل النص.
خصائص TextStyle
Section titled “خصائص TextStyle”كل ما يخص شكل النص يوضع داخل TextStyle:
const Text( 'عرض خاص لفترة محدودة', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: Colors.indigo, letterSpacing: 0.5, height: 1.6, ),)| الخاصية | وظيفتها |
|---|---|
fontSize |
حجم الخط (الافتراضي 14) |
fontWeight |
سُمك الخط: FontWeight.bold أو درجات من w100 إلى w900 |
color |
لون النص |
fontStyle |
FontStyle.italic للخط المائل |
letterSpacing |
المسافة بين الحروف |
height |
ارتفاع السطر كمضاعف لحجم الخط (1.6 مريح للقراءة) |
decoration |
خط تحت النص أو خط يشطبه |
backgroundColor |
لون خلفية خلف الحروف فقط |
الخطوط والشطب: decoration
Section titled “الخطوط والشطب: decoration”خاصية decoration ترسم خطاً مع النص. أشهر استخدام لها هو السعر القديم المشطوب في المتاجر:
const Text( '250 جنيهاً', style: TextStyle( decoration: TextDecoration.lineThrough, decorationColor: Colors.red, decorationThickness: 2, color: Colors.grey, ),),const Text( 'اقرأ الشروط والأحكام', style: TextStyle(decoration: TextDecoration.underline),),TextDecoration.lineThrough: خط يشطب النص.TextDecoration.underline: خط تحت النص، مثل الروابط.TextDecoration.overline: خط فوق النص.decorationColorوdecorationThickness: لون الخط وسُمكه.
المحاذاة والنص الطويل
Section titled “المحاذاة والنص الطويل”ماذا يحدث إذا كان النص أطول من المساحة المتاحة؟ افتراضياً ينزل إلى سطر جديد. لكنك تستطيع التحكم في ذلك:
const Text( 'هذا وصف طويل جداً لمنتج في المتجر، ولن يظهر منه إلا سطران فقط، والباقي يُستبدل بثلاث نقاط.', maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,)maxLines: أقصى عدد من الأسطر.overflow: TextOverflow.ellipsis: يضع «…» مكان الجزء المقصوص. والبدائل:clipيقص بدون نقاط، وfadeيجعل النهاية تتلاشى.textAlign: المحاذاة. القيمstartوendتحترم اتجاه اللغة، فـTextAlign.startتعني اليمين في تطبيق عربي واليسار في تطبيق إنجليزي. لذلك فضّلها علىleftوright.
تخيّل maxLines مع ellipsis مثل عنوان خبر في الجريدة: المساحة محدودة، فيُكتب أول الخبر ثم «…» لتفهم أن له بقية.
تلوين جزء من الجملة: Text.rich
Section titled “تلوين جزء من الجملة: Text.rich”Text العادية لها نمط واحد لكل النص. فكيف تكتب «السعر: 150 جنيهاً» والرقم وحده بلون مختلف؟ الحل Text.rich مع TextSpan:
const Text.rich( TextSpan( text: 'السعر: ', children: [ TextSpan( text: '150', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.green, fontSize: 20, ), ), TextSpan(text: ' جنيهاً'), ], ),)كل TextSpan قطعة من الجملة لها نمطها الخاص، والقطع كلها تُعرض في سطر واحد متصل كأنها نص واحد. والقطعة التي لا تحدد نمطاً ترث نمط القطعة الأم.
أنماط الثيم الجاهزة
Section titled “أنماط الثيم الجاهزة”كتابة fontSize: 22 في كل مكان تجعل تطبيقك غير متناسق: عنوان بحجم 22 هنا و 24 هناك. الأفضل أن تستخدم الأنماط الجاهزة في ثيم التطبيق:
Text( 'عنوان الصفحة', style: Theme.of(context).textTheme.headlineSmall,),Text( 'نص عادي داخل الصفحة', style: Theme.of(context).textTheme.bodyLarge,),أهم الأنماط من الأكبر إلى الأصغر:
| المجموعة | الأنماط | الاستخدام |
|---|---|---|
headline |
headlineLarge و headlineMedium و headlineSmall |
عناوين الصفحات |
title |
titleLarge و titleMedium و titleSmall |
عناوين البطاقات والأقسام |
body |
bodyLarge و bodyMedium و bodySmall |
النص العادي |
label |
labelLarge و labelMedium و labelSmall |
نصوص الأزرار والملصقات |
وإذا أردت نمطاً جاهزاً مع تعديل صغير، استخدم copyWith:
Text( 'عنوان ملون', style: Theme.of(context).textTheme.titleLarge?.copyWith( color: Colors.indigo, ),)copyWith تنسخ النمط وتغيّر الخصائص التي تحددها فقط. وعلامة ?. موجودة لأن النمط قد يكون null، كما تعلمت في درس Null Safety.
خط مخصص للتطبيق
Section titled “خط مخصص للتطبيق”لاستخدام خط عربي جميل مثل Cairo أو Tajawal:
- نزّل ملفات الخط (بامتداد
.ttf) وضعها في مجلدassets/fonts/داخل المشروع. - عرّف الخط في
pubspec.yaml:
flutter: uses-material-design: true fonts: - family: Cairo fonts: - asset: assets/fonts/Cairo-Regular.ttf - asset: assets/fonts/Cairo-Bold.ttf weight: 700- استخدمه في نص واحد بـ
fontFamily: 'Cairo'داخلTextStyle، أو اجعله خط التطبيق كله من الثيم:
MaterialApp( theme: ThemeData(fontFamily: 'Cairo'), home: const ArticleCard(),)المشروع: بطاقة مقال
Section titled “المشروع: بطاقة مقال”لنجمع كل ما سبق في بطاقة تعرض مقالاً: تصنيف صغير، وعنوان عريض، ووصف يُقص بعد ثلاثة أسطر، وسطر معلومات فيه جزء ملون.
class ArticleCard extends StatelessWidget { const ArticleCard({super.key});
@override Widget build(BuildContext context) { final textTheme = Theme.of(context).textTheme;
return Scaffold( appBar: AppBar(title: const Text('بطاقة مقال')), body: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'برمجة', style: textTheme.labelLarge?.copyWith(color: Colors.indigo), ), const SizedBox(height: 8), Text( 'لماذا يحب المبرمجون Flutter؟', style: textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text( 'لأنه يسمح لك بكتابة الكود مرة واحدة وتشغيله على أندرويد و iOS والويب، ' 'ولأن التعديل يظهر أمامك في أقل من ثانية بفضل إعادة التحميل السريعة، ' 'ولأن مكتبته مليئة بعناصر جاهزة جميلة تختصر عليك أسابيع من العمل.', maxLines: 3, overflow: TextOverflow.ellipsis, style: textTheme.bodyLarge?.copyWith(height: 1.7), ), const SizedBox(height: 12), const Text.rich( TextSpan( text: 'وقت القراءة: ', children: [ TextSpan( text: '4 دقائق', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.teal, ), ), ], ), ), ], ), ), ); }}لاحظ في نص الوصف أننا كتبنا ثلاثة نصوص متتالية بدون أي علامة بينها. Dart تدمج النصوص المتجاورة تلقائياً في نص واحد، وهذه طريقة مريحة لتقسيم النص الطويل على عدة أسطر في الكود.
جرّب بنفسك
Section titled “جرّب بنفسك”- شغّل بطاقة المقال، ثم غيّر
maxLinesإلى 1 وشاهد أين تظهر النقاط الثلاث. - أضف تحت العنوان سعراً قديماً مشطوباً بجانبه سعر جديد أخضر عريض، باستخدام
Text.rich. - غيّر
overflowإلىTextOverflow.fadeولاحظ الفرق. - تحدٍّ: نزّل خطاً عربياً وعرّفه في
pubspec.yaml، واجعله خط التطبيق كله.
أخطاء شائعة
Section titled “أخطاء شائعة”- نسيان const أو وضعها في غير مكانها: النص الذي يستخدم
Theme.of(context)لا يمكن أن يكونconst، لأن قيمته تُعرف وقت التشغيل. إذا ظهر خطأ «Not a constant expression» فاحذفconstمن ذلك الـ Widget. - استخدام TextAlign.left في تطبيق عربي: النص سيلتصق باليسار دائماً. استخدم
TextAlign.startليتبع اتجاه اللغة. - انتظار ظهور النقاط الثلاث بدون maxLines:
overflowلا تعمل وحدها إذا كان النص حراً في النزول لأسطر جديدة. حدّدmaxLinesمعها.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
كيف تجعل النص الطويل يظهر في سطرين فقط وينتهي بثلاث نقاط؟
maxLines تحدد أقصى عدد من الأسطر، و TextOverflow.ellipsis تضع ثلاث نقاط مكان الجزء المقصوص.
سؤال
ما الوسيلة المناسبة لكتابة جملة واحدة فيها كلمة بلون مختلف؟
Text.rich تقبل شجرة من TextSpan، ولكل قطعة نمطها، وتُعرض كلها كنص واحد متصل.
الخلاصة
Section titled “الخلاصة”TextStyleتتحكم في الحجم والسُمك واللون وارتفاع السطر والشطب والتسطير.maxLinesمعTextOverflow.ellipsisلقص النص الطويل، وTextAlign.startللمحاذاة التي تحترم اتجاه اللغة.Text.richوTextSpanلتنسيق أجزاء مختلفة داخل نص واحد.- استخدم أنماط
Theme.of(context).textThemeليبقى تطبيقك متناسقاً، وعدّل عليها بـcopyWith. - الخط المخصص يُعرَّف في
pubspec.yamlثم يُفعَّل بـfontFamily.