الأزرار والإدخال في Flutter: Buttons و TextField
التطبيق الذي لا يستطيع المستخدم التفاعل معه مجرد صورة. في هذا الدرس ستجعل تطبيقك يستمع للمستخدم: يضغط الأزرار، ويكتب في الحقول، ويختار من مربعات الاختيار. وفي النهاية ستبني نموذجاً يرحب بالمستخدم باسمه، ويعرض رسالة تنبيه إذا نسي كتابته.
أنواع الأزرار
Section titled “أنواع الأزرار”Flutter يوفر أزراراً جاهزة بتصميم Material، ولكل نوع مكانه:
ElevatedButton( onPressed: () { debugPrint('زر مرتفع'); }, child: const Text('تسجيل'),),OutlinedButton(onPressed: () {}, child: const Text('إلغاء')),TextButton(onPressed: () {}, child: const Text('نسيت كلمة المرور؟')),IconButton(onPressed: () {}, icon: const Icon(Icons.share)),ElevatedButton.icon( onPressed: () {}, icon: const Icon(Icons.send), label: const Text('إرسال'),),| الزر | متى تستخدمه |
|---|---|
ElevatedButton |
الإجراء الأهم في الشاشة، مثل «تسجيل» |
OutlinedButton |
إجراء ثانوي بجانب الأساسي، مثل «إلغاء» |
TextButton |
إجراء خفيف، مثل الروابط داخل النماذج |
IconButton |
أيقونة قابلة للضغط، مثل المشاركة أو الحذف |
.icon |
أي زر مع أيقونة ونص معاً |
تخيّل الأزرار مثل الأصوات في المحادثة: ElevatedButton صوت عالٍ واضح للأمر المهم، و TextButton همسة للأمر الجانبي. لا تجعل كل الأزرار تصرخ!
تعطيل الزر
Section titled “تعطيل الزر”إذا أعطيت onPressed القيمة null، يصبح الزر معطلاً: رمادياً لا يستجيب للضغط:
const ElevatedButton(onPressed: null, child: Text('معطل'))هذه الحيلة مفيدة جداً: عطّل زر «إرسال» حتى يملأ المستخدم البيانات المطلوبة، كما سترى في المشروع.
حقل الإدخال TextField
Section titled “حقل الإدخال TextField”TextField( controller: _nameController, decoration: const InputDecoration( labelText: 'الاسم', hintText: 'مثلاً: سارة', prefixIcon: Icon(Icons.person), border: OutlineInputBorder(), ),)labelText: اسم الحقل، ويصعد للأعلى بحركة جميلة عندما يبدأ المستخدم الكتابة.hintText: مثال رمادي يظهر داخل الحقل الفارغ.prefixIcon: أيقونة في بداية الحقل.border: OutlineInputBorder(): إطار حول الحقل بدلاً من الخط السفلي الافتراضي.
وهناك خصائص مفيدة أخرى: obscureText: true لإخفاء كلمة المرور بنقاط، و keyboardType: TextInputType.emailAddress لإظهار لوحة مفاتيح فيها @، و TextInputType.number للوحة الأرقام.
قراءة ما كتبه المستخدم: TextEditingController
Section titled “قراءة ما كتبه المستخدم: TextEditingController”كيف تعرف ما كُتب في الحقل؟ بـ متحكم (Controller) تربطه بالحقل:
final _nameController = TextEditingController();
@overridevoid dispose() { _nameController.dispose(); super.dispose();}_nameController.text: النص المكتوب حالياً._nameController.clear(): يمسح الحقل.dispose: دالة يستدعيها Flutter عندما تُغلق الشاشة نهائياً، وفيها نحرر المتحكم من الذاكرة. نسيانها يسبب تسرباً في الذاكرة (Memory Leak) مع الوقت.
تخيّل المتحكم مثل جهاز تحكم عن بعد للحقل: تقرأ به ما على الشاشة، وتمسحه، وتغيّره. وعندما تغادر الغرفة نهائياً، تطفئ الجهاز.
المشروع: نموذج ترحيب
Section titled “المشروع: نموذج ترحيب”class GreetingForm extends StatefulWidget { const GreetingForm({super.key});
@override State<GreetingForm> createState() => _GreetingFormState();}
class _GreetingFormState extends State<GreetingForm> { final _nameController = TextEditingController(); String _greeting = ''; bool _agreed = false;
@override void dispose() { _nameController.dispose(); super.dispose(); }
void _submit() { final name = _nameController.text.trim(); if (name.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('اكتب اسمك أولاً')), ); return; } setState(() { _greeting = 'أهلاً يا $name!'; }); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('نموذج ترحيب')), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ TextField( controller: _nameController, decoration: const InputDecoration( labelText: 'الاسم', hintText: 'مثلاً: سارة', prefixIcon: Icon(Icons.person), border: OutlineInputBorder(), ), ), CheckboxListTile( title: const Text('أوافق على الشروط'), value: _agreed, onChanged: (value) { setState(() { _agreed = value ?? false; }); }, ), ElevatedButton( onPressed: _agreed ? _submit : null, child: const Text('رحّب بي'), ), const SizedBox(height: 16), Text(_greeting, style: const TextStyle(fontSize: 22)), ], ), ), ); }}كيف يعمل خطوة بخطوة:
- في البداية زر «رحّب بي» معطل، لأن
_agreedقيمتهfalseفيصبحonPressedيساويnull. - عند تفعيل مربع «أوافق على الشروط»، تتغير
_agreedبـsetState، فيُعاد البناء ويصبح الزر فعالاً. - إذا ضغط المستخدم الزر والحقل فارغ، تظهر في أسفل الشاشة رسالة مؤقتة: «اكتب اسمك أولاً». هذه الرسالة اسمها SnackBar.
- إذا كتب « عمر » (بمسافات زائدة) وضغط، تحذف
trim()المسافات، ويظهر تحت الزر: «أهلاً يا عمر!».
لاحظ value ?? false في مربع الاختيار: القيمة الواصلة من نوع bool?، فنتعامل مع احتمال null كما تعلمت في درس Null Safety.
جرّب بنفسك
Section titled “جرّب بنفسك”- شغّل نموذج الترحيب وجرّب السيناريوهات الأربعة السابقة.
- أضف حقلاً ثانياً للعمر بـ
keyboardType: TextInputType.number، واطبع «أهلاً يا عمر، عمرك 15 سنة». - أضف
OutlinedButtonبجانب الزر يمسح الحقلين والتحية. - تحدٍّ: أضف حقل كلمة مرور بـ
obscureText: true، معIconButtonفيsuffixIconيُظهرها ويخفيها.
أخطاء شائعة
Section titled “أخطاء شائعة”- نسيان dispose للمتحكم: تسرب في الذاكرة يبطئ التطبيق مع الوقت.
- قراءة النص بدون trim: اسم مكون من مسافات فقط يمر وكأنه صحيح.
- كتابة onPressed: _submit(): بالأقواس تُنفَّذ الدالة فوراً أثناء البناء! الصحيح
onPressed: _submitبدون أقواس.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
كيف تجعل زراً معطلاً لا يستجيب للضغط؟
الأزرار في Flutter تصبح معطلة تلقائياً عندما تكون قيمة onPressed تساوي null.
سؤال
ما الطريقة الصحيحة لقراءة النص المكتوب في TextField؟
المتحكم المربوط بالحقل يعطيك النص الحالي عبر text، ويسمح بمسحه وتغييره.
الخلاصة
Section titled “الخلاصة”ElevatedButtonللإجراء الأهم، وOutlinedButtonوTextButtonللثانوي، وIconButtonللأيقونات.onPressed: nullيعطّل الزر، وهي طريقة ممتازة لمنع الإرسال قبل اكتمال البيانات.TextFieldمعInputDecorationللشكل، وTextEditingControllerلقراءة النص، وdisposeلتحريره.CheckboxListTileللاختيار، وSnackBarللرسائل المؤقتة.- ابنِ التفاعل بنمط واحد: حدث، ثم
setState، ثم إعادة بناء الشاشة.