Kodax

شجرة الـ Widgets: Stateless و Stateful — أساسيات Flutter

Widgets في Flutter

كل شيء Widget!

في Flutter، كل عنصر على الشاشة هو Widget:

- النصوص

- الأزرار

- التخطيطات

- حتى التطبيق نفسه!

أنواع Widgets

1. StatelessWidget

- لا تتغير بعد البناء

- للعناصر الثابتة

- أداء أفضل

2. StatefulWidget

- تتغير خلال التشغيل

- تستخدم setState()

- للعناصر التفاعلية

Widgets شائعة

تخطيط (Layout)

- Container: صندوق مع تنسيق

- Row: ترتيب أفقي

- Column: ترتيب عمودي

- Stack: عناصر فوق بعض

- ListView: قائمة قابلة للتمرير

إدخال (Input)

- TextField: حقل نصي

- Button: أزرار متنوعة

- Checkbox, Switch, Slider

عرض (Display)

- Text: نص

- Image: صورة

- Icon: أيقونة

- Card: بطاقة

القاعدة الذهبية

> إذا كان العنصر يتغير، استخدم StatefulWidget

> إذا كان ثابتاً، استخدم StatelessWidget

// مثال شامل على Widgets
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'تطبيق Widgets',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), // primarySwatch لا أثر لها عملياً مع Material 3
        useMaterial3: true,
      ),
      home: HomePage(),
    );
  }
}

// ===== 1. StatelessWidget - بطاقة منتج =====
class ProductCard extends StatelessWidget {
  final String name;
  final double price;
  final String imageUrl;
  final VoidCallback? onTap;

  const ProductCard({
    super.key,
    required this.name,
    required this.price,
    required this.imageUrl,
    this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 4,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // الصورة
            ClipRRect(
              borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
              child: Image.network(
                imageUrl,
                height: 120,
                width: double.infinity,
                fit: BoxFit.cover,
              ),
            ),
            // المحتوى
            Padding(
              padding: const EdgeInsets.all(12),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    name,
                    style: const TextStyle(
                      fontSize: 16,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 4),
                  Text(
                    '$${price.toStringAsFixed(2)}',
                    style: TextStyle(
                      fontSize: 14,
                      color: Theme.of(context).primaryColor,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// ===== 2. StatefulWidget - عداد مع أنيميشن =====
class AnimatedCounter extends StatefulWidget {
  const AnimatedCounter({super.key});

  @override
  State<AnimatedCounter> createState() => _AnimatedCounterState();
}

class _AnimatedCounterState extends State<AnimatedCounter>
    with SingleTickerProviderStateMixin {
  int _count = 0;
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 200),
      vsync: this,
    );
    _animation = Tween<double>(begin: 1, end: 1.2).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeOut),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _increment() {
    setState(() => _count++);
    _controller.forward().then((_) => _controller.reverse());
  }

  void _decrement() {
    if (_count > 0) {
      setState(() => _count--);
      _controller.forward().then((_) => _controller.reverse());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: [
            const Text('العداد', style: TextStyle(fontSize: 18)),
            const SizedBox(height: 16),
            ScaleTransition(
              scale: _animation,
              child: Text(
                '$_count',
                style: const TextStyle(
                  fontSize: 48,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            const SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: _decrement,
                  child: const Icon(Icons.remove),
                ),
                const SizedBox(width: 20),
                ElevatedButton(
                  onPressed: _increment,
                  child: const Icon(Icons.add),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// ===== الصفحة الرئيسية =====
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('تعلم Widgets'),
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              'StatefulWidget - عداد',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            const AnimatedCounter(),
            const SizedBox(height: 24),
            const Text(
              'StatelessWidget - منتجات',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            GridView.count(
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              crossAxisCount: 2,
              crossAxisSpacing: 12,
              mainAxisSpacing: 12,
              childAspectRatio: 0.8,
              children: [
                ProductCard(
                  name: 'هاتف ذكي',
                  price: 599.99,
                  imageUrl: 'https://via.placeholder.com/150',
                  onTap: () => print('تم النقر!'),
                ),
                ProductCard(
                  name: 'سماعات',
                  price: 199.99,
                  imageUrl: 'https://via.placeholder.com/150',
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
اختبر فهمك