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

آموزش مدیریت State در فلاتر با الگوی بلاک Bloc + ویدیو

44 دیدگاه
فلاتر
20 دقیقه برای مطالعه

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

در برنامه نویسی فلاتر یکی از الگوهای طراحی پر کاربرد الگوی Bloc می باشد.

قبلا در مورد Bloc به شکل مختصر در مطلب زیر صحبت کرده بودیم.

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

در این مطلب بصورت تئوری با Bloc آشنا میشیم و در ویدیو بالا هم بصورت عملی نحوه پیاده سازی این الگوی برنامه نویسی در فلاتر را بررسی می کنیم.

بلاک چیست؟

در واقع Bloc شکل کوتاه شده Business Logic Components هست که در سال 2018 توسط گوگل به عنوان روشی برای مدیریت State معرفی شد. این الگو به ما اجازه میدهد از یک مکان مشخص به State ها دسترسی داشته باشیم.

برای درک بهتر نحوه کار Bloc میتونید به دیاگرام زیر نگاه کنید تا با روند کار جریان اطلاعات از صفحه رابط کاربری تا بخش دیتا آشنا شوید.

آموزش فلاتر bloc
آموزش bloc فلاتر

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

همانطور هم که احتمالا خودتون متوجه شدید این الگو بسیار شبیه به الگوهای MVVM,MVP هست, برای مثال ViewModel با Bloc جایگزین شده است.

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

در هر زمانی که نیازی به تغییر اطلاعات یا همان State باشد یک رویداد که به آن Event میگوییم از طریق کلاس بلاک ایجاد میشود و براساس نوع رویداد عملیات مورد نظر انجام شده و نتیجه به کلاس State که طراحی کرده ایم ارسال میشود.

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

Stream چیست؟

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

به جریان مداوم از هر چیزی استریم میگوییم. در اینجا هم منظور یک جریان همیشگی از اطلاعات می باشد.

ویژگی های مهم استریم شامل موارد زیر می باشد.

  • StreamController: وظیفه کنترل استریم را برعهده دارد.
  • StreamTransformer: عملیاتی که روی ورودی ها انجام می شود.
  • StreamBuilder: این متد یک استریم به عنوان ورودی میگیرد و با استفاده از بخش builder میتونیم ویجت هایی که میخواهیم در زمان تغییر اطلاعات آپدیت شوند را داخلش قرار دهیم.
  • subscription: مشخص کننده نوع استریم است. هر استریم به دو نوع کلی تقسیم میشود.

    بصورت پیش فرض هر استریم در زمان ساخته شدن از نوع single subscription است یعنی تنها یک آبجکت یا ویجت میتواند در لحظه به تغییرات این استریم گوش دهد یا اطلاعات جدید را دریافت کند.

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

پیاده سازی الگوی بلاک

در این ویدیو باهمدیگه یاد میگیریم که چگونه از طریق کتابخانه های bloc و flutter_bloc برای پیاده سازی الگوی بلاک کمک بگیریم.

با استفاده از یک پروژه عملی نحوه کار Bloc و به صورت کامل بررسی میکنیم.

پیوست: در ویدیو بالا در انتهای پروژه در فایل main کد زیر را قرار دهید.

@override
  void dispose() {
    _bloc.dispose();
  }

حتما در متد dispose باید آبجکت بلاک خودمون را dispose کنیم.


شروع کار با بلاک

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

flutter pub add flutter_bloc
flutter pub add bloc

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

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

مثالی که بررسی میکنیم مشابه پروژه پیشفرض فلاتر است که با کلیک کردن روی یک دکمه مقدار عدد مورد نظر افزایش پیدا میکند.

abstract class CounterEvent {}

class Increment extends CounterEvent {}

کلاس CounterEvent کلاس رویداد اصلی ما است و باقی رویدادهای ما نیز باید از این کلاس ارث بری کنند. البته رویدادهایی که برای کلاس بلاک در ادامه خواهیم ساخت. هر صفحه از اپلیکیشن میتواند شامل یک کلاس بلاک و رویداد جدا باشد.

