مقدمة: عالم Widgets المتقدمة في Flutter
في رحلة تعلم Flutter المتقدم، يأتي إتقان الـ Widgets كخطوة حاسمة لبناء تطبيقات احترافية ومتميزة. بعد فهم الأساسيات، حان الوقت للغوص عميقاً في عالم الـ Widgets المتقدمة التي تمكنك من إنشاء واجهات مستخدم معقدة وتفاعلية تنافس أفضل التطبيقات العالمية.
في هذا الدليل المتخصص، سنستكشف أعماق Flutter Widgets المتقدمة بطريقة عملية ومفصلة. سنتعلم كيفية بناء مكونات قابلة للإعادة الاستخدام، وإدارة الحالة بكفاءة، وإنشاء رسوم متحركة سلسة، وتحسين الأداء للحصول على تجربة مستخدم استثنائية.
StatelessWidget vs StatefulWidget: الفهم العميق
إن فهم الفرق بين StatelessWidget وStatefulWidget بعمق هو أساس بناء تطبيقات Flutter فعالة. هذا الفهم يتجاوز التعريفات البسيطة ليشمل متى وكيف نستخدم كل نوع لتحقيق أفضل أداء وتنظيم للكود.
StatelessWidget المتقدم:
import 'package:flutter/material.dart';
class AdvancedStatelessWidget extends StatelessWidget {
final String title;
final String subtitle;
final IconData icon;
final Color primaryColor;
final VoidCallback? onTap;
const AdvancedStatelessWidget({
Key? key,
required this.title,
required this.subtitle,
required this.icon,
this.primaryColor = Colors.blue,
this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
elevation: 8,
margin: EdgeInsets.all(12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: LinearGradient(
colors: [
primaryColor.withOpacity(0.1),
primaryColor.withOpacity(0.05),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Row(
children: [
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: primaryColor,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
icon,
color: Colors.white,
size: 24,
),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: primaryColor,
size: 16,
),
],
),
),
),
);
}
}
StatefulWidget المتقدم مع إدارة حالة معقدة:
import 'package:flutter/material.dart';
class AdvancedCounter extends StatefulWidget {
final int initialValue;
final int minValue;
final int maxValue;
final Function(int)? onValueChanged;
const AdvancedCounter({
Key? key,
this.initialValue = 0,
this.minValue = 0,
this.maxValue = 100,
this.onValueChanged,
}) : super(key: key);
@override
_AdvancedCounterState createState() => _AdvancedCounterState();
}
class _AdvancedCounterState extends State<AdvancedCounter>
with TickerProviderStateMixin {
late int _currentValue;
late AnimationController _animationController;
late Animation<double> _scaleAnimation;
late Animation<Color?> _colorAnimation;
@override
void initState() {
super.initState();
_currentValue = widget.initialValue;
_animationController = AnimationController(
duration: Duration(milliseconds: 200),
vsync: this,
);
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 1.2,
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.elasticOut,
));
_colorAnimation = ColorTween(
begin: Colors.blue,
end: Colors.green,
).animate(_animationController);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _increment() {
if (_currentValue < widget.maxValue) {
setState(() {
_currentValue++;
});
_triggerAnimation();
widget.onValueChanged?.call(_currentValue);
}
}
void _decrement() {
if (_currentValue > widget.minValue) {
setState(() {
_currentValue--;
});
_triggerAnimation();
widget.onValueChanged?.call(_currentValue);
}
}
void _triggerAnimation() {
_animationController.forward().then((_) {
_animationController.reverse();
});
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: Offset(0, 5),
),
],
gradient: LinearGradient(
colors: [Colors.white, Colors.grey[50]!],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'العداد المتقدم',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(height: 20),
AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: _colorAnimation.value,
shape: BoxShape.circle,
),
child: Center(
child: Text(
'$_currentValue',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
);
},
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildControlButton(
icon: Icons.remove,
onPressed: _currentValue > widget.minValue ? _decrement : null,
color: Colors.red,
),
_buildControlButton(
icon: Icons.add,
onPressed: _currentValue < widget.maxValue ? _increment : null,
color: Colors.green,
),
],
),
SizedBox(height: 10),
Text(
'النطاق: ${widget.minValue} - ${widget.maxValue}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
);
}
Widget _buildControlButton({
required IconData icon,
required VoidCallback? onPressed,
required Color color,
}) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: color,
shape: CircleBorder(),
padding: EdgeInsets.all(16),
elevation: 5,
),
child: Icon(
icon,
color: Colors.white,
size: 24,
),
);
}
}
دورة حياة الـ Widgets: الفهم العميق والتطبيق العملي
فهم دورة حياة الـ Widgets بعمق أمر بالغ الأهمية لبناء تطبيقات Flutter محسنة الأداء. كل مرحلة في دورة الحياة لها غرض محدد ويمكن استغلالها لتحسين تجربة المستخدم وأداء التطبيق.
دورة حياة StatefulWidget الكاملة:
import 'package:flutter/material.dart';
class LifecycleDemo extends StatefulWidget {
final String title;
const LifecycleDemo({Key? key, required this.title}) : super(key: key);
@override
_LifecycleDemoState createState() {
print('🏗️ createState() - إنشاء الحالة');
return _LifecycleDemoState();
}
}
class _LifecycleDemoState extends State<LifecycleDemo> {
int _counter = 0;
late String _displayTitle;
@override
void initState() {
super.initState();
print('🚀 initState() - تهيئة الحالة');
_displayTitle = widget.title;
// تهيئة البيانات، الاشتراك في الأحداث، إعداد المؤقتات
_initializeData();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('🔄 didChangeDependencies() - تغيير التبعيات');
// يتم استدعاؤها عند تغيير التبعيات مثل Theme أو MediaQuery
_updateBasedOnDependencies();
}
@override
void didUpdateWidget(LifecycleDemo oldWidget) {
super.didUpdateWidget(oldWidget);
print('🔄 didUpdateWidget() - تحديث الويدجت');
// مقارنة الخصائص الجديدة مع القديمة
if (oldWidget.title != widget.title) {
setState(() {
_displayTitle = widget.title;
});
}
}
@override
Widget build(BuildContext context) {
print('🎨 build() - بناء الواجهة');
return Scaffold(
appBar: AppBar(
title: Text(_displayTitle),
backgroundColor: Colors.purple,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.purple.withOpacity(0.1),
borderRadius: BorderRadius.circular(15),
border: Border.all(color: Colors.purple, width: 2),
),
child: Column(
children: [
Text(
'دورة حياة الويدجت',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.purple,
),
),
SizedBox(height: 10),
Text(
'العداد: $_counter',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
),
SizedBox(height: 30),
ElevatedButton(
onPressed: _incrementCounter,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple,
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
),
child: Text(
'زيادة العداد',
style: TextStyle(fontSize: 16, color: Colors.white),
),
),
SizedBox(height: 20),
Text(
'تحقق من وحدة التحكم لرؤية دورة الحياة',
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
fontStyle: FontStyle.italic,
),
),
],
),
),
);
}
@override
void deactivate() {
print('⏸️ deactivate() - إلغاء التفعيل');
super.deactivate();
}
@override
void dispose() {
print('🗑️ dispose() - تنظيف الموارد');
// تنظيف الموارد: إلغاء الاشتراكات، إيقاف المؤقتات، تحرير الذاكرة
_cleanupResources();
super.dispose();
}
// دوال مساعدة
void _initializeData() {
// تهيئة البيانات الأولية
print('📊 تهيئة البيانات...');
}
void _updateBasedOnDependencies() {
// تحديث البيانات بناءً على التبعيات
print('🔗 تحديث التبعيات...');
}
void _incrementCounter() {
setState(() {
_counter++;
print('➕ تحديث العداد إلى: $_counter');
});
}
void _cleanupResources() {
// تنظيف الموارد
print('🧹 تنظيف الموارد...');
}
}
مراقبة دورة الحياة في الممارسة العملية:
class LifecycleMonitor extends StatefulWidget {
@override
_LifecycleMonitorState createState() => _LifecycleMonitorState();
}
class _LifecycleMonitorState extends State<LifecycleMonitor>
with WidgetsBindingObserver {
List<String> _lifecycleEvents = [];
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_addEvent('initState');
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
_addEvent('App State: ${state.toString()}');
switch (state) {
case AppLifecycleState.resumed:
_addEvent('التطبيق نشط');
break;
case AppLifecycleState.inactive:
_addEvent('التطبيق غير نشط');
break;
case AppLifecycleState.paused:
_addEvent('التطبيق متوقف مؤقتاً');
break;
case AppLifecycleState.detached:
_addEvent('التطبيق منفصل');
break;
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_addEvent('dispose');
super.dispose();
}
void _addEvent(String event) {
setState(() {
_lifecycleEvents.add('${DateTime.now().toString().substring(11, 19)}: $event');
if (_lifecycleEvents.length > 10) {
_lifecycleEvents.removeAt(0);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('مراقب دورة الحياة'),
backgroundColor: Colors.purple,
),
body: Column(
children: [
Container(
padding: EdgeInsets.all(16),
color: Colors.purple.withOpacity(0.1),
child: Text(
'أحداث دورة الحياة الأخيرة:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.purple,
),
),
),
Expanded(
child: ListView.builder(
itemCount: _lifecycleEvents.length,
itemBuilder: (context, index) {
return Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.purple,
child: Text('${index + 1}'),
),
title: Text(_lifecycleEvents[index]),
dense: true,
),
);
},
),
),
],
),
);
}
}
إنشاء Widgets مخصصة: من البسيط إلى المعقد
إنشاء Widgets مخصصة هو المفتاح لبناء تطبيقات منظمة وقابلة للصيانة. الـ Widgets المخصصة تسمح بإعادة الاستخدام وتنظيم الكود وإنشاء مكونات فريدة تميز تطبيقك عن غيره.
Widget مخصص بسيط - بطاقة معلومات:
import 'package:flutter/material.dart';
class InfoCard extends StatelessWidget {
final String title;
final String value;
final IconData icon;
final Color color;
final String? subtitle;
final VoidCallback? onTap;
const InfoCard({
Key? key,
required this.title,
required this.value,
required this.icon,
this.color = Colors.blue,
this.subtitle,
this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
elevation: 6,
margin: EdgeInsets.all(8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: LinearGradient(
colors: [
color.withOpacity(0.1),
Colors.white,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(10),
),
child: Icon(
icon,
color: Colors.white,
size: 20,
),
),
SizedBox(width: 12),
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
),
],
),
SizedBox(height: 16),
Text(
value,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: color,
),
),
if (subtitle != null) ...[
SizedBox(height: 4),
Text(
subtitle!,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
],
),
),
),
);
}
}
// استخدام الـ Widget المخصص
class InfoCardDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('بطاقات المعلومات المخصصة'),
backgroundColor: Colors.purple,
),
body: Padding(
padding: EdgeInsets.all(16),
child: GridView.count(
crossAxisCount: 2,
childAspectRatio: 1.2,
children: [
InfoCard(
title: 'المبيعات اليومية',
value: '1,250 ر.س',
icon: Icons.trending_up,
color: Colors.green,
subtitle: 'زيادة 12% عن أمس',
onTap: () => print('تم النقر على المبيعات'),
),
InfoCard(
title: 'العملاء الجدد',
value: '45',
icon: Icons.people,
color: Colors.blue,
subtitle: 'هذا الأسبوع',
),
InfoCard(
title: 'الطلبات المعلقة',
value: '8',
icon: Icons.pending,
color: Colors.orange,
subtitle: 'تحتاج متابعة',
),
InfoCard(
title: 'التقييم العام',
value: '4.8',
icon: Icons.star,
color: Colors.amber,
subtitle: 'من 5 نجوم',
),
],
),
),
);
}
}
Widget معقد - مشغل موسيقى مخصص:
import 'package:flutter/material.dart';
class CustomMusicPlayer extends StatefulWidget {
final String songTitle;
final String artist;
final String albumArt;
final Duration duration;
final Function(bool)? onPlayPause;
final VoidCallback? onNext;
final VoidCallback? onPrevious;
const CustomMusicPlayer({
Key? key,
required this.songTitle,
required this.artist,
required this.albumArt,
required this.duration,
this.onPlayPause,
this.onNext,
this.onPrevious,
}) : super(key: key);
@override
_CustomMusicPlayerState createState() => _CustomMusicPlayerState();
}
class _CustomMusicPlayerState extends State<CustomMusicPlayer>
with TickerProviderStateMixin {
bool _isPlaying = false;
double _currentPosition = 0.0;
late AnimationController _rotationController;
late AnimationController _playButtonController;
@override
void initState() {
super.initState();
_rotationController = AnimationController(
duration: Duration(seconds: 10),
vsync: this,
);
_playButtonController = AnimationController(
duration: Duration(milliseconds: 200),
vsync: this,
);
}
@override
void dispose() {
_rotationController.dispose();
_playButtonController.dispose();
super.dispose();
}
void _togglePlayPause() {
setState(() {
_isPlaying = !_isPlaying;
});
if (_isPlaying) {
_rotationController.repeat();
_playButtonController.forward();
} else {
_rotationController.stop();
_playButtonController.reverse();
}
widget.onPlayPause?.call(_isPlaying);
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
String minutes = twoDigits(duration.inMinutes.remainder(60));
String seconds = twoDigits(duration.inSeconds.remainder(60));
return '$minutes:$seconds';
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.purple.shade800,
Colors.purple.shade600,
Colors.pink.shade400,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.purple.withOpacity(0.3),
blurRadius: 20,
offset: Offset(0, 10),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Album Art with Rotation
AnimatedBuilder(
animation: _rotationController,
builder: (context, child) {
return Transform.rotate(
angle: _rotationController.value * 2 * 3.14159,
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 15,
offset: Offset(0, 8),
),
],
),
child: ClipOval(
child: Image.network(
widget.albumArt,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
color: Colors.grey[300],
child: Icon(
Icons.music_note,
size: 80,
color: Colors.grey[600],
),
);
},
),
),
),
);
},
),
SizedBox(height: 32),
// Song Info
Text(
widget.songTitle,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.white,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 8),
Text(
widget.artist,
style: TextStyle(
fontSize: 16,
color: Colors.white.withOpacity(0.8),
),
textAlign: TextAlign.center,
),
SizedBox(height: 32),
// Progress Bar
Column(
children: [
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.white,
inactiveTrackColor: Colors.white.withOpacity(0.3),
thumbColor: Colors.white,
overlayColor: Colors.white.withOpacity(0.2),
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 8),
),
child: Slider(
value: _currentPosition,
max: widget.duration.inSeconds.toDouble(),
onChanged: (value) {
setState(() {
_currentPosition = value;
});
},
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
_formatDuration(Duration(seconds: _currentPosition.toInt())),
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 12,
),
),
Text(
_formatDuration(widget.duration),
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 12,
),
),
],
),
),
],
),
SizedBox(height: 24),
// Control Buttons
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildControlButton(
icon: Icons.skip_previous,
onPressed: widget.onPrevious,
size: 32,
),
AnimatedBuilder(
animation: _playButtonController,
builder: (context, child) {
return Transform.scale(
scale: 1.0 + (_playButtonController.value * 0.1),
child: Container(
width: 64,
height: 64,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
child: IconButton(
onPressed: _togglePlayPause,
icon: AnimatedSwitcher(
duration: Duration(milliseconds: 200),
child: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
key: ValueKey(_isPlaying),
color: Colors.purple,
size: 32,
),
),
),
),
);
},
),
_buildControlButton(
icon: Icons.skip_next,
onPressed: widget.onNext,
size: 32,
),
],
),
],
),
);
}
Widget _buildControlButton({
required IconData icon,
required VoidCallback? onPressed,
required double size,
}) {
return Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
),
child: IconButton(
onPressed: onPressed,
icon: Icon(
icon,
color: Colors.white,
size: size,
),
),
);
}
}
تقنيات متقدمة: في الـ Widget المعقد أعلاه، استخدمنا تقنيات متقدمة مثل AnimationController للرسوم المتحركة، وGradient للخلفيات المتدرجة، وCustom Shapes للأشكال المخصصة. هذه التقنيات تخلق تجربة مستخدم غنية ومتميزة.
Layout Widgets المتقدمة: إتقان التخطيطات المعقدة
إتقان Layout Widgets المتقدمة يمكنك من إنشاء تخطيطات معقدة ومتجاوبة تتكيف مع جميع أحجام الشاشات. هذه الـ Widgets هي أدواتك لتحويل التصاميم المعقدة إلى واقع قابل للتطبيق.
تخطيط متقدم باستخدام CustomScrollView:
import 'package:flutter/material.dart';
class AdvancedLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
// SliverAppBar المتقدم
SliverAppBar(
expandedHeight: 200,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text(
'التخطيط المتقدم',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.purple.shade800,
Colors.purple.shade400,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Center(
child: Icon(
Icons.dashboard,
size: 80,
color: Colors.white.withOpacity(0.3),
),
),
),
),
),
// SliverPersistentHeader
SliverPersistentHeader(
pinned: true,
delegate: _StickyHeaderDelegate(
child: Container(
color: Colors.white,
padding: EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.filter_list, color: Colors.purple),
SizedBox(width: 8),
Text(
'الفئات',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.purple,
),
),
],
),
),
),
),
// SliverGrid للشبكة
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.purple.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.purple.shade200),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.category,
size: 40,
color: Colors.purple,
),
SizedBox(height: 8),
Text(
'فئة ${index + 1}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.purple,
),
),
],
),
);
},
childCount: 6,
),
),
// SliverToBoxAdapter للمحتوى المخصص
SliverToBoxAdapter(
child: Container(
margin: EdgeInsets.all(16),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue.shade50, Colors.purple.shade50],
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'المحتوى المميز',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.purple,
),
),
SizedBox(height: 10),
Text(
'هذا مثال على استخدام SliverToBoxAdapter لإدراج محتوى مخصص في CustomScrollView.',
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
height: 1.5,
),
),
],
),
),
),
// SliverList للقائمة
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return Card(
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.purple,
child: Text('${index + 1}'),
),
title: Text('عنصر القائمة ${index + 1}'),
subtitle: Text('وصف العنصر رقم ${index + 1}'),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('تم النقر على العنصر ${index + 1}'),
),
);
},
),
);
},
childCount: 20,
),
),
],
),
);
}
}
// مفوض الرأس الثابت
class _StickyHeaderDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
_StickyHeaderDelegate({required this.child});
@override
double get minExtent => 60;
@override
double get maxExtent => 60;
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return child;
}
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}
تخطيط متجاوب باستخدام LayoutBuilder:
class ResponsiveLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('التخطيط المتجاوب'),
backgroundColor: Colors.purple,
),
body: LayoutBuilder(
builder: (context, constraints) {
// تحديد نوع الجهاز بناءً على العرض
if (constraints.maxWidth > 1200) {
return _buildDesktopLayout();
} else if (constraints.maxWidth > 800) {
return _buildTabletLayout();
} else {
return _buildMobileLayout();
}
},
),
);
}
Widget _buildDesktopLayout() {
return Row(
children: [
// الشريط الجانبي
Container(
width: 250,
color: Colors.purple.shade50,
child: _buildSidebar(),
),
// المحتوى الرئيسي
Expanded(
flex: 2,
child: _buildMainContent(),
),
// الشريط الجانبي الثانوي
Container(
width: 200,
color: Colors.grey.shade50,
child: _buildSecondarySidebar(),
),
],
);
}
Widget _buildTabletLayout() {
return Row(
children: [
// الشريط الجانبي المصغر
Container(
width: 80,
color: Colors.purple.shade50,
child: _buildCompactSidebar(),
),
// المحتوى الرئيسي
Expanded(
child: _buildMainContent(),
),
],
);
}
Widget _buildMobileLayout() {
return Column(
children: [
// شريط التنقل العلوي
Container(
height: 60,
color: Colors.purple.shade50,
child: _buildTopNavigation(),
),
// المحتوى الرئيسي
Expanded(
child: _buildMainContent(),
),
],
);
}
Widget _buildSidebar() {
return ListView(
padding: EdgeInsets.all(16),
children: [
Text(
'القائمة الرئيسية',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.purple,
),
),
SizedBox(height: 16),
...List.generate(5, (index) {
return ListTile(
leading: Icon(Icons.dashboard, color: Colors.purple),
title: Text('عنصر ${index + 1}'),
onTap: () {},
);
}),
],
);
}
Widget _buildCompactSidebar() {
return ListView(
padding: EdgeInsets.all(8),
children: List.generate(5, (index) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: IconButton(
icon: Icon(Icons.dashboard, color: Colors.purple),
onPressed: () {},
),
);
}),
);
}
Widget _buildTopNavigation() {
return Row(
children: [
IconButton(
icon: Icon(Icons.menu, color: Colors.purple),
onPressed: () {},
),
Expanded(
child: Text(
'التنقل العلوي',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.purple,
),
textAlign: TextAlign.center,
),
),
IconButton(
icon: Icon(Icons.search, color: Colors.purple),
onPressed: () {},
),
],
);
}
Widget _buildMainContent() {
return Container(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'المحتوى الرئيسي',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(height: 16),
Expanded(
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.5,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: 10,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
child: Center(
child: Text(
'بطاقة ${index + 1}',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
);
},
),
),
],
),
);
}
Widget _buildSecondarySidebar() {
return Container(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'معلومات إضافية',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
),
SizedBox(height: 16),
...List.generate(3, (index) {
return Card(
child: Padding(
padding: EdgeInsets.all(12),
child: Text('معلومة ${index + 1}'),
),
);
}),
],
),
);
}
}
Widgets التفاعلية: إنشاء تجارب مستخدم غنية
الـ Widgets التفاعلية هي قلب تجربة المستخدم في Flutter. هذه الـ Widgets تتيح للمستخدمين التفاعل مع التطبيق بطرق طبيعية ومباشرة، مما يخلق تجربة سلسة وممتعة.
Widget تفاعلي متقدم - لوحة التحكم:
import 'package:flutter/material.dart';
class InteractiveControlPanel extends StatefulWidget {
@override
_InteractiveControlPanelState createState() => _InteractiveControlPanelState();
}
class _InteractiveControlPanelState extends State<InteractiveControlPanel>
with TickerProviderStateMixin {
double _brightness = 50.0;
double _volume = 30.0;
double _temperature = 22.0;
bool _isWifiEnabled = true;
bool _isBluetoothEnabled = false;
bool _isLocationEnabled = true;
late AnimationController _pulseController;
late Animation<double> _pulseAnimation;
@override
void initState() {
super.initState();
_pulseController = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
_pulseAnimation = Tween<double>(begin: 1.0, end: 1.1).animate(
CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
);
_pulseController.repeat(reverse: true);
}
@override
void dispose() {
_pulseController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('لوحة التحكم التفاعلية'),
backgroundColor: Colors.purple,
elevation: 0,
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.purple.shade50,
Colors.blue.shade50,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
children: [
// قسم المتزلقات
_buildSectionCard(
title: 'التحكم في الإعدادات',
icon: Icons.tune,
children: [
_buildSliderControl(
label: 'السطوع',
value: _brightness,
min: 0,
max: 100,
icon: Icons.brightness_6,
color: Colors.orange,
onChanged: (value) {
setState(() {
_brightness = value;
});
},
),
_buildSliderControl(
label: 'مستوى الصوت',
value: _volume,
min: 0,
max: 100,
icon: Icons.volume_up,
color: Colors.blue,
onChanged: (value) {
setState(() {
_volume = value;
});
},
),
_buildSliderControl(
label: 'درجة الحرارة',
value: _temperature,
min: 16,
max: 30,
icon: Icons.thermostat,
color: Colors.red,
suffix: '°C',
onChanged: (value) {
setState(() {
_temperature = value;
});
},
),
],
),
SizedBox(height: 16),
// قسم المفاتيح
_buildSectionCard(
title: 'الاتصالات',
icon: Icons.wifi,
children: [
_buildSwitchControl(
label: 'Wi-Fi',
value: _isWifiEnabled,
icon: Icons.wifi,
activeColor: Colors.green,
onChanged: (value) {
setState(() {
_isWifiEnabled = value;
});
},
),
_buildSwitchControl(
label: 'Bluetooth',
value: _isBluetoothEnabled,
icon: Icons.bluetooth,
activeColor: Colors.blue,
onChanged: (value) {
setState(() {
_isBluetoothEnabled = value;
});
},
),
_buildSwitchControl(
label: 'الموقع',
value: _isLocationEnabled,
icon: Icons.location_on,
activeColor: Colors.purple,
onChanged: (value) {
setState(() {
_isLocationEnabled = value;
});
},
),
],
),
SizedBox(height: 16),
// أزرار الإجراءات
_buildSectionCard(
title: 'إجراءات سريعة',
icon: Icons.flash_on,
children: [
Row(
children: [
Expanded(
child: _buildActionButton(
label: 'إعادة تشغيل',
icon: Icons.refresh,
color: Colors.orange,
onPressed: () => _showActionDialog('إعادة تشغيل'),
),
),
SizedBox(width: 12),
Expanded(
child: AnimatedBuilder(
animation: _pulseAnimation,
builder: (context, child) {
return Transform.scale(
scale: _pulseAnimation.value,
child: _buildActionButton(
label: 'طوارئ',
icon: Icons.warning,
color: Colors.red,
onPressed: () => _showActionDialog('طوارئ'),
),
);
},
),
),
],
),
SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildActionButton(
label: 'حفظ الإعدادات',
icon: Icons.save,
color: Colors.green,
onPressed: () => _saveSettings(),
),
),
SizedBox(width: 12),
Expanded(
child: _buildActionButton(
label: 'إعادة تعيين',
icon: Icons.restore,
color: Colors.grey,
onPressed: () => _resetSettings(),
),
),
],
),
],
),
],
),
),
),
);
}
Widget _buildSectionCard({
required String title,
required IconData icon,
required List<Widget> children,
}) {
return Card(
elevation: 8,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: Colors.purple, size: 24),
SizedBox(width: 12),
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
],
),
SizedBox(height: 16),
...children,
],
),
),
);
}
Widget _buildSliderControl({
required String label,
required double value,
required double min,
required double max,
required IconData icon,
required Color color,
String suffix = '',
required ValueChanged<double> onChanged,
}) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: color, size: 20),
SizedBox(width: 8),
Text(
label,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
Spacer(),
Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
'${value.round()}$suffix',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: color,
),
),
),
],
),
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: color,
inactiveTrackColor: color.withOpacity(0.3),
thumbColor: color,
overlayColor: color.withOpacity(0.2),
),
child: Slider(
value: value,
min: min,
max: max,
onChanged: onChanged,
),
),
],
),
);
}
Widget _buildSwitchControl({
required String label,
required bool value,
required IconData icon,
required Color activeColor,
required ValueChanged<bool> onChanged,
}) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Icon(
icon,
color: value ? activeColor : Colors.grey,
size: 20,
),
SizedBox(width: 12),
Expanded(
child: Text(
label,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
Switch(
value: value,
onChanged: onChanged,
activeColor: activeColor,
),
],
),
);
}
Widget _buildActionButton({
required String label,
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: color,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 4,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 24),
SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
void _showActionDialog(String action) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('تأكيد الإجراء'),
content: Text('هل أنت متأكد من تنفيذ إجراء "$action"؟'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('إلغاء'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('تم تنفيذ إجراء $action')),
);
},
child: Text('تأكيد'),
),
],
),
);
}
void _saveSettings() {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('تم حفظ الإعدادات بنجاح'),
backgroundColor: Colors.green,
),
);
}
void _resetSettings() {
setState(() {
_brightness = 50.0;
_volume = 30.0;
_temperature = 22.0;
_isWifiEnabled = true;
_isBluetoothEnabled = false;
_isLocationEnabled = true;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('تم إعادة تعيين الإعدادات'),
backgroundColor: Colors.orange,
),
);
}
}
أفضل الممارسات المتقدمة في Widgets
تطبيق أفضل الممارسات المتقدمة في تطوير الـ Widgets يضمن إنشاء تطبيقات عالية الأداء وسهلة الصيانة. هذه الممارسات مستمدة من خبرات المطورين المحترفين والتوصيات الرسمية من فريق Flutter.
جدول مقارنة أنواع الـ Widgets:
نصائح الأداء المتقدمة:
مثال على أفضل الممارسات:
// ❌ ممارسة خاطئة
class BadPracticeWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// إنشاء widget جديد في كل build
Container(
padding: EdgeInsets.all(16),
child: Text('عنوان'),
),
// استخدام دالة داخل build
_buildComplexWidget(),
],
);
}
Widget _buildComplexWidget() {
return Container(/* ... */);
}
}
// ✅ ممارسة صحيحة
class GoodPracticeWidget extends StatelessWidget {
// إنشاء widgets ثابتة خارج build
static const Widget _titleWidget = Padding(
padding: EdgeInsets.all(16),
child: Text('عنوان'),
);
@override
Widget build(BuildContext context) {
return Column(
children: [
_titleWidget,
const ComplexWidget(), // widget منفصل
],
);
}
}
class ComplexWidget extends StatelessWidget {
const ComplexWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: const Text('محتوى معقد'),
);
}
}
نصيحة متقدمة: استخدم Flutter Inspector وPerformance Overlay لمراقبة أداء تطبيقك. هذه الأدوات تساعدك في تحديد الـ Widgets التي تحتاج تحسين وتتبع استهلاك الذاكرة والمعالج.
خاتمة: إتقان فن الـ Widgets في Flutter
إن إتقان Widgets المتقدمة في Flutter يفتح أمامك عالماً من الإمكانيات اللامحدودة لإنشاء تطبيقات استثنائية. من خلال فهم دورة الحياة وإنشاء مكونات مخصصة وتطبيق أفضل الممارسات، تصبح قادراً على بناء تجارب مستخدم لا تُنسى.
تذكر أن الـ Widgets ليست مجرد أدوات تقنية، بل هي وسيلتك للتعبير عن الإبداع وتحويل الأفكار إلى واقع رقمي يلمسه المستخدمون. استثمر في إتقان هذه المهارات، وستجد نفسك قادراً على إنشاء تطبيقات تنافس أفضل التطبيقات العالمية.
إقرأ أيضا هذا المقال التفصيلي حول تعلم Flutter وDart بالعربية للمبتدئين: دليلك الشامل لتطوير التطبيقات 2025