ورود و عضویت
0
سبد خرید شما خالی است
0
سبد خرید شما خالی است

آموزش مدیریت State با استفاده از Riverpod در فلاتر

4 دیدگاه
10 دقیقه برای مطالعه

پکیج Riverpod یکی از جدیدترین روش های معرفی شده در فلاتر برای مدیریت State می باشد.

انتخاب بهترین سیستم مدیریت State در فریمورک فلاتر کار سختی هست مخصوصا اگر بخواهید از دیگران بابت آن مشورت بگیرید به دلیل اینکه هر شخصی یک پکیجی معرفی میکند و باعث سردرگمی برنامه نویسان تازه کار میشود.

بنابراین بهتر با سیستم های مدیریت State مختلف کار کنید و با هر کدام که احساس راحتی میکردید را انتخاب کنید. در این مطلب قصد داریم شما را با پکیج فلاتر Riverpod آشنا کنیم.

Riverpod در واقع یک پیاده سازی جدید با امکانات متفاوت از Provider است که احتمالا با آن آشنا هستید, نکته جالب توجه این که هر دو ویجت هم توسط یک نفر نوشته شده است.

امکانات Riverpod

قبل از شروع به کار کردن با مثال های عملی در فلاتر بهتر هست که کمی با امکانات Riverpod آشنا شوید.

  • پشتیبانی از چندین providers به صورت همزمان
  • اطلاع از خطاهای برنامه در هنگام کدنویسی به جای زمان اجرا, اگر کد شما کامپایل شود پس مطمئن هستید خطایی ندارد.
  • اطمینان از قابل تست بودن کد
  • وابسته نبودن به فلاتر

شما میتونید در هرجایی state ویجت را به اشتراک بگذارید, میتونید حتی کد state خود را در یک پکیج جدا از ویجتی که به آن نیاز دارد قرار دهید.

مدیریت های لودینگ و خطا به راحتی و بدون نوشتن کدهای اضافی.

نصب Riverpod

برای نصب پکیج این ویجت شما با سه گزینه مختلف روبرو هستید که بنابر نیاز میتوانید از آنها استفاده کنید. اما با هم بررسی کنیم ببینیم این سه نسخه مختلف چی هستند.

hooks_riverpod: این پکیج برای زمانی هست که بخواید از امکانات پکیج flutter_hooks هم نیز بصورت همزمان استفاده کنید. در این آموزش از این نسخه استفاده میکنیم.

flutter_riverpod: این نسخه حالت پایه ای از پکیج Riverpod هست بدون امکانات اضافی.

riverpod: این نسخه مخصوص زبان دارت هست و از فلاتر پشتیبانی نمیکند.

dependencies:
  flutter_hooks: ^0.14.0
  hooks_riverpod: ^0.11.1

طراحی رابط کاربری

هدف کلی سیستم های مدیریت state در فلاتر جدا کردن بخش رابط کاربری از بخش منطقی و state و قابل گسترش دادن پروژه است.

زمانی که پروژه جدیدی در فلاتر ایجاد میکنید یک مثال پیش فرض از counter هست که تمام بخش های رابط کاربری و منطقی برنامه باهم در فایل main قرار دارد.

فایل main را با کدهای زیر جایگزین کنید.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('My Counter App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text(
              '0',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

هدف ما پیاده سازی این پروژه با استفاده از riverpod است.

بنابراین MyHomeWidget را به یک StatelessWidget تغییر دادیم.

ایجاد کلاس مدل

کلاس مدل در واقع نمایانگر state بخشی از برنامه شما است.در این مثال به عنوان state شمارنده عمل میکند.

شما از این کلاس استفاده میکنید تا مقدار فعلی counter را در صفحه نمایش دهید.

فایل جدیدی به نام counter.dart بسازید و کدهای زیر را در آن قرار دهید.

class CounterModel {
  const CounterModel(this.count);
  final int count;
}

داخل کلاس مدل از int و final برای تعریف متغیر استفاده کردیم تا نوع state غیر قابل تغییر باشد در طول پروه این کار از ایجاد خطا هنگام تغییر نوع state جلوگیری میکند.

بعد از ساخت کلاسی برای نمایش state حالا نیاز داریم تا کلاسی برای مدیریت state و همچین ذخیره کردن مقدار فعلی و افزایش این مقدار با کلیک کردن روی + ایجاد کنیم.

کلاسی به اسم counter.dart بسازید و کدهای زیر را در آن قرار دهید.

import 'package:hooks_riverpod/hooks_riverpod.dart';class CounterNotifier extends StateNotifier<CounterModel> {
  CounterNotifier() : super(_initialValue);  static const _initialValue = CounterModel(0);  void increment() {
    state = CounterModel(state.count + 1);
  } 
}

StateNotifier یک متغیر داخلی به نام state دارد که در واقع مقدار فعلی state برنامه است, در اینجا یک نمونه از کلاس مدل را برای آن قرار میدهیم.

در این کلاس باید مقدار اولیه state را با استفاده از متد super مشخص کنید.

زمانی که state تغییر کند StateNotifier به تمام آبجکت هایی که به آن گوش میکردند اطلاع میدهد.

فعال کردن Riverpod

برای استفاده کردن از Riverpod در پروژه باید کل برنامه را داخل ویجت ProviderScope قرار دهید.

پس متد main برنامه را به شکل زیر تغییر دهید.

void main() {
  runApp(
    const ProviderScope(child: MyApp()),
  );
}

نیازی نیست کل برنامه خودتون و به شکل const تعریف کنید اما اینکار باعث بهینه تر شدن برنامه برای کامپایلر میشود.

برای اینکه به خطا نخوریم کلاس MyApp را نیز باید از نوع const تعریف کنیم.

const MyApp({Key key}) : super(key: key);

تعریف provider سراسری

داخل فایل main متغیری جدیدی به این شکل تعریف کنید.

final provider = StateNotifierProvider((ref) => CounterNotifier());

تا زمانی که provider را به شکل constant سراسری تعریف کردید از تمام بخش های برنامه به آن دسترسی دارید. معمولا پیشنهاد میشه که در برنامه نویسی از تعریف متغیر های سراسری ثابت اجتناب کنید یا در زبان های دیگر متغیر استاتیک تعریف کنید, علت این حرف به این خاطر هست که این متغیر از طریق همه کلاس ها قابل تغییر هستند و اینکار باعث به وجود آمدن مشکل میشود اما اگر یادتون باشه کلاس مدل ما به شکل غیرقابل تغییر تعریف شده است.

در مرحله بعدی باید ویجت کلاس Myhomepage را از StatelessWidget به نوع HookWidget ویجت تغییر دهید. این کار باعث می شود به متدهایی که در مرحله بعد نیاز داریم دسترسی داشته باشیم مثل useProvider.

class MyHomePage extends HookWidget {
  // ...
}

گوش دادن به تغییرات

برای اینکه با تغییر مقدار counter بتوانیم مقدار جدید را نمایش دهیم و ویجت را دوباره بسازیم باید به تغییرات آن گوش دهیم.

در کلاس MyhomePage و درون متد build متغیر زیر را تعریف میکنیم.

final counterModel = useProvider(provider.state);

متد useProvider به تغییرات provider.state که از نوع کلاس مدل CounterModel است گوش میدهد.

همچنین در این حالت شما به state نیز دسترسی دارید.

برای نمایش مقدار فعلی شمارنده از یک ویجت متن استفاده میکنیم.

Text(
  '${counterModel.count}',
  style: Theme.of(context).textTheme.headline4,
),

counterModel.count شامل مقدار فعلی state می باشد.

زمانی که کاربر روی دکمه + کلیک میکنید باید متد increment اجرا شود تا مقدار جدید به شمارنده اضافه شود.

به همین دلیل متد onPressed را به شکل زیر تغییر میدهیم.

onPressed: () {
  context.read(provider).increment();
},

زمانی که از Riverpod استفاده میکنید BuildContext یک متد جدید به نام read خواهد داشت که provider را پیدا میکند. که در این مثال از نوع کلاس CounterNotifier است.

در حالت نهایی کلاس MyHomePage به شکل زیر باید باشد.

class MyHomePage extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final counterModel = useProvider(provider.state);
    return Scaffold(
      appBar: AppBar(
        title: Text('My Counter App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text(
              '${counterModel.count}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

همانطور که میبینید کل کلاس داخل یک ویجت قرار دارد و با تغیر مقدار شمارنده کل ویجت دوباره ساخته می شود اما ما نیاز داریم فقط ویجت Text دوباره ساخته شود تا مقدار جدید را نمایش دهد به همین کمی این کلاس را تغییر میدهیم.

در انتهای main یک کلاس جدید میسازیم به شکل زیر.

class CounterTextWidget extends HookWidget {
  const CounterTextWidget({Key key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    print('building CounterTextWidget');
    final counterModel = useProvider(provider.state);
    return Text(
      '${counterModel.count}',
      style: Theme.of(context).textTheme.headline4,
    );
  }
}

پس حالا کلاس MyHomeWidget به شکل زیر تغییر میکند.

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('building MyHomePage');
    return Scaffold(
      appBar: AppBar(
        title: Text('My Counter App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            CounterTextWidget(),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read(provider).increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

چون نیازی نیست کل کلاس MyHomePage به تغییرات گوش دهد میتونید از همان StatelessWidget استفاده کنید.

خروجی برنامه در صورت اجرا به شکل زیر است.

استفاده از Riverpod

4 پاسخ به “آموزش مدیریت State با استفاده از Riverpod در فلاتر”

  1. محسن گفت:

    بسیار عالی و کاربردی بود خسته نباشید

  2. Null XD گفت:

    سلام من یه مقاله راجب جداسازی ui و logic خوندم که خیلی به نظرم به برنامه نویس های تازه کار یا اونایی که نیتیو اندروید کار کردن ساده و مفیده و ازین بهتر میشه تو پروژه های متوسط هم استفاده کرد
    میخواستم نظر شما رو هم راجبش بپرسم بنظر من مشکلات رایج رو به نوعی حل کرده
    خوشحال ميشم نظر شما رو هم بدونم. و اینکه خوصیش میکنید یا خیر
    https://blog.gskinner.com/archives/2020/02/flutter-widgetview-a-simple-separation-of-layout-and-logic.html

    • Hesam گفت:

      سلام
      ببینید به اندازه هر برنامه نویسی تو دنیای کامپیوتر الگوی طراحی و معماری میتونه وجود داشته باشه و با هر کدام که فکر میکنیدراحت ترهستید از اون باید استفاده کنید مگر اینکه مدیر پروژه معماری خاصی را در نظر داشته باشد. الگوها و معماری ها روی عملکرد برنامه تاثیری نداره فقط بحث clean code هست.
      تو این الگو هم مدیریت state خاصی انجام نمیشه در واقع مثل حالت عادی هست فقط یک کلاس جدا داخل همون ویجت ایجاد شده که باز ui از logic نمیشه گفت جدا شده.

  3. سعید گفت:

    ممنون مفید بود

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

Hesam
25 اکتبر 2020
آموزش فارسی فلاتر
آموزش فارسی flutter