هر زمان که نیاز بود یک مقدار به عدد مورد نظر اضافه کنیم این رویداد را صدا خواهیم زد.

در ادامه کلاس bloc برنامه را ایجاد میکنیم.

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<Increment>((event, emit) => emit(state + 1));
  }
}

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

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

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

در ادامه یک متد ایجاد میکنیم که نوع آن برابر است با نام رویدادی که پیش تر ساخته بودیم. این متد هربار که این رویداد صدا زده شود اجرا میشود و وظیفه آن اضافه کردن یک واحد به State میباشد.

اما نوبت به نحوه استفاده از این کلاس رسیده است.

Future<void> main() async {
  final bloc = CounterBloc();


  bloc.add(Increment());

  await Future.delayed(Duration.zero);

  print(bloc.state); 

  await bloc.close();
}

ابتدا نیاز است تا یک نمونه از کلاس بلاک ایجاد کینم.

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

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

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

BlocBuilder<CounterBloc, int>(
  builder: (context, state) {
    return Text('Count: $state');
  },
)

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

سپس در متد builder طراحی مورد نظر خود را انجام میدهیم که برای این بخش یک ویجت Text قرار داده ایم.

طراحی کلاس State

بله، برای مدیریت وضعیت در الگوی BLoC در فلاتر، معمولاً از کلاس جداگانه‌ای برای نگهداری وضعیت (state) استفاده می‌شود. در اینجا یک مثال از ایجاد یک کلاس جداگانه برای نگهداری وضعیت برای یک نمونه ساده از شمارنده (counter) آورده شده است:

import 'package:equatable/equatable.dart';

abstract class CounterState extends Equatable {
  final int count;

  const CounterState(this.count);

  @override
  List<Object?> get props => [count];
}

class InitialCounterState extends CounterState {
  const InitialCounterState(int count) : super(count);
}

class IncrementedCounterState extends CounterState {
  const IncrementedCounterState(int count) : super(count);
}

class DecrementedCounterState extends CounterState {
  const DecrementedCounterState(int count) : super(count);
}

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

به عنوان مثال، وضعیت اولیه برای شروع و وضعیت‌هایی برای افزایش و کاهش شمارنده.

حالا که کلاس State تعریف شد، می‌توانیم از آن در کلاس BLoC استفاده کنیم:

import 'package:bloc/bloc.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(InitialCounterState(0));

  @override
  Stream<CounterState> mapEventToState(CounterEvent event) async* {
    if (event is IncrementEvent) {
      yield IncrementedCounterState(state.count + 1);
    } else if (event is DecrementEvent) {
      yield DecrementedCounterState(state.count - 1);
    }
  }
}

همچنان نیاز به تعریف رویدادها یا همان Eventها داریم:

abstract class CounterEvent {}

class IncrementEvent extends CounterEvent {}

class DecrementEvent extends CounterEvent {}

در ویو، از BLoC استفاده می‌کنیم تا وضعیت را مدیریت کنیم و به تغییرات وضعیت واکنش نشان دهیم:

BlocBuilder<CounterBloc, CounterState>(
  builder: (context, state) {
    return Text('Count: ${state.count}');
  },
)

این روش به شما امکان می‌دهد که وضعیت برنامه را با استفاده از کلاس‌های جداگانه مدیریت کنید و از مزایای الگوی BLoC برای مدیریت وضعیت در فلاتر بهره‌برداری کنید.

در بخش BlocBuilder از کتابخانه flutter_bloc، شما می‌توانید بر اساس وضعیت‌های مختلف که از BLoC دریافت می‌کنید، ویجت‌های مختلف فلاتر را نمایش دهید.

برای این کار، می‌توانید از builder که به عنوان یک تابع برگشتی به BlocBuilder پاس داده می‌شود، استفاده کنید و بر اساس وضعیت‌های مختلف ویجت‌های متفاوتی ایجاد کنید. در زیر، یک مثال از چگونگی انجام این کار آمده است:

