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

روش های مدیریت State در فلاتر

0 دیدگاه
فلاتر
10 دقیقه برای مطالعه

روش های مدیریت State در فریمورک هایی چون Flutter و React Native همیشه یکی از داغ ترین بحث هاست و افراد نظرات و شیوه های مختلفی برای انجام این کار دارند.

در این مطلب هدف ما بررسی روش های مدیریت State در فریمورک فلاتر هست.

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

پس توصیه میکنم که همیشه متکی به یک راه و روش یا کتابخانه نباشید و پروژه و نیازمندی های خودتون و بررسی کنید و سپس بهترین گزینه و انتخاب کنید.

تعریف State در فلاتر

اول از همه شاید بهتر باشه برای افرادی که هنوز مدت زمان زیادی نیست که با برنامه نویسی فلاتر آشنا شده اند توضیح بدیم که State به شکل ساده چی هست.

به اطلاعاتی که در طول چرخه حیات یک اپلیکیشن تغییر میکنند State میگیم.

به طور کلی دو نوع State وجود دارد.

نوع اول State های محلی هستند که مختص به یک ویجت هستند و فقط یک ویجت از این اطلاعات استفاده میکند.

نوع دوم App State نام دارد که به اطلاعاتی گفته میشود که تعداد زیادی از ویجت ها از آن استفاده میکنند.

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

چرا نیاز به مدیریت State داریم

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

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

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

  • StatefulWidget
  • StatefulBuilder
  • StreamBuilder
  • InheritedWidget

علاوه بر این ابزارهای مختلف دیگه مثل RxDart و Bloc هم به عنوان موارد خارجی کاربرهای زیادی برای ما دارند.

اگر در یک صفحه از اپلیکیشن خودتون نیاز به تغییرات اطلاعات ندارید و صرفا حالت نمایشی دارند محتویات, میتونید تمام State ها را داخل یک ویجت قرار دهید.

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

State

StatefulWidget

در این ویجت با استفاده از دستور SetState میتونید مقادیر خودتون و هروقت که نیاز بود تغییر بدید.

در واقع اولین و ساده ترین راه برای مدیریت state همین روش می باشد.

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

استفاده از StatefulBuilder میتونه جایگزین بهتری باشه در برخی مواقع.

class MyHomePage2 extends StatelessWidget {

  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return StatefulBuilder(
      builder: (ctx, StateSetter setState) => 
        Scaffold(
          body:Text('$_counter'),
          floatingActionButton: FloatingActionButton(
            onPressed: () => setState(() => _counter++),
          ), 
        )
    );
  }
}

InheritedWidget

این ویجت به تمام ویجت های فرزند خودش یک context اختصاص میده و با استفاده از آن میتونید تغییراتی که نیاز دارید و اعمال کنید.

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

و از روش های دیگه مثل Bloc, Redux, ,Riverpod,Scoped Model استفاده کنند.

 class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: InheritedCounter( child: MyHomePage3() ), // <-- make sure your 
InheritedWidget wraps the widgets that use its data
    );
  }
}


// The InheritedWidget
class InheritedCounter extends InheritedWidget {
  final Map _counter = { 'val': 0 };  // Data structure is a 
map because InheritedWidgets are immutable
  final Widget child;

  InheritedCounter({ this.child }) : super(child: child);

  increment() { 
     _counter['val']++;
  }

  get counter => _counter['val'];

  @override
  bool updateShouldNotify(InheritedCounter oldWidget) => true;

  static InheritedCounter of(BuildContext context) =>
      context.inheritFromWidgetOfExactType(InheritedCounter);
}



class MyHomePage3 extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

   return StatefulBuilder(
      builder: (BuildContext context, StateSetter setState) {

        int counter = InheritedCounter.of(context).counter;
        Function increment = InheritedCounter.of(context).increment;

        return Scaffold(

          body: Text('$counter'),
          floatingActionButton: FloatingActionButton(
            onPressed: () => setState(() => increment()),
          ), 
          
        );
      }
    );
  }
}

StreamBuilder + RxDart BehaviorSubject

