StatelessWidget بالتفصيل
ما هو StatelessWidget؟
في Flutter، كل ما تراه على الشاشة هو ودجت (widget). الـ StatelessWidget هو ودجت لا يتغير بعد بنائه. ليس لديه حالة قابلة للتغيير — مما يعني أن مظهره وسلوكه يتحددان بالكامل من خلال الإعدادات الممررة إليه عبر المُنشئ. بمجرد أن يستدعي Flutter دالة build()، يُعرض الودجت ويبقى كما هو حتى يعيد الودجت الأب بناءه بمعاملات جديدة.
فكّر في StatelessWidget على أنه دالة نقية لمدخلاته. إذا أُعطيت نفس وسائط المُنشئ، فإنه ينتج دائماً نفس مخرجات واجهة المستخدم. هذا يجعله قابلاً للتنبؤ وسهل الاختبار وعالي الكفاءة.
دالة build()
كل StatelessWidget يجب أن يُعيد تعريف دالة build(). تستقبل هذه الدالة BuildContext وتُرجع شجرة ودجت تصف ما يجب أن يظهر على الشاشة.
StatelessWidget أساسي
import 'package:flutter/material.dart';
class WelcomeMessage extends StatelessWidget {
const WelcomeMessage({super.key});
@override
Widget build(BuildContext context) {
return const Text(
'Welcome to Flutter!',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
);
}
}
معامل BuildContext يمنح الودجت الوصول إلى موقعه في شجرة الودجت. يمكنك استخدامه للبحث عن الودجت الموروثة والسمات واستعلامات الوسائط والمزيد. سنستكشف هذا في دروس لاحقة.
عدم القابلية للتغيير — لماذا هو مهم
جميع الحقول في StatelessWidget يجب أن تكون final. يتم فرض هذا بواسطة محلل Dart. نظراً لأن الودجت غير قابل للتغيير، يمكن لـ Flutter تخزينه مؤقتاً وإعادة استخدامه بأمان مما يُحسّن الأداء.
حقول غير قابلة للتغيير
class UserBadge extends StatelessWidget {
final String username;
final int level;
const UserBadge({
super.key,
required this.username,
required this.level,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.person, size: 20),
const SizedBox(width: 8),
Text('\$username (Lv. \$level)'),
],
);
}
}
متى تستخدم StatelessWidget
استخدم StatelessWidget عندما يكون الودجت الخاص بك:
- يعرض محتوى ثابتاً يعتمد فقط على معاملات المُنشئ
- لا يحتاج لتتبع تفاعلات المستخدم داخلياً
- لا يحتاج لدوال دورة الحياة مثل
initStateأوdispose - لا يستدعي
setState
أمثلة شائعة من العالم الحقيقي تشمل: التسميات والأيقونات والبطاقات الثابتة وعناصر القوائم للعرض فقط والفواصل والعناصر الزخرفية وأغلفة التخطيط.
معاملات المُنشئ والمعاملات المُسماة
تستخدم ودجت Flutter المعاملات المُسماة في Dart بشكل مكثف. الكلمة المفتاحية required تضمن أن المستدعي يوفر القيم الأساسية، بينما المعاملات الاختيارية يمكن أن تحتوي على قيم افتراضية.
معاملات مطلوبة واختيارية
class ProductCard extends StatelessWidget {
final String name;
final double price;
final String? imageUrl;
final bool showBorder;
const ProductCard({
super.key,
required this.name,
required this.price,
this.imageUrl,
this.showBorder = true,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: showBorder
? Border.all(color: Colors.grey.shade300)
: null,
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (imageUrl != null)
Image.network(imageUrl!, height: 120, fit: BoxFit.cover),
const SizedBox(height: 8),
Text(name, style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w600,
)),
Text('\\$\${price.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 16, color: Colors.green.shade700,
),
),
],
),
);
}
}
const عندما يكون ذلك ممكناً. هذا يسمح لـ Flutter بتحسين إعادة بناء الودجت عن طريق إعادة استخدام نسخ الودجت المتطابقة. نمط super.key (الذي قُدم في Dart 2.17) هو الطريقة الحديثة لتمرير معامل المفتاح.مُنشئات const والأداء
عندما تكون جميع الحقول في الودجت ثوابت وقت الترجمة، يمكنك إعلان المُنشئ كـ const. هذا يخبر Dart بإنشاء الودجت في وقت الترجمة بدلاً من وقت التشغيل، مما يوفر فائدتين رئيسيتين:
- كفاءة الذاكرة: ودجت const المتطابقة تتشارك نفس النسخة في الذاكرة
- تحسين إعادة البناء: يتخطى Flutter إعادة بناء ودجت const لأنه يعلم أنها لم تتغير
استخدام مُنشئات const
class AppLogo extends StatelessWidget {
const AppLogo({super.key});
@override
Widget build(BuildContext context) {
return const Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.flutter_dash, size: 64, color: Colors.blue),
SizedBox(height: 8),
Text(
'My Flutter App',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
],
);
}
}
// الاستخدام - كلاهما نسختان متطابقتان في الذاكرة
const logo1 = AppLogo();
const logo2 = AppLogo(); // نفس نسخة logo1
تركيب الودجت
يشجع Flutter على بناء واجهات مستخدم معقدة عن طريق تركيب ودجت صغيرة ومُركّزة معاً بدلاً من إنشاء ودجت واحد كبير. هذا يُسمى تركيب الودجت، وهو أحد أقوى أنماط Flutter.
تركيب الودجت معاً
class InfoRow extends StatelessWidget {
final IconData icon;
final String label;
final String value;
const InfoRow({
super.key,
required this.icon,
required this.label,
required this.value,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Icon(icon, size: 20, color: Colors.blue),
const SizedBox(width: 12),
Text(label, style: const TextStyle(
fontWeight: FontWeight.w500, color: Colors.grey,
)),
const Spacer(),
Text(value, style: const TextStyle(
fontWeight: FontWeight.w600,
)),
],
),
);
}
}
class UserProfile extends StatelessWidget {
final String name;
final String email;
final String phone;
final String location;
const UserProfile({
super.key,
required this.name,
required this.email,
required this.phone,
required this.location,
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: const TextStyle(
fontSize: 22, fontWeight: FontWeight.bold,
)),
const Divider(height: 24),
InfoRow(icon: Icons.email, label: 'Email', value: email),
InfoRow(icon: Icons.phone, label: 'Phone', value: phone),
InfoRow(icon: Icons.location_on, label: 'Location', value: location),
],
),
),
);
}
}
build() أطول من ~50 سطراً، فكر في استخراج أجزاء منها إلى ودجت منفصلة. هذا يُحسّن القراءة وإعادة الاستخدام والأداء (يمكن لـ Flutter تخطي إعادة بناء الودجت الفرعية التي لم تتغير).مثال عملي: ودجت بطاقة التحية
لنبني ودجت بطاقة تحية كاملة وقابلة لإعادة الاستخدام توضح جميع المفاهيم التي تعلمناها.
ودجت بطاقة التحية
class GreetingCard extends StatelessWidget {
final String recipientName;
final String message;
final Color backgroundColor;
final bool showIcon;
const GreetingCard({
super.key,
required this.recipientName,
required this.message,
this.backgroundColor = Colors.white,
this.showIcon = true,
});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (showIcon)
const Icon(Icons.celebration, size: 40, color: Colors.amber),
if (showIcon)
const SizedBox(height: 12),
Text(
'Hello, \$recipientName!',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
message,
style: TextStyle(
fontSize: 16,
color: Colors.grey.shade700,
height: 1.5,
),
),
],
),
);
}
}
// الاستخدام
GreetingCard(
recipientName: 'أحمد',
message: 'نتمنى لك يوماً رائعاً!',
backgroundColor: Colors.blue.shade50,
)
مثال عملي: ودجت تسمية قابلة لإعادة الاستخدام
التسميات والعلامات هي عناصر واجهة مستخدم شائعة. إليك ودجت تسمية مرنة وقابلة لإعادة الاستخدام:
ودجت تسمية قابلة لإعادة الاستخدام
class StatusLabel extends StatelessWidget {
final String text;
final Color color;
final IconData? icon;
final double fontSize;
const StatusLabel({
super.key,
required this.text,
this.color = Colors.blue,
this.icon,
this.fontSize = 12,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: color.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: color.withOpacity(0.3)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null) ...[
Icon(icon, size: fontSize + 2, color: color),
const SizedBox(width: 4),
],
Text(
text,
style: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.w600,
color: color,
),
),
],
),
);
}
}
// أمثلة على الاستخدام
const StatusLabel(text: 'نشط', color: Colors.green)
const StatusLabel(text: 'قيد الانتظار', color: Colors.orange, icon: Icons.schedule)
const StatusLabel(text: 'خطأ', color: Colors.red, icon: Icons.error_outline)
مثال عملي: ودجت عرض المعلومات
نمط شائع في التطبيقات هو عرض معلومات المفتاح-القيمة في تخطيط منظم:
ودجت عرض المعلومات
class InfoDisplay extends StatelessWidget {
final String title;
final List<MapEntry<String, String>> items;
final IconData? headerIcon;
const InfoDisplay({
super.key,
required this.title,
required this.items,
this.headerIcon,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
if (headerIcon != null) ...[
Icon(headerIcon, color: Colors.blue),
const SizedBox(width: 8),
],
Text(title, style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold,
)),
],
),
const Divider(height: 24),
...items.map((entry) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(entry.key, style: TextStyle(
color: Colors.grey.shade600,
)),
Text(entry.value, style: const TextStyle(
fontWeight: FontWeight.w500,
)),
],
),
)),
],
),
);
}
}
// الاستخدام
InfoDisplay(
title: 'ملخص الطلب',
headerIcon: Icons.receipt_long,
items: [
const MapEntry('المجموع الفرعي', '\\$45.00'),
const MapEntry('الضريبة', '\\$3.60'),
const MapEntry('الإجمالي', '\\$48.60'),
],
)
الملخص
في هذا الدرس، تعلمت:
- StatelessWidget هو ودجت غير قابل للتغيير ومخرجاته تعتمد فقط على معاملات المُنشئ
- دالة build() تُرجع شجرة ودجت وتستقبل
BuildContext - جميع الحقول يجب أن تكون final لفرض عدم القابلية للتغيير
- استخدم مُنشئات const لأداء وكفاءة ذاكرة أفضل
- تركيب الودجت هو الطريقة المفضلة لبناء واجهات مستخدم معقدة من أجزاء بسيطة
- استخدم المعاملات المُسماة المطلوبة والاختيارية لواجهات ودجت مرنة
StatefulWidget ودوال دورة حياته التي تسمح للودجت بإدارة الحالة الداخلية والاستجابة للتغييرات بمرور الوقت.