BlocBuilder<CounterBloc, CounterState>(
  builder: (context, state) {
    if (state is InitialCounterState) {
      return Column(
        children: [
          Text('Count: ${state.count}'),
          ElevatedButton(
            onPressed: () {
              // Dispatch an event to increment the counter
              context.read<CounterBloc>().add(IncrementEvent());
            },
            child: Text('Increment'),
          ),
        ],
      );
    } else if (state is IncrementedCounterState) {
      return Column(
        children: [
          Text('Count: ${state.count}'),
          ElevatedButton(
            onPressed: () {
              // Dispatch an event to decrement the counter
              context.read<CounterBloc>().add(DecrementEvent());
            },
            child: Text('Decrement'),
          ),
        ],
      );
    } else if (state is DecrementedCounterState) {
      return Column(
        children: [
          Text('Count: ${state.count}'),
          ElevatedButton(
            onPressed: () {
              // Dispatch an event to increment the counter
              context.read<CounterBloc>().add(IncrementEvent());
            },
            child: Text('Increment'),
          ),
        ],
      );
    }
    return Container(); .
  },
)

پیاده سازی الگوی Cubit در فلاتر

پکیج بلاک فلاتر شامل روش دیگری برای مدیریت حالت به نام Cubit نیز میشود.

Cubit و BLoC هر دو الگوهای معماری برای مدیریت وضعیت (state management) در فریم‌ورک فلاتر هستند، اما دارای تفاوت‌های مهمی هستند:

  1. معماری و نحوه کارکرد:
    • BLoC (Business Logic Component): BLoC یک الگوی معماری کاملتر است که علاوه بر مدیریت وضعیت، برای مدیریت رویدادها و ارتباط با سرویس‌ها و منابع داده نیز استفاده می‌شود. BLoC از Stream یا RxDart برای ارسال و دریافت داده‌ها استفاده می‌کند.
    • Cubit: Cubit یک سیستم ساده‌تر و کوچکتر از BLoC است.
      معمولاً برای مدیریت وضعیت استفاده می‌شود و فقط یک مقدار وضعیت را تعیین می‌کند. Cubit از Stream استفاده نمی‌کند و به جای آن با استفاده از emit مقدار وضعیت را تغییر می‌دهد.

  2. کدگذاری و پیچیدگی:
    • BLoC: BLoC به عنوان یک الگوی معماری کامل برای برنامه‌های پیچیده تر مناسب است. این الگو به شما امکان می‌دهد که منطق کسب و کار را از ویو جدا کنید و مدیریت دقیق‌تری بر روی وضعیت و رویدادها داشته باشید. این امکان را داراست که چندین BLoC در برنامه‌تان داشته باشید.
    • Cubit: Cubit به عنوان یک راه حل ساده‌تر و کوچکتر برای برنامه‌های کوچکتر و ساده‌تر مناسب است. این به شما کمک می‌کند که به سادگی وضعیت را مدیریت کنید و از پیچیدگی‌های اضافی در برنامه خود دوری بیافتید.
  3. کتابخانه‌های مورد نیاز:
    • BLoC: BLoC برای مدیریت وضعیت و رویدادها معمولاً از کتابخانه‌هایی مانند bloc و flutter_bloc استفاده می‌کند.
    • Cubit: Cubit معمولاً از کتابخانه flutter_bloc برای مدیریت وضعیت استفاده می‌کند، اما به صورت کلی کوچکتر و ساده‌تر از BLoC است و نیاز به کتابخانه‌های کمتری دارد.

به طور خلاصه، اگر برنامه‌تان کوچک و ساده باشد، Cubit ممکن است گزینه‌ی بهتری باشد، اما اگر برنامه‌تان پیچیده‌تر باشد و نیاز به مدیریت دقیق‌تر وضعیت و رویدادها داشته باشید، BLoC یک گزینه قدرتمندتر و انعطاف‌پذیرتر است. انتخاب بین این دو به نیاز‌ها و پیچیدگی برنامه‌تان بستگی دارد.

