شرح TextFormField في Flutter مع controller وvalidator ولوحة المفاتيح واتجاه النص، وتصحيح الخصائص القديمة وإغلاق الموارد عند انتهاء الصفحة.
الحقل ليس نموذج البيانات كله
TextFormField يجمع سلوك إدخال النص مع التكامل داخل Form. controller يتيح قراءة النص وتغييره، وdecoration يصف العرض، وvalidator يعيد رسالة أو null، بينما onSaved يجمع قيمة عند طلب حفظ النموذج. لا يعني أي منها حفظًا تلقائيًا إلى قاعدة بيانات أو إنشاء حساب. افصل حالة الحقل عن طبقة البيانات حتى لا يؤدي بناء الواجهة أو كل تغيير حرف إلى كتابة غير مقصودة على الخادم.
اختر مالك controller
إذا أنشأت TextEditingController في State فأغلقه في dispose. لا تنشئه داخل build وإلا قد تُفقد الكتابة والاختيار عند إعادة البناء. وإذا استلمت controller من الأب فالمالك الذي أنشأه هو المسؤول عن إغلاقه، وليس كل طفل يستخدمه. لا تجمع initialValue مع controller على افتراض أنهما مصدران مستقلان للنص؛ حدّد المصدر بوضوح. عند تغيير قيمة واردة من الخادم راجع هل يكتب المستخدم بالفعل قبل استبدال النص تلقائيًا.
خصائص التحقق الحديثة
استخدم autovalidateMode المناسب مثل onUserInteraction بدل نسخ autovalidate القديمة مع خصائص أخرى ناقصة الفواصل. validator يستقبل String?، ولذلك افحص الغياب دون فرض value! قبل التحقق. يجب أن تكون دالة التحقق سريعة ولا تنفذ طلب شبكة في كل بناء. إذا احتجت فحص توفر اسم مستخدم فافصله في تدفق غير متزامن يتعامل مع الطلبات القديمة، ثم أعد الفحص النهائي على الخادم عند الإرسال.
لوحة المفاتيح والمهيئات
keyboardType يطلب لوحة مفاتيح مناسبة لكنه لا يضمن أن المحتوى صالح؛ المستخدم قد يلصق نصًا أو يستخدم لوحة خارجية. FilteringTextInputFormatter.digitsOnly يقبل الأرقام اللاتينية في هذا الاستخدام، ولا يحول الأرقام العربية تلقائيًا. إذا كان التطبيق يقبل أكثر من نظام أرقام فحدد التطبيع صراحة واختبره، ولا تحول رقم هاتف أو رمز بريدي إلى int إذا كانت الأصفار الأولى جزءًا من المعنى. النصوص التعريفية تختلف عن القيم الحسابية.
اتجاه النص وتجربة الإدخال
يمكن تثبيت اتجاه بريد أو كود تقني إلى LTR عند ملاءمة المنتج، بينما يعتمد حقل اسم عربي على سياق الواجهة عادة. لا تقلب اتجاه الحقل مع كل حرف اعتمادًا على regex غير صحيح مثل [u0600-u06FF]؛ ذلك قد يحرك المؤشر بصورة مزعجة ولا يغطي النص المختلط. استخدم label واضحًا ورسالة خطأ مفهومة وTextInputAction.next أو done حسب ترتيب النموذج. وفر انتقال focus مقصودًا بدل إجبار المستخدم على إغلاق لوحة المفاتيح بين الحقول.
التحقق من المثال
المثال يقبل اسمًا غير فارغ ويعرض رسالة نجاح تحقق محلي فقط، ولا يطبع النص أو أي أسرار. جرّب نصًا فارغًا ومسافات واسمًا عربيًا طويلًا وتغيير حجم الخط وإعادة بناء الأب. تأكد أن النص يبقى وأن الموارد تنتهي عند مغادرة الصفحة. onSaved أو FormState.save لا يعنيان حفظًا دائمًا؛ إذا أضفتهما لاحقًا فليجمعا البيانات ثم مررها لخدمة واضحة. تحقق من الأخطاء والتحميل على الخادم ولا تستخدم نجاح validator دليلًا على إذن الوصول أو صحة الحساب.
مثال مستقل للتجربة
شغّل المثال داخل مشروع Flutter تجريبي. يعتمد على Material فقط ولا يحتاج assets أو أصنافًا من مشروع خاص. غيّر مساحة النافذة وجرّب تكبير النص أثناء الفحص، ثم انقل المكوّن إلى مشروعك بعد تحديد سياق قيوده الفعلي.
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: DemoPage()));
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
final _formKey = GlobalKey<FormState>();
final _name = TextEditingController();
@override
void dispose() { _name.dispose(); super.dispose(); }
void _submit() {
if (!(_formKey.currentState?.validate() ?? false)) return;
FocusScope.of(context).unfocus();
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Local validation passed')));
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('TextFormField')),
body: Form(key: _formKey, child: ListView(padding: const EdgeInsets.all(16), children: [
TextFormField(
controller: _name,
autovalidateMode: AutovalidateMode.onUserInteraction,
textInputAction: TextInputAction.done,
decoration: const InputDecoration(labelText: 'Name', border: OutlineInputBorder()),
validator: (value) => value == null || value.trim().isEmpty ? 'Enter your name' : null,
onFieldSubmitted: (_) => _submit(),
),
const SizedBox(height: 16),
ElevatedButton(onPressed: _submit, child: const Text('Validate')),
])),
);
}مرجع الواجهة البرمجية
راجع التوثيق الرسمي لمطابقة السلوك مع إصدار Flutter المستخدم. نجاح المثال في مساحة واحدة لا يثبت توافق التصميم مع جميع الشاشات؛ اختبر حدود الانتقال والاتجاه واللغة والنص الطويل.
الصور والفيديو من الشرح السابق
الكود النصي أعلاه هو النسخة المصححة؛ قد تختلف المواد المرئية السابقة في أسماء المتغيرات أو طريقة التشغيل.







