التصنيفات
Flutter

استخدام Provider مع Flutter لإدارة حالة التطبيق

كثير من عمليات البحث في المدونة تسأل عن كيفية استخدام Provider في تطبيقات Flutter، وهذا متوقع لأن موقع Flutter مصنف Provider أسهل طريقة لإدارة بيانات التطبيق. لكن إذا بحثت ستجد ان هناك اكثر من نوع للـProvider، واكثر من طريقة لاستخدامه، في هذا الدرس سأقوم بشكل يسير بتعريف الـ Provider وكيف نوظفها في التطبيق.

ماهو الـ Provider ؟

خلنا نقول هو اسهل طريقة لادارة بيانات التطبيق، بالاضافة للتوصية من Flutter باستخدامة، بالاضافة الى وصف كاتب الحزمة:

ليس مجرد عرض للقيم، ولكن يمكنك إنشاؤها ومتابعتها وايضاً التخلص منها.

https://pub.dev/packages/provider

عند اضافة Provider إلى Widget Tree فيمكنك الوصول الى قيمة الـ Provider في أي مكان من التطبيق.

الآن يمكن اخذنا فكره من الـ Provider ولكن كيف نقدر نستخدمة ؟ انشئ مشروع Flutter جديد وقم باضافة الـ Provider الى dependencies . يوجد طريقة اسهل واجمل لاضافة الحزم الى مشروعك، فقط استخدم هذا الامر فيد مسار مشروعك.

flutter pub add provider

سيقوم هذا الأمر بإضافة آخر اصدار من الحزمة لمشروعك.

سنقوم بإنشاء واجهة بسيطة تحتوي على Button ينفذ عملية و Text يعرض نتائج العملية، حتى يكون الشرح سهل وواضح بعيد عن التعقيدات.

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Provider"),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Container(
            padding: const EdgeInsets.all(50),
            child: Text('النتيجة'),
          ),
          Container(
            padding: const EdgeInsets.all(20),
            child: ElevatedButton(
              child: Text('تنفيذ الامر'),
              onPressed: () {},
            ),
          ),
        ],
      ),
    );
  }
}

لا يوجد في الكود السابق شئ مميز ، عمود يوجد فيه Widgets، سنقوم الآن باضافة Provider.

Provider هو أبسط Widget يوجد في الحزمة، يقوم بالعادة بالتعامل مع Object ولكن لا يساعد ابدا في تحديث الواجهة في حال حدوث تغيير للبيانات، كما ستلاحظ في الفيديو التالي عند الضغط على الـ Button لا يوجد تحديث أو تغيير للبيانات. سنقوم بإنشاء الـ Object الذي سنتعامل معه وهو بسييييييط جدا.

class User {
  String name = 'Flutter';

  void action() {
    name = 'Provider';
  }
}