این روش یکی از بهترین راه ها برای مدیریت State های global می باشد که انعطاف پذیری بالایی دارد و به خوبی نیازهای شما را رفع می کند.

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

  • دارای یک مقدار ارزش فعلی ( current value) هست که به صورت همروند قابل دسترس است.
  • قابلیت تبدیل به عملگرهای RxDart را دارد.
  • دارای یک جریان (stream) بصورت اشتراکی و broadcast می باشد.

در زیر باهم دیگه به بررسی یک مثال میپردازیم که قصد داریم از عدد صفر شروع به شماردن کنیم.

در واقع مقدار current value را میخونیم و سپس یک عدد به آن اضافه میکنیم.

کلاس Counter بخش منطقی برنامه و ایجاد میکند و همچنین یک state که از همه جا به آن دسترسی داریم.

در واقع در این روش از الگوی Observable استفاده میکنیم و stream$ را به StreamBuilder  پاس میدیم تا هروقت مقداری جدیدی داخل آن قرار گرفت ویجت ما هم آپدیت بشه.

import 'package:rxdart/rxdart.dart';

// Global Variable 
Counter counterService = Counter();

// Data Model
class Counter {

  BehaviorSubject _counter = BehaviorSubject.seeded(0);

  Observable get stream$ => _counter.stream;
  int get current => _counter.value;

  increment() { 
    _counter.add(current + 1);
  }

}

// StreamBuilder Widget
class MyHomePage4 extends StatelessWidget {


  @override
  Widget build(BuildContext context) {

      return Scaffold(
            body: StreamBuilder(
                stream: counterService.stream$,
                builder: (BuildContext context, AsyncSnapshot snap) {
                  return Text('${snap.data}');
                }
            ),

            floatingActionButton: FloatingActionButton(
              onPressed: () => counterService.increment(),
            ), 
      );
  }
}

تنها مشکلی که روش بالا دارد این هست که از یک متغیر سراسری برای اشتراک گذاری state استفاده می کند.

الگوی Bloc

روش BLoC تقریبا شبیه به InheritedWidget می باشد اما مرتب تر و قابل بسط دادن می باشد.

برای این که بتونید راحت تر این نوع الگو را پیاده سازی کنید میتونید از پکیج flutter_bloc استفاده کنید.

روش پیاده سازی تقریبا شبیه به Redux می باشد که شامل موارد زیر هست.

  • تعریف رویداد Events/Actions
  • ایجاد یک کلاس (bloc) که mapEventToState را پیاده سازی می کند و هر زمان که رویدادی رخ داد مقدار State را محاسبه می کند.
  • قرار دادن BlocProvider در ریشه درخت ویجت ها تا تمام فرزندان بتوانند به مقدار آن دسترسی داشته باشند.
  • استفاده از BlockBuilder برای بازسازی ویجت زمانی که State تغییر می کند.
  • استفاده از dispatch برای بررسی رویدادهایی که مقدار state را تغییر می دهند.
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

enum CounterEvent { increment } // 1

class CounterBloc extends Bloc<counterevent, int=""> { // 2
  @override
  int get initialState =&gt; 0;

  @override
  Stream<int> mapEventToState(int currentState, CounterEvent event) async* {
    switch (event) {
      case CounterEvent.increment:
        yield currentState + 1;
        break;
    }
  }
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider<counterbloc>(  // 3
        bloc:CounterBloc(),
        child: MyHomePage5()
      ),
    );
  }
}


class MyHomePage5 extends StatelessWidget {
  

  @override
  Widget build(BuildContext context) {

    final CounterBloc _counterBloc = BlocProvider.of<counterbloc>(context); // 4

      return Scaffold(
        appBar: AppBar(
          title: Text('BLoC'),
        ),
        body: BlocBuilder(  // 5
                bloc: _counterBloc,
                builder: (BuildContext context, int count) {
                  return Text(
                    '${count}',
                  );
                }
              ),
        floatingActionButton: FloatingActionButton(
          onPressed: () =&gt; _counterBloc.dispatch(CounterEvent.increment), // 6
        ), 
      );
  }
}

ابزارهای جانبی

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

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

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

Hesam
24 جولای 2019
آموزش فارسی فلاتر
آموزش فارسی flutter