مقدمة:
في هذا الدرس، سنتعلم كيفية استخدام وتخصيص TextField في تطبيقات Flutter. TextField هو عبارة عن واجهة لإدخال النص تتيح للمستخدمين إدخال بياناتهم بسهولة، مما يجعلها أداة أساسية في تطبيقات الهواتف الذكية وتطبيقات الويب. خلال هذا الدرس، سنتناول الخطوات الأساسية لإنشاء حقول النص باستخدام TextField، بالإضافة إلى كيفية تخصيص المظهر والسلوك باستخدام خيارات التنسيق والتحكم في التغييرات. كما سنتعرف أيضًا على كيفية التعامل مع الحدث onChanged لمتابعة تغييرات المستخدم في المدخلات. هذا الدرس سيمنحك الأساس اللازم لإضافة وتخصيص حقول النص في تطبيقات Flutter الخاصة بك بشكل فعال وجذاب.
TextField in flutter
في Flutter، TextField هو عبارة عن عنصر واجهة مستخدم يُستخدم لإدخال النص من قبل المستخدم. يُمكن استخدامه لتمكين المستخدمين من إدخال النص، سواء كان ذلك لإدخال البيانات في نموذج أو لأغراض أخرى مثل البحث أو التعليقات.
تعريف TextField في Flutter يبدو كما يلي:
TextField({ Key? key, TextEditingController? controller, FocusNode? focusNode, InputDecoration? decoration, TextInputType? keyboardType, TextInputAction? textInputAction, TextStyle? style, TextAlign textAlign = TextAlign.start, bool autofocus = false, bool readOnly = false, ToolbarOptions? toolbarOptions, bool? showCursor, String obscuringCharacter = '•', bool obscureText = false, bool autocorrect = true, SmartDashesType? smartDashesType, SmartQuotesType? smartQuotesType, bool enableSuggestions = true, int? maxLines = 1, int? minLines, bool expands = false, int? maxLength, ValueChanged<String>? onChanged, VoidCallback? onEditingComplete, ValueChanged<String>? onSubmitted, List<TextInputFormatter>? inputFormatters, bool? enabled, double cursorWidth = 2.0, double? cursorHeight, Radius? cursorRadius, Color? cursorColor, Brightness? keyboardAppearance, EdgeInsets scrollPadding = const EdgeInsets.all(20.0), bool enableInteractiveSelection = true, TextSelectionControls? selectionControls, DragStartBehavior dragStartBehavior = DragStartBehavior.start, GestureTapCallback? onTap, MouseCursor? mouseCursor, InputCounterWidgetBuilder? buildCounter, ScrollPhysics? scrollPhysics, ScrollController? scrollController, Iterable<String>? autofillHints, String? restorationId, })
الخصائص الهامة التي يمكن تخصيصها عند استخدام TextField:
decoration: تُستخدم لتخصيص مظهر حاوية النص مثل لون الخلفية وحدودها وغيرها.
keyboardType: يُحدد نوع لوحة المفاتيح المُظهرة عندما يتم تنشيط TextField.
controller: يُستخدم للتحكم في النص الذي تم إدخاله أو عرضه في TextField.
onChanged: يُستخدم لتحديث الحالة أو القيمة عندما يتم تغيير النص داخل TextField.
onSubmitted: يُستخدم لتحديث الحالة أو القيمة عندما يقوم المستخدم بالضغط على زر التأكيد على لوحة المفاتيح.
obscureText: يُستخدم لإخفاء النص المدخل بحيث يُظهر بدلاً منه رمز معين، مثل نقطة سوداء.
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('TextField Example'), ), body: Center( child: TextField( decoration: InputDecoration( border: OutlineInputBorder(), labelText: 'Enter your text here', ), onChanged: (text) { print("The text now: $text"); }, ), ), ), ); } }
هذا المثال يعرض TextField بسيطًا في التطبيق، حيث يُمكن المستخدم من إدخال النص وطباعته على الإخراج القياسي.
شرح الكود :
سنشرح الكود سطرًا بسطر، مع توضيح كل سطر:
import 'package:flutter/material.dart';
في هذا السطر، يتم استيراد مكتبة flutter/material.dart التي تحتوي على مكونات وأدوات Flutter التي نحتاجها لبناء التطبيقات، بما في ذلك TextField وMaterialApp وغيرها.
void main() { runApp(MyApp()); }
في هذا السطر، يتم تعريف دالة main() التي تعمل كنقطة البداية للتطبيق. تقوم الدالة بتشغيل تطبيق Flutter عن طريق استدعاء دالة ()runApp وتمريرها لكائن من نوع MyApp.
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('TextField Example'), ), body: Center( child: TextField( decoration: InputDecoration( border: OutlineInputBorder(), labelText: 'Enter your text here', ), onChanged: (text) { print("The text now: $text"); }, ), ), ), ); } }
في هذا السطر، يتم تعريف فئة MyApp التي تمثل تطبيق Flutter. ترث الفئة من StatelessWidget لأن التطبيق لا يحتوي على حالة داخلية متغيرة. داخل الفئة، يتم تنفيذ الوظيفة ()build التي تعيد MaterialApp، والذي يحتوي على Scaffold كطفل، والذي يحتوي على شريط العنوان وجسم التطبيق. في هذا المثال، يتم عرض TextField في وسط الشاشة.
TextField( decoration: InputDecoration( border: OutlineInputBorder(), labelText: 'Enter your text here', ), onChanged: (text) { print("The text now: $text"); }, ),
في هذا السطر، يتم إنشاء TextField، الذي يسمح للمستخدم بإدخال النص. تمرر خصائص مختلفة مثل decoration التي تحدد مظهر TextField وonChanged الذي يتم استدعاؤه عندما يتغير النص داخل TextField.
تنفيذ الكود:
شجرة العناصر
بناءً على الشرح السابق، يمكننا تحديد العناصر الرئيسية في التطبيق وترتيبها في شجرة العناصر كما يلي:
MyApp └─ MaterialApp └─ Scaffold ├─ AppBar │ └─ Text └─ Center └─ TextField
MyApp: هو التطبيق الرئيسي الذي يبدأ تشغيله في الدالة ()main.
MaterialApp: يوفر إطارًا لتطبيق Flutter ويسمح بتخصيص مظهر التطبيق والمزيد.
Scaffold: يوفر تصميمًا للصفحة تتضمن شريط عنوان وجسم التطبيق.
AppBar: يعرض شريط العنوان الذي يحتوي على عنوان التطبيق.
Text: يعرض نص عنوان التطبيق.
Center: يُوسِّط عنصره الابن أفقيًا وعموديًا داخل جسم التطبيق.
TextField: عنصر إدخال النص الذي يسمح للمستخدم بإدخال النص.
هذه هي الشجرة البسيطة لعناصر التطبيق في تطبيق Flutter الذي تم شرحه.
طبعًا، هيا بنا نقوم بإنشاء تطبيق Flutter يشمل TextField وشرحًا موجزًا حول كيفية استخدامه. سأقوم بكتابة التطبيق وشرح الأجزاء المهمة:
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: TextFieldDemo(), ); } } class TextFieldDemo extends StatefulWidget { @override _TextFieldDemoState createState() => _TextFieldDemoState(); } class _TextFieldDemoState extends State<TextFieldDemo> { String _enteredText = ''; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('TextField Demo'), ), body: Padding( padding: EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Enter your text:', style: TextStyle(fontSize: 18.0), ), SizedBox(height: 10.0), TextField( onChanged: (text) { setState(() { _enteredText = text; }); }, decoration: InputDecoration( border: OutlineInputBorder(), hintText: 'Type something...', ), ), SizedBox(height: 20.0), Text( 'You entered:', style: TextStyle(fontSize: 18.0), ), SizedBox(height: 10.0), Text( _enteredText, style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold), ), ], ), ), ); } }
شرح التطبيق:
;import ‘package:flutter/material.dart: هنا يتم استيراد مكتبة Flutter التي تحتوي على واجهات المستخدم والأدوات اللازمة لبناء التطبيق.
void main() { runApp(MyApp()); }: تعتبر دالة دخول main() ونقطة البداية للتطبيق. يتم فيها تشغيل التطبيق عن طريق استدعاء دالة runApp() وتمريرها لكائن من نوع MyApp.
MyApp: فئة تطبيق Flutter الرئيسية التي تمثل التطبيق بأكمله.
MaterialApp: يعتبر مكونًا رئيسيًا لتطبيق Flutter ويوفر إطارًا لتحديد السمات العامة للتطبيق.
TextFieldDemo: هذه الفئة هي واجهة المستخدم الرئيسية التي تحتوي على TextField.
_TextFieldDemoState: يُمثل الحالة المتغيرة لـ TextFieldDemo ويُستخدم لتحديث الواجهة عند تغيير النص داخل TextField.
;”=String _enteredText : متغير يُستخدم لتخزين النص المدخل في TextField.
TextField: عنصر واجهة المستخدم الذي يسمح للمستخدم بإدخال النص. يُطبق استدعاء تابع onChanged لتحديث المتغير _enteredText عندما يتم تغيير النص داخل TextField.
Scaffold: يوفر تصميمًا للصفحة تتضمن شريط عنوان وجسم التطبيق.
AppBar: يُعرض شريط العنوان الذي يحتوي على عنوان التطبيق.
Column: يُستخدم لترتيب العناصر بشكل عمودي داخل الـ Scaffold.
Text: يُستخدم لعرض النص.
()setState : تُستدعى هذه الدالة لتحديث حالة الواجهة عندما يتم تغيير النص في TextField.
بهذا، يكون لدينا تطبيق Flutter يشمل TextField مع شرح موجز لكيفية استخدامه.
تشغيل التطبيق:
شجرة العناصر لهذا التطبيق
هاكم شجرة العناصر للتطبيق الذي تم إنشاؤه:
MyApp └─ MaterialApp └─ TextFieldDemo └─ Scaffold ├─ AppBar │ └─ Text └─ Padding └─ Column ├─ Text ├─ SizedBox ├─ TextField ├─ SizedBox ├─ Text └─ Text
MyApp: التطبيق الرئيسي.
MaterialApp: يحتوي على TextFieldDemo.
TextFieldDemo: واجهة المستخدم الرئيسية.
Scaffold: الهيكل الأساسي للتطبيق.
AppBar: شريط العنوان.
Text: عنوان التطبيق.
Padding: حاشية للمساحة بين العناصر والحواف الخارجية.
Column: ترتيب العناصر بشكل عمودي.
Text: عنوان “Enter your text”.
SizedBox: مساحة فارغة.
TextField: مربع النص.
SizedBox: مساحة فارغة.
Text: عنوان “You entered”.
Text: النص الذي تم إدخاله.
سأقوم بإنشاء تطبيق متقدم يشمل استخدامات متعددة لـ TextField في Flutter. سيتضمن التطبيق العديد من الميزات مثل تخصيص TextField، وإضافة صفحات جديدة، والتعامل مع مدخلات المستخدم بشكل متقدم. هيا بنا نبدأ:
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: TextFieldDemo(), ); } } class TextFieldDemo extends StatefulWidget { @override _TextFieldDemoState createState() => _TextFieldDemoState(); } class _TextFieldDemoState extends State<TextFieldDemo> { final TextEditingController _textController = TextEditingController(); String _enteredText = ''; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('TextField Demo'), ), body: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Enter your text:', style: TextStyle(fontSize: 18.0), ), SizedBox(height: 10.0), TextField( controller: _textController, decoration: InputDecoration( border: OutlineInputBorder(), hintText: 'Type something...', ), onChanged: (text) { setState(() { _enteredText = text; }); }, ), SizedBox(height: 20.0), Text( 'You entered:', style: TextStyle(fontSize: 18.0), ), SizedBox(height: 10.0), Text( _enteredText, style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold), ), SizedBox(height: 20.0), ElevatedButton( onPressed: () { _showAlertDialog(context); }, child: Text('Show Alert'), ), ], ), ), ); } void _showAlertDialog(BuildContext context) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text('Alert'), content: Text('You entered: $_enteredText'), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, child: Text('OK'), ), ], ); }, ); } @override void dispose() { _textController.dispose(); super.dispose(); } }
في هذا التطبيق، قمنا بتضمين الميزات التالية:
إنشاء TextField لإدخال النص وعرض النص المُدخل.
زر “Show Alert” لعرض AlertDialog يعرض النص المدخل في TextField.
استخدام TextEditingController للتحكم في النص المدخل.
إغلاق TextEditingController في دالة dispose() لتفادي تسرب الذاكرة.
يمكنك الآن تشغيل التطبيق واستكشاف استخدامات مختلفة لـ TextField وكيفية التعامل مع مدخلات المستخدم بشكل متقدم.
تشغيل التطبيق:
شجرة العناصر لهذا التطبيق:
هاكم شجرة العناصر لتطبيق الذي تم إنشاؤه:
MyApp └─ MaterialApp └─ TextFieldDemo └─ Scaffold ├─ AppBar │ └─ Text └─ Padding └─ Column ├─ Text ├─ SizedBox ├─ TextField ├─ SizedBox ├─ Text ├─ SizedBox ├─ ElevatedButton └─ AlertDialog ├─ AlertDialog ├─ Text └─ TextButton
سنقوم بإنشاء تطبيق بسيط لتسجيل الدخول باستخدام TextField في Flutter. سيقوم المستخدم بإدخال اسم المستخدم وكلمة المرور، وبعد الضغط على زر تسجيل الدخول، سنقوم بطباعة البيانات التي تم إدخالها في وحدة تحكم الطباعة. هيا بنا نبدأ:
import 'package:flutter/material.dart'; void main() { runApp(LoginApp()); } class LoginApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: LoginPage(), ); } } class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { final TextEditingController _nameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Login Page'), ), body: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ TextField( controller: _nameController, decoration: InputDecoration( labelText: 'name', ), ), SizedBox(height: 20.0), TextField( controller: _passwordController, decoration: InputDecoration( labelText: 'Password', ), obscureText: true, ), SizedBox(height: 20.0), ElevatedButton( onPressed: () { _login(); }, child: Text('Login'), ), ], ), ), ); } void _login() { String name = _nameController.text; String password = _passwordController.text; print('name: $name'); print('Password: $password'); } @override void dispose() { _nameController.dispose(); _passwordController.dispose(); super.dispose(); } }
في هذا التطبيق:
قمت بإنشاء فئة LoginApp التي تعرض LoginPage.
في LoginPage، أنشأت حقول لإدخال اسم المستخدم وكلمة المرور باستخدام TextField.
أضفت زر “Login” الذي يقوم بطباعة قيم اسم المستخدم وكلمة المرور في وحدة تحكم الطباعة.
استخدمت TextEditingController للتحكم في قيم الحقول.
يمكنك الآن تشغيل التطبيق وتجربة تسجيل الدخول باستخدام حقول النص الموجودة.
شرح الكود سطرا بسطر :
سأقوم بشرح الكود سطرا بسطر وسأقوم بتنظيم الشرح في عناوين منطمة:
import 'package:flutter/material.dart';
1. استيراد المكتبيات
يتم استيراد مكتبة flutter/material.dart التي تحتوي على مكونات وأدوات Flutter الأساسية.
void main() { runApp(LoginApp()); }
2. دالة الدخول الرئيسية
تعتبر دالة دخول ()main نقطة البداية للتطبيق.
تقوم بتشغيل تطبيق Flutter عن طريق استدعاء دالة ()runApp وتمريرها لكائن من نوع LoginApp.
class LoginApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: LoginPage(), ); } }
3. تعريف تطبيق الدخول
تعريف فئة LoginApp التي تمثل التطبيق الرئيسي لتسجيل الدخول.
تقوم بإرجاع مادة التطبيق MaterialApp وتعيين LoginPage كصفحة البداية.
class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); }
4. تعريف صفحة تسجيل الدخول
تعريف فئة LoginPage التي تمثل صفحة تسجيل الدخول.
ترث من StatefulWidget لأن الصفحة تحتوي على حالة قابلة للتغيير.
class _LoginPageState extends State<LoginPage> { final TextEditingController _nameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController();
5. تعريف حالة صفحة تسجيل الدخول
تعريف فئة _LoginPageState التي تمثل الحالة لصفحة تسجيل الدخول.
يتم فيها تعريف متحكمات النصوص _nameController و _passwordController للتحكم في حقول النص.
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Login Page'), ), body: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ TextField( controller: _nameController, decoration: InputDecoration( labelText: 'name', ), ), SizedBox(height: 20.0), TextField( controller: _passwordController, decoration: InputDecoration( labelText: 'Password', ), obscureText: true, ), SizedBox(height: 20.0), ElevatedButton( onPressed: () { _login(); }, child: Text('Login'), ), ], ), ), ); }
6. بناء واجهة صفحة تسجيل الدخول
تنشئ واجهة صفحة تسجيل الدخول في دالة build() باستخدام Scaffold.
يحتوي الـ Scaffold على شريط العنوان وجسم التطبيق.
يتضمن الجسم حقول النص لاسم المستخدم وكلمة المرور وزر تسجيل الدخول.
void _login() { String name = _nameController.text; String password = _passwordController.text; print('name: $name'); print('Password: $password'); }
7. دالة تسجيل الدخول
تقوم بإنشاء دالة ()_login التي يتم استدعاؤها عند النقر على زر تسجيل الدخول.
تقوم بقراءة قيم اسم المستخدم وكلمة المرور من المتحكمات وطباعتها في وحدة تحكم الطباعة.
@override void dispose() { _nameController.dispose(); _passwordController.dispose(); super.dispose(); }
8. التخلص من المتحكمات
يتم في دالة ()dispose التخلص من المتحكمات عندما لا تعد بحاجة إليها لتجنب تسرب الذاكرة.
بهذا، تم شرح الكود سطرا بسطر مع تقسيمه إلى عناوين منطمة. يمكنك فهم كل جزء من التطبيق بشكل أفضل الآن.
شجرة العناصر لهذا التطبيق:
هاكم شجرة العناصر لتطبيق تسجيل الدخول:
MyApp └─ MaterialApp └─ LoginPage └─ Scaffold ├─ AppBar │ └─ Text └─ Padding └─ Column ├─ TextField (name) ├─ SizedBox ├─ TextField (Password) ├─ SizedBox ├─ ElevatedButton (Login)
MyApp: التطبيق الرئيسي.
MaterialApp: يحتوي على LoginPage.
LoginPage: صفحة تسجيل الدخول.
Scaffold: الهيكل الأساسي للصفحة.
AppBar: شريط العنوان.
Text: عنوان الصفحة.
Padding: توسيع للحواف لتجنب التماس مع الحواف.
Column: ترتيب العناصر بشكل عمودي.
TextField (name): حقل إدخال لاسم المستخدم.
SizedBox: مساحة فارغة.
TextField (Password): حقل إدخال لكلمة المرور.
SizedBox: مساحة فارغة.
ElevatedButton (Login): زر لتسجيل الدخول.
سنقدم بعض الأسئلة كجزء من الاختبار حول درس TextField:
أ) عرض الصور.
ب) تمثيل قائمة من العناصر.
ج) إدخال النص من قبل المستخدم.
د) عرض الرسائل النصية.
أ) onTap.
ب) onChanged.
ج) onSubmit.
د) onLongPress.
أ) باستخدام حدث onTap.
ب) باستخدام خيارات التنسيق مثل decoration.
ج) بإضافة الصور إليه.
د) بإنشاء حقول نصية متعددة.
أ) للتحكم في مظهر TextField.
ب) لتتبع تغييرات المستخدم في TextField.
ج) لإدراج صور داخل TextField.
د) لتحديد تصرفات معينة عند النقر على TextField.
أ) hintText.
ب) labelText.
ج) helperText.
د) placeholderText.
أ) border.
ب) outline.
ج) frame.
د) shadow.
أ) obscureText.
ب) passwordText.
ج) secureText.
د) hiddenText.
أ) setDecoration().
ب) setStyle().
ج) setInputType().
د) setFormat().
أ) عن طريق تعيين decoration.
ب) باستخدام onTap.
ج) عن طريق تحديد obscureText.
د) بإضافة prefix أو suffix.
أ) dispose().
ب) removeController().
ج) clearController().
د) cleanUp().
الاجابات
إليك الإجابات الصحيحة للأسئلة:
ج) إدخال النص من قبل المستخدم.
ب) onChanged.
ب) باستخدام خيارات التنسيق مثل decoration.
ب) لتتبع تغييرات المستخدم في TextField.
ج) helperText.
أ) border.
أ) obscureText.
أ) setDecoration().
ب) باستخدام onTap.
أ) dispose().