الآن سنقوم بتعديل واجهة التطبيق واضافة Provider Widget إلى الواجهة واستخدام Consumer Widget واضافة Model الذي قمنا بانشائه.

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider(
      create: (context) => User(),
      child: Scaffold(
        appBar: AppBar(
          title: Text("Provider"),
        ),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Consumer<User>(
              builder: (context, model, child) => Container(
                padding: const EdgeInsets.all(50),
                child: Text(model.name),
              ),
            ),
            Consumer<User>(
              builder: (context, model, child) => Container(
                padding: const EdgeInsets.all(20),
                child: ElevatedButton(
                  child: Text('تنفيذ الامر'),
                  onPressed: () {
                    model.action();
                  },
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

نلاحظ اضفنا Provider و قمنا أيضا باضافة User الى create، بالاضافة قمنا باستخدام Consumer على Text و Button ايضاً، داخل Text وصلنا لقيم User وهو name وداخل Button اضفنا العملية وهي action. لكن بعد كل هذه التعديلات لن تتغير القيمة إذا قمنا بالضغط على Button. الاختلاف اننا استطعنا الوصول لقيمة المتغير name في User.

كيف نقدر نغير القيمة إذا المستخدم ضغط الـ Button 🤔 ؟

ChnageNotifireProvider

هنا سيقوم Widget لمتابعة التغييرات في User وعند حدوث أي تغيير في البيانات سيقوم بإعادة بناء Widget وعرض القيم الجديدة. سنقوم بتعديل User ونضيف ChangeNotifre عند حدوث تغيير سيقوم ChangeNotifireProvider بإعادة بناء اي Widget تحت Consumer٫

class User with ChangeNotifier {
  String name = 'Flutter';

  void action() {
    name = 'Provider';
    notifyListeners();
  }
}

الان سنقوم فقط بتغيير Provider Widget الى ChangeNotifierWidget

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => User(),
      child: Scaffold(
        appBar: AppBar(
          title: Text("Provider"),
        ),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Consumer<User>(
              builder: (context, model, child) => Container(
                padding: const EdgeInsets.all(50),
                child: Text(model.name),
              ),
            ),
            Consumer<User>(
              builder: (context, model, child) => Container(
                padding: const EdgeInsets.all(20),
                child: ElevatedButton(
                  child: Text('تنفيذ الامر'),
                  onPressed: () {
                    model.action();
                  },
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

الان اصبحت البيانات تتغير عن الضغط على Button ولكن اذا لاحظت ان Widget تحت Consumer Widget, لكن مانحتاج نعيد بناء Button لأن لايوجد قيمة متغيرة للدالة Action. سنقوم بتعديل بسيط على الكود.

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => User(),
      child: Scaffold(
        appBar: AppBar(
          title: Text("Provider"),
        ),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Consumer<User>(
              builder: (context, model, child) => Container(
                padding: const EdgeInsets.all(50),
                child: Text(model.name),
              ),
            ),
            Container(
              padding: const EdgeInsets.all(20),
              child: Button(),
            ),
          ],
        ),
      ),
    );
  }
}

class Button extends StatelessWidget {
  const Button({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      child: Text('تنفيذ الامر'),
      onPressed: () => Provider.of<User>(context, listen: false).action(),
    );
  }
}

طيب لو عندي اكثر من Provider كيف اقدر اتعامل معها ؟ طيب لو نفرض عندنا Data Model ثاني خاص بعمر المستخدم، سنقوم باستخدام MultiProvider .

MultiProvider

حقيقة لا يوجد اي اختلاف بين استخدام Provider او Multi Provider الا في طريقة اضافتها للمشروع، اولا سنقوم بانشاء Data Model للـAge بعد ذلك سنقوم بتعديل الكود لاستخدام اكثر من Provider .

class Age with ChangeNotifier {
  int age = 0;

  void action() {
    age++;
    notifyListeners();
  }
}
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<User>(create: (context) => User()),
        ChangeNotifierProvider<Age>(create: (context) => Age()),
      ],
      child: Scaffold(
        appBar: AppBar(
          title: Text("Provider"),
        ),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Consumer<User>(
              builder: (context, model, child) => Container(
                padding: const EdgeInsets.all(50),
                child: Text(model.name),
              ),
            ),
            Container(
              padding: const EdgeInsets.all(20),
              child: Button(),
            ),
            Consumer<Age>(
              builder: (context, model, child) => Container(
                padding: const EdgeInsets.all(50),
                child: Text('${model.age}'),
              ),
            ),
            Container(
              padding: const EdgeInsets.all(20),
              child: AgeButton(),
            ),
          ],
        ),
      ),
    );
  }
}

class Button extends StatelessWidget {
  const Button({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      child: Text('تغيير الاسم  '),
      onPressed: () => Provider.of<User>(context, listen: false).action(),
    );
  }
}

class AgeButton extends StatelessWidget {
  const AgeButton({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      child: Text('تغيير العمر'),
      onPressed: () => Provider.of<Age>(context, listen: false).action(),
    );
  }
}

لايوجد اختلاف استخدمنا ChangeNotifire Provider ولكن مع اكثر من Provider ايضا استخدمنا نفس المفهوم. ستكون النتيجة كما في التالي:

طيب من هنا وين نروح ؟ حقيقة انصح بـRiverpod من نفس كاتب Provider وقد تم بنائه على نفس الـProvider

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني.