【Flutter 專題】88 初識狀態管理 Bloc (三)

阿策小和尚發表於2021-07-21

      小菜剛學習了 FlutterBloc 的基本用法,使用的場景還很簡單,主要是單一 Bloc 的應用,今天小菜繼續嘗試多個 Bloc 共用的場景;       小菜繼續完善前兩節的 Demo,新增了隨機變更背景色的功能(並沒有實際意義,僅為了學習新知識點而已);

FlutterBloc

MultiBlocProvider

      對於多個 Bloc 的應用場景,小菜嘗試瞭如下三種方式:

方案一:

      在 build() 外建立和初始化 Bloc;小菜認為這種方式一定程度上擴大了 Bloc 的作用域;

NumberBloc _numBloc = NumberBloc();
ColorBloc _colorBloc = ColorBloc();
  
@override
Widget build(BuildContext context) {
  return BlocBuilder<NumberBloc, int>(
      bloc: _numBloc,
      condition: (previousState, state) {
        print('BlocPage.condition->$previousState==$state');
        return state <= 30 ? true : false;
      },
      builder: (context, count) {
        return BlocBuilder<ColorBloc, Color>(
            bloc: _colorBloc,
            builder: (context, color) {
              return Scaffold(
                  appBar: AppBar(title: Text('Bloc Page'), actions: <Widget>[_settingWid()]),
                  body: Container(color: _colorBloc.state, child: _numberWid()),
                  floatingActionButton: _floatingWid());
            });
      });
}
複製程式碼
方案二:

      通過多個 BlocProvider 巢狀方式對 Bloc 進行建立;小菜認為這種方式巢狀層級較多,略微有一些繁瑣;

@override
Widget build(BuildContext context) {
  return BlocProvider(
      create: (BuildContext context) => _numBloc = NumberBloc(),
      child: BlocBuilder<NumberBloc, int>(
          bloc: _numBloc,
          condition: (previousState, state) {
            print('BlocPage.condition->$previousState==$state');
            return state <= 30 ? true : false;
          },
          builder: (context, count) {
            return BlocProvider(
                create: (BuildContext context) => _colorBloc = ColorBloc(),
                child: BlocBuilder<ColorBloc, Color>(
                    bloc: _colorBloc,
                    builder: (context, color) {
                      return Scaffold(
                          appBar: AppBar(title: Text('Bloc Page'), actions: <Widget>[_settingWid()]),
                          body: Container(color: _colorBloc.state, child: _numberWid()),
                          floatingActionButton: _floatingWid());
                    }));
          }));
}
複製程式碼
方案三:

      便是採用 MultiBlocProvider 聚合繫結方式,作為一個 WidgetBlocProvider 方式聚合建立和初始化,小菜更傾向於這種方式,層級更清晰簡潔;

@override
Widget build(BuildContext context) {
  return MultiBlocProvider(
      providers: [
        BlocProvider(create: (BuildContext context) => _numBloc = NumberBloc()),
        BlocProvider(create: (BuildContext context) => _colorBloc = ColorBloc())
      ],
      child: BlocBuilder<NumberBloc, int>(
          bloc: _numBloc,
          condition: (previousState, state) {
            print('BlocPage.condition->$previousState==$state');
            return state <= 30 ? true : false;
          },
          builder: (context, count) {
            return BlocBuilder<ColorBloc, Color>(
                bloc: _colorBloc,
                builder: (context, color) {
                  return Scaffold(
                      appBar: AppBar(title: Text('Bloc Page'), actions: <Widget>[_settingWid()]),
                      body: Container(color: _colorBloc.state, child: _numberWid()),
                      floatingActionButton: _floatingWid());
                });
          }));
}
複製程式碼

MultiBlocListener

      對於多個 Bloc 的場景,對於其 Bloc 的監聽也可以有多種方式;

方案一:

      對應於 BlocProvider 的方式,小菜合併前兩種,嘗試 listener 巢狀方式進行監聽;

@override
Widget build(BuildContext context) {
  return BlocProvider(
      create: (BuildContext context) => _numBloc = NumberBloc(),
      child: BlocListener<NumberBloc, int>(
          bloc: _numBloc,
          listener: (context, state) => print('BlocListener--->NumberBloc--->$state'),
          child: BlocBuilder<NumberBloc, int>(
              bloc: _numBloc,
              condition: (previousState, state) {
                print('BlocPage.condition->$previousState==$state');
                return state <= 30 ? true : false;
              },
              builder: (context, count) {
                return BlocProvider(
                    create: (BuildContext context) => _colorBloc = ColorBloc(),
                    child: BlocListener<ColorBloc, Color>(
                        bloc: _colorBloc,
                        listener: (context, state) => print('BlocListener--->ColorBloc--->$state'),
                        child: BlocBuilder<ColorBloc, Color>(
                            bloc: _colorBloc,
                            builder: (context, color) {
                              return ScaffoldappBar: AppBar(title: Text('Bloc Page'), actions: <Widget>[_settingWid()]),
                                  body: Container(color: _colorBloc.state, child: _numberWid()),
                                  floatingActionButton: _floatingWid());
                            })));
              })));
}
複製程式碼
方案二:

      MultiBlocListener 作為一個 Widget,將 listener 聚合在一起;效果完全相同,但消除了巢狀提高了 Code 可讀性;

@override
Widget build(BuildContext context) {
  return MultiBlocProvider(
      providers: [
        BlocProvidercreate: (BuildContext context) => _numBloc = NumberBloc()),
        BlocProvider(create: (BuildContext context) => _colorBloc = ColorBloc())
      ],
      child: MultiBlocListener(
          listeners: [
            BlocListener<NumberBloc, int>(listener: (context, state) => print('BlocListener--->NumberBloc--->$state')),
            BlocListener<ColorBloc, Color>(listener: (context, state) => print('BlocListener--->ColorBloc--->$state'))
          ],
          child: BlocBuilder<NumberBloc, int>(
              bloc: _numBloc,
              condition: (previousState, state) {
                print('BlocPage.condition->$previousState==$state');
                return state <= 30 ? true : false;
              },
              builder: (context, count) {
                return BlocBuilder<ColorBloc, Color>(
                    bloc: _colorBloc,
                    builder: (context, color) {
                      return Scaffold(
                          appBar: AppBar(title: Text('Bloc Page'), actions: <Widget>[_settingWid()]),
                          body: Container(color: _colorBloc.state, child: _numberWid()),
                          floatingActionButton: _floatingWid());
                    });
              })));
}
複製程式碼

小感想

      小菜嘗試了 ProviderBloc 兩種狀態管理工具,均是對 Stream 的操作,小菜認為各有各的優勢,不能互相替代;       Bloc 方式最大的優勢是把頁面 UI 與業務邏輯拆分的更清晰,不管是 MVC 或 MVP 方式都更方便的融入應用;Provider 的應用更加簡單,無需考慮拆分的情況;       小菜在瞭解原始碼的時候發現一個有趣的現象,FlutterBloc 也是對 Provider 的一種封裝;

      現在針對狀態管理的方式還有很多其他方式,小菜認為無需強制使用某一種,選擇適合自己對就好;


      FlutterBloc 案例原始碼


      小菜對 Bloc 的嘗試暫時告一個段落,對於更高階的用法在實際應用中再進行嘗試和學習;如有錯誤,請多多指導!

來源: 阿策小和尚

相關文章