إدارة الحالة في Flutter: تطبيق الأنماط المتقدمة للمطورين

Amine
11/12/2024

إدارة الحالة هي حجر الأساس في تطوير تطبيقات Flutter الحديثة. في هذا الدليل الشامل، سنتعمق في جميع جوانب إدارة الحالة، من المفاهيم الأساسية إلى الأنماط المتقدمة.

  • فهم مفهوم الحالة وأنواعها في Flutter
  • التعرف على مختلف حلول إدارة الحالة
  • متى وكيف تستخدم كل حل
  • أفضل الممارسات والأنماط المتقدمة

1. مفهوم الحالة في Flutter

الحالة في Flutter هي أي معلومات يمكن أن تتغير مع مرور الوقت وتؤثر على مظهر التطبيق. تنقسم الحالة إلى عدة أنواع:

  • الحالة المؤقتة (Ephemeral State): حالة محلية تؤثر على widget واحد فقط
  • حالة التطبيق (App State): حالة مشتركة بين عدة أجزاء من التطبيق
  • الحالة المستمرة (Persistent State): بيانات تحتاج للحفظ محلياً أو على الخادم

2. إدارة الحالة المحلية باستخدام setState

setState هي الطريقة الأساسية لإدارة الحالة في Flutter. مناسبة للحالات البسيطة والمحلية.

class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;
  bool _isEven = true;

  void _incrementCounter() {
    setState(() {
      _counter++;
      _isEven = _counter % 2 == 0;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          'العدد: $_counter',
          style: TextStyle(
            fontSize: 24,
            color: _isEven ? Colors.green : Colors.red,
          ),
        ),
        ElevatedButton(
          onPressed: _incrementCounter,
          child: Text('زيادة'),
        ),
      ],
    );
  }
}
  • استخدم setState عندما تكون الحالة محلية لـ widget واحد
  • تجنب استخدام setState للحالات المعقدة أو المشتركة
  • تأكد من استدعاء setState داخل الـ State class

3. إدارة الحالة باستخدام Provider

Provider هو حل متقدم لإدارة الحالة يسمح بمشاركة البيانات بين widgets المختلفة بسهولة.

// نموذج البيانات
class CartModel extends ChangeNotifier {
  final List<String> _items = [];

  List<String> get items => _items;

  void addItem(String item) {
    _items.add(item);
    notifyListeners();
  }

  void removeItem(String item) {
    _items.remove(item);
    notifyListeners();
  }
}

// استخدام Provider في التطبيق
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CartModel(),
      child: MyApp(),
    ),
  );
}

// عرض البيانات
class CartView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<CartModel>(
      builder: (context, cart, child) {
        return Column(
          children: [
            Text('عدد العناصر: ${cart.items.length}'),
            ListView.builder(
              itemCount: cart.items.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(cart.items[index]),
                  trailing: IconButton(
                    icon: Icon(Icons.remove),
                    onPressed: () {
                      cart.removeItem(cart.items[index]);
                    },
                  ),
                );
              },
            ),
          ],
        );
      },
    );
  }
}

4. أفضل الممارسات في إدارة الحالة

  • اختيار الحل المناسب:

    • setState: للحالات البسيطة والمحلية
    • Provider: للتطبيقات المتوسطة وإدارة الحالة المشتركة
    • BLoC: للتطبيقات الكبيرة والمعقدة

  • تنظيم الكود:

    • فصل منطق الأعمال عن واجهة المستخدم
    • استخدام نماذج البيانات (Models)
    • تجزئة الحالة إلى وحدات منطقية

  • الأداء:

    • تجنب إعادة البناء غير الضرورية
    • استخدام Consumer بدلاً من Provider.of
    • إغلاق Streams عند عدم الحاجة

5. مثال عملي: تطبيق متكامل

لنقم ببناء تطبيق متكامل يجمع بين مختلف أنماط إدارة الحالة:

// نموذج البيانات
class Product {
  final String id;
  final String name;
  final double price;
  final String imageUrl;

  Product({
    required this.id,
    required this.name,
    required this.price,
    required this.imageUrl,
  });
}

// حالة السلة باستخدام Provider
class CartModel extends ChangeNotifier {
  final Map<String, int> _items = {};
  
  Map<String, int> get items => _items;
  
  int get totalItems => _items.values.fold(0, (sum, quantity) => sum + quantity);
  
  void addItem(Product product) {
    _items[product.id] = (_items[product.id] ?? 0) + 1;
    notifyListeners();
  }
  
  void removeItem(String productId) {
    _items.remove(productId);
    notifyListeners();
  }
}

// حالة المصادقة باستخدام BLoC
class AuthBloc {
  final _stateController = StreamController<User?>();
  Stream<User?> get user => _stateController.stream;

  Future<void> login(String email, String password) async {
    try {
      final user = await AuthService.login(email, password);
      _stateController.add(user);
    } catch (e) {
      _stateController.addError(e);
    }
  }

  void logout() {
    _stateController.add(null);
  }
}

// الصفحة الرئيسية
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Consumer<CartModel>(
          builder: (context, cart, child) {
            return Text('السلة (${cart.totalItems})');
          },
        ),
        actions: [
          StreamBuilder<User?>(
            stream: authBloc.user,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return IconButton(
                  icon: Icon(Icons.logout),
                  onPressed: () => authBloc.logout(),
                );
              }
              return IconButton(
                icon: Icon(Icons.login),
                onPressed: () => Navigator.pushNamed(context, '/login'),
              );
            },
          ),
        ],
      ),
      body: ProductGrid(),
    );
  }
}

// شبكة المنتجات
class ProductGrid extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<ProductsModel>(
      builder: (context, products, child) {
        return GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            childAspectRatio: 0.75,
          ),
          itemCount: products.items.length,
          itemBuilder: (context, index) {
            final product = products.items[index];
            return ProductCard(product: product);
          },
        );
      },
    );
  }
}

// بطاقة المنتج
class ProductCard extends StatelessWidget {
  final Product product;

  ProductCard({required this.product});

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(
        children: [
          Image.network(product.imageUrl),
          Text(product.name),
          Text('${product.price} د.ج'),
          Consumer<CartModel>(
            builder: (context, cart, child) {
              return ElevatedButton(
                onPressed: () => cart.addItem(product),
                child: Text('إضافة للسلة'),
              );
            },
          ),
        ],
      ),
    );
  }
}

التعليقات

اترك تعليقاً