در مثال زیر الگوی Cubit را پیاده سازی میکنیم.

ابتدا یک Cubit برای مدیریت وضعیت ایجاد کنید:

import 'package:flutter_bloc/flutter_bloc.dart';

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);

  void decrement() => emit(state - 1);
}

در ویو، BlocProvider را به عنوان والد ویجت‌ها ایجاد کنید و CounterCubit را در آن قرار دهید:

BlocBuilder<CounterCubit, int>(
  builder: (context, state) {
    return Column(
      children: [
        Text('Count: $state'),
        ElevatedButton(
          onPressed: () {
            context.read<CounterCubit>().increment();
          },
          child: Text('Increment'),
        ),
        ElevatedButton(
          onPressed: () {
            context.read<CounterCubit>().decrement();
          },
          child: Text('Decrement'),
        ),
      ],
    );
  },
)

در این روش هم همانند حالت قبل پیشنهاد میشود که یک کلاس جداگانه برای State ایجاد کنید. روش استفاده مشابه همدیگر است.

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

روش بلاک در ترکیب با معماری تمیز Clean Architecture در فلاتر نیز در بسیاری از پروژه ها استفاده میشود.

44 پاسخ به “آموزش مدیریت State در فلاتر با الگوی بلاک Bloc + ویدیو”

  1. سامسون گفت:

    سلام خسته نباشید
    مرسی برای این ویدیوی آموزشی
    خیلی قشنگ توضیح دادید واقعا خسته نباشید
    لطفا تو یک ویدیو دیگه آموزش بدید که چطور می تونیم داخل یک bloc چندین state داشته باشیم
    ممنونم

    • Hesam گفت:

      سلام
      ممنون, حتما ویدیو های بیشتری در آینده گذاشته میشه.
      شما میتونید بجای تعریف یدونه فیلد int current چندین نوع فیلد دیگه هم تعریف کنید.

    • امین گفت:

      سلام
      میشه ی مثال هم وقتی که ریپازیتوری لوکال و ریموت داریم هم با bloc بذارید
      ممنون

  2. سعید گفت:

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

  3. سعید گفت:

    سلام بهترین دیزاین بترن برای فلاتر bloc هست؟؟

  4. amirkho_ir گفت:

    سپاس

  5. محمدرضا گفت:

    سلام خیلی ممنون واقعا قشنگ توضیح میدید خلاصه ومفید فقط اگه ممکنه با ورژن جدید یه مثال حتی اگه شده متن نه ویدیو بذارید چون تو ورژن جدید تو قسمت bloc عوض شده
    Stream mapEventToState(
    UserEvent event,
    ) async* {}
    همینطور که میبینید کنار event نمیشه currentState گذاشت تا بهشون دسترسی داشته باشیم

    • Hesam گفت:

      سلام
      تو دوره جدیدی که در حال ضبط هست از ورژن 1.0 بلاک استفاده شده و تمام این ها بررسی میشه, برای بقیه دوستان هم در همین پست یا پست جدیدی نحوه کار کردن با ورژن جدید و حتما آموزش خواهیم داد.

  6. علی گفت:

    چقدر خوب میشه که کد مثال رو روی گیت قرار بدین

  7. mr.da گفت:

    سلام مهندس حسام.
    لینک ویدیو مشکل داره و باز نمیشه…
    من هر 2 دوره ی آموزشیتون رو دیدم خیلی خوب بود
    لطفا یک دوره اختصاصی فقط در مورد طراحی اپلیکیشن با bloc و RxDart و Dio بزارین که نحوه صحیح کار با stream و ارتباط اینترنتی رو در معماری bloc یاد بگیریم…
    مشکل لینک ویدیو رو هم رفع کنید …
    ممنون

  8. taha گفت:

    سلام
    ممنون بخاطر آموزش خوبتون اگه امکانش هست مثال هایی پیشرفته تر با event بیشتر در مورد Bloc قرار بدید

    • Hesam گفت:

      سلام خواهش میکنم, برای آموزش های بیشتر درباره بلاک میتونید از دوره “طراحی اپلیکیشن وردپرس استفاده کنید”

  9. Amir Hosseinzadeh گفت:

    سلام دوست عزیز
    بسیار بسیار سپاسگزارم از توضیحات و آموزش این بحث
    نکته ای که دارم اینکه در این زمان که من دارم این موضوع را بررسی می کنم فکر میکنم (البته مطمعن نیستم) احتمالا تغییراتی در ساختار فلاتر بوجود اومده که دیگه تابع mapEventToState پارامتر State را قبول نمی کنه و فقط مجاز به دریافت Event هست اگر اینطوره لطفا در تغییرات احتمالی را بشکلی مشخص کنید.

    با سپاس
    امیر
    http://www.amirhome.com

  10. parisa گفت:

    kheili khub bud mamnun

  11. peyman گفت:

    سلام استاد واقعا مفید اگه بتونید یه ویدیو از bloc با سرویس بزارید ممنون میشم یا یه توضیح بدید ممنوم میشم چون واقعا اموزشی از این نیست به زبان فارسی

    • Hesam گفت:

      سلام, داخل دوره وردپرس بصورت کامل در قالب یک پروژه عملی الگوی bloc پیاده سازی شده است.

  12. مهدی نریمانی گفت:

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

  13. Nasrin گفت:

    سلام مشتاقانه منتظر قسمت های بعدی این آموزش هستم که بتوانم api ها را با استفاده از بلاک پیاده سازی کنم.
    اگر سایت این امکان را داشته باشد که انتشار ویدیوهای جدید رو توسط ایمیل یا پیامک بهمون اطلاع بده عالی میشد.

    • Hesam گفت:

      سلام داخل کانال تلگرام پست های جدید اطلاع رسانی میشوند.
      برای کار با api میتونید به آموزش Rxdart مراجعه کنید

  14. پریسا عالی پژوه گفت:

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

    • Hesam گفت:

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

  15. صالح گفت:

    ببخشید تو ابدیت جدید بلاک چجوری میشه بلاک رو پیاده کرد

    • Hesam گفت:

      خود مستندات نسخه جدید پکیج کامل هست با بررسی مثال ها متوجه میشید ولی به زودی یه آموزش با نسخه جدید 6 داخل سایت قرار میدم.

  16. NULL XD گفت:

    نظرتون راجب معماری widgetView چیه من به بلاک ترجیهش میدم.
    https://blog.gskinner.com/archives/2020/02/flutter-widgetview-a-simple-separation-of-layout-and-logic.html

  17. مهدی هستم گفت:

    ممنون از مطلب زیباتون ،یادگیری فلاتر با زبان شیرین فارسی خیلی میچسبه 🙂

  18. همون مهدی قبلی یه گفت:

    من یه شرکتی کار میکردم ، اونجا از GetX استفاده میکردیم ، جدیدا خیلی پرکاربرد شده و خیلی جذابه
    https://pub.dev/packages/get

  19. مینا گفت:

    سلام. سوالم اینه که بهترین معماری برای فلاتر چه معماری ای هست؟
    bloc
    provider
    MVC
    یا چی؟

    • Hesam گفت:

      سلام
      bloc, provider معماری مخصوص مدیریت state میباشند و mvc معماری نرم افزار هست بهترین حالت ترکیب معماری مدیریت state با یکی از معماری های نرم افزار است مثل mvc,mvp,mvvm

  20. محمدرضا گفت:

    سلام معماری BLOCK همون provider هست یا اینها باهم فرق دارند؟

  21. سالار گفت:

    Bloc آیا دیزاین پترنه دوست عزیز؟ Bloc نوعی معماری است نه دیزاین پترن. هرچند این مورد از محتوای ویدیو کم نمیکنه اما اصطلاح بکار برده شده اشتباهه.

  22. مهدی گفت:

    با سلام
    بسیار جامع و کامل
    ممنون از زحمتت

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

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

Hesam
29 سپتامبر 2019
آموزش فارسی فلاتر
آموزش فارسی flutter