flutter_bloc 使用将从下图的三个维度说明
前言
- 首先,有很多的文章在说 flutter bloc 模式的应用,但是百分之八九十的文章都是在说,使用 StreamController+StreamBuilder 搭建 bloc,提升性能的会加上 InheritedWidget,这些文章看了很多,真正写使用 bloc 作者开发的 flutter_bloc 却少之又少。没办法,只能去 bloc 的 github 上去找使用方式,最后去 bloc 官网翻文档。 
- 蛋痛,各位叼毛,就不能好好说说 flutter_bloc 的使用吗?非要各种抄 bloc 模式提出作者的那俩篇文章。现在,搞的杂家这个伸手党要自己去翻文档总结(手动滑稽)。 
项目效果(建议 PC 浏览器打开)
下面是 Flutter_Bloc 历程的一系列链接
问题
初次使用 flutter_bloc 框架,可能会有几个疑问
准备工作
说明
引用
库
 flutter_bloc: ^6.1.1 #状态管理框架equatable: ^1.2.3 #增强组件相等性判断
   复制代码
 
插件
在 Android Studio 设置的 Plugins 里,搜索:Bloc
安装重启下,就 OK 了
Bloc 范例
效果
初始化代码
来看下这三个生成的 bloc 文件:main_bloc,main_event,main_state
 class MainBloc extends Bloc<MainEvent, MainState> {  MainBloc() : super(MainInitial());
  @override  Stream<MainState> mapEventToState(    MainEvent event,  ) async* {    // TODO: implement mapEventToState  }}
   复制代码
 
 @immutableabstract class MainEvent {}
   复制代码
 
 @immutableabstract class MainState {}
class MainInitial extends MainState {}
   复制代码
 实现
 void main() {  runApp(MyApp());}
class MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return MaterialApp(      home: MainPage(),    );  }}
   复制代码
 
 class MainBloc extends Bloc<MainEvent, MainState> {  MainBloc() : super(MainState(selectedIndex: 0, isExtended: false));
  @override  Stream<MainState> mapEventToState(MainEvent event) async* {    ///main_view中添加的事件,会在此处回调,此处处理完数据,将数据yield,BlocBuilder就会刷新组件    if (event is SwitchTabEvent) {      ///获取到event事件传递过来的值,咱们拿到这值塞进MainState中      ///直接在state上改变内部的值,然后yield,只能触发一次BlocBuilder,它内部会比较上次MainState对象,如果相同,就不build      yield MainState()        ..selectedIndex = event.selectedIndex        ..isExtended = state.isExtended;    } else if (event is IsExtendEvent) {      yield MainState()        ..selectedIndex = state.selectedIndex        ..isExtended = !state.isExtended;    }  }}
   复制代码
 
 @immutableabstract class MainEvent extends Equatable{  const MainEvent();}///切换NavigationRail的tabclass SwitchTabEvent extends MainEvent{  final int selectedIndex;
  const SwitchTabEvent({@required this.selectedIndex});
  @override  List<Object> get props => [selectedIndex];}///展开NavigationRail,这个逻辑比较简单,就不用传参数了class IsExtendEvent extends MainEvent{  const IsExtendEvent();
  @override  List<Object> get props => [];}
   复制代码
 
- main_state:state 有很多种写法,在 bloc 官方文档上,不同项目 state 的写法也很多 
- 这边变量名可以设置为私用,用 get 和 set 可选择性的设置读写权限,因为我这边设置的俩个变量全是必用的,读写均要,就设置公有类型,不用下划线“_”去标记私有了。 
- 对于生成的模板代码,我们在这:去掉 @immutable 注解,去掉 abstract; 
- 这里说下加上 @immutable 和 abstract 的作用,这边是为了标定不同状态,这种写法,会使得代码变得更加麻烦,用 state 不同状态去标定业务事件,代价太大,这边用一个变量去标定,很容易轻松代替 
 class MainState{   int selectedIndex;   bool isExtended;     MainState({this.selectedIndex, this.isExtended});}
   复制代码
 
 class MainPage extends StatelessWidget {  @override  Widget build(BuildContext context) {    return _buildBg(children: [      //侧边栏      _buildLeftNavigation(),
      //右边主体内容      Expanded(child: Center(        child: BlocBuilder<MainBloc, MainState>(builder: (context, state) {          return Text(            "选择Index:" + state.selectedIndex.toString(),            style: TextStyle(fontSize: 30.0),          );        }),      ))    ]);  }
  Widget _buildBg({List<Widget> children}) {    ///创建BlocProvider的,表明该Page,我们是用MainBloc,MainBloc是属于该页面的Bloc了    return BlocProvider(      create: (BuildContext context) => MainBloc(),      child: Scaffold(        appBar: AppBar(title: Text('Bloc')),        body: Row(children: children),      ),    );  }
  //增加NavigationRail组件为侧边栏  Widget _buildLeftNavigation() {    return BlocBuilder<MainBloc, MainState>(builder: (context, state) {      return NavigationRail(        backgroundColor: Colors.white,        elevation: 3,        extended: state.isExtended,        labelType: state.isExtended            ? NavigationRailLabelType.none            : NavigationRailLabelType.selected,        //侧边栏中的item        destinations: [          NavigationRailDestination(            icon: Icon(Icons.add_to_queue),            selectedIcon: Icon(Icons.add_to_photos),            label: Text("测试一"),          ),          NavigationRailDestination(            icon: Icon(Icons.add_circle_outline),            selectedIcon: Icon(Icons.add_circle),            label: Text("测试二"),          ),          NavigationRailDestination(            icon: Icon(Icons.bubble_chart),            selectedIcon: Icon(Icons.broken_image),            label: Text("测试三"),          ),        ],        //顶部widget        leading: _buildNavigationTop(),        //底部widget        trailing: _buildNavigationBottom(),        selectedIndex: state.selectedIndex,        onDestinationSelected: (int index) {          ///添加切换tab事件          BlocProvider.of<MainBloc>(context)              .add(SwitchTabEvent(selectedIndex: index));        },      );    });  }
  Widget _buildNavigationTop() {    return Center(      child: Padding(        padding: const EdgeInsets.all(8.0),        child: Container(          width: 80,          height: 80,          decoration: BoxDecoration(            shape: BoxShape.circle,            image: DecorationImage(              image: NetworkImage(                "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3383029432,2292503864&fm=26&gp=0.jpg",              ),              fit: BoxFit.fill,            ),          ),        ),      ),    );  }
  Widget _buildNavigationBottom() {    return Container(      child: BlocBuilder<MainBloc, MainState>(        builder: (context, state) {          return FloatingActionButton(            onPressed: () {              ///添加NavigationRail展开,收缩事件              BlocProvider.of<MainBloc>(context).add(IsExtendEvent());            },            child: Icon(state.isExtended ? Icons.send : Icons.navigation),          );        },      ),    );  }}
   复制代码
 Bloc 范例优化
反思
从上面的代码来看,实际存在几个隐式问题,这些问题,刚开始使用时候,没异常的感觉,但是使用 bloc 久了后,感觉肯定越来越强烈
- state 问题 
- 初始化问题:这边初始化是在 bloc 里,直接在构造方法里面赋初值的,state 中一旦变量多了,还是这么写,会感觉极其难受,不好管理。需要优化 
- 可以看见这边我们只改动 selectedIndex 或者 isExtended;另一个变量不需要变动,需要保持上一次的数据,进行了此类:state.selectedIndex 或者 state.isExtended 赋值,一旦变量达到十几个乃至几十个,还是如此写,是让人极其崩溃的。需要优化 
- bloc 问题 
- 如果进行一个页面,需要进行复杂的运算或者请求接口后,才能知晓数据,进行赋值,这里肯定需要一个初始化入口,初始化入口需要怎样去定义呢? 
插件
因为官方插件生成的写法,和调整后写法差距有点大,而且官方插件不支持生成 view 层和相关设置,此处我就撸了一个插件,完善了相关功能
请注意,wrap 代码和提示代码片段,参靠了官方插件规则
Wrap Widget 规则来着:intellij_generator_plugin
快捷代码生成规则来着: intellij_generator_plugin
优化实现
这边完整走一下流程,让大家能有个完整的思路
- state:首先来看看我们对 state 中的优化,这边进行了俩个很重要优化,增加俩个方法:init()和 clone() 
- init():这里初始化统一用 init()方法去管理 
- clone():这边克隆方法,是非常重要的,一旦变量达到俩位数以上,就能深刻体会该方法是多么的重要 
 class MainState {  int selectedIndex;  bool isExtended;
  ///初始化方法,基础变量也需要赋初值,不然会报空异常  MainState init() {    return MainState()      ..selectedIndex = 0      ..isExtended = false;  }
  ///clone方法,此方法实现参考fish_redux的clone方法  ///也是对官方Flutter Login Tutorial这个demo中copyWith方法的一个优化  ///Flutter Login Tutorial(https://bloclibrary.dev/#/flutterlogintutorial)  MainState clone() {    return MainState()      ..selectedIndex = selectedIndex      ..isExtended = isExtended;  }}
   复制代码
 
 @immutableabstract class MainEvent {}
///初始化事件,这边目前不需要传什么值class MainInitEvent extends MainEvent {}
///切换NavigationRail的tabclass SwitchTabEvent extends MainEvent {  final int selectedIndex;
  SwitchTabEvent({@required this.selectedIndex});}
///展开NavigationRail,这个逻辑比较简单,就不用传参数了class IsExtendEvent extends MainEvent {}
   复制代码
 
- bloc 
- 这增加了初始化方法,请注意,如果需要进行异步请求,同时需要将相关逻辑提炼一个方法,咱们在这里配套 Future 和 await 就能解决在异步场景下同步数据问题 
- 这里使用了克隆方法,可以发现,我们只要关注自己需要改变的变量就行了,其它的变量都在内部赋值好了,我们不需要去关注;这就大大的便捷了页面中有很多变量,只需要变动一俩个变量的场景 
- 注意:如果变量的数据未改变,界面相关的 widget 是不会重绘的;只会重绘变量被改变的 widget 
 class MainBloc extends Bloc<MainEvent, MainState> {  MainBloc() : super(MainState().init());
  @override  Stream<MainState> mapEventToState(MainEvent event) async* {    ///main_view中添加的事件,会在此处回调,此处处理完数据,将数据yield,BlocBuilder就会刷新组件    if (event is MainInitEvent) {      yield await init();    } else if (event is SwitchTabEvent) {      ///获取到event事件传递过来的值,咱们拿到这值塞进MainState中      ///直接在state上改变内部的值,然后yield,只能触发一次BlocBuilder,它内部会比较上次MainState对象,如果相同,就不build      yield switchTap(event);    } else if (event is IsExtendEvent) {      yield isExtend();    }  }
  ///初始化操作,在网络请求的情况下,需要使用如此方法同步数据  Future<MainState> init() async {    return state.clone();  }
  ///切换tab  MainState switchTap(SwitchTabEvent event) {    return state.clone()..selectedIndex = event.selectedIndex;  }
  ///是否展开  MainState isExtend() {    return state.clone()..isExtended = !state.isExtended;  }}
   复制代码
 
- view 
- view 层代码太多,这边只增加了个初始化事件,就不重新把全部代码贴出来了,初始化操作直接在创建的时候,在 XxxBloc 上使用 add()方法就行了,就能起到进入页面,初始化一次的效果;add()方法也是 Bloc 类中提供的,遍历事件的时候,就特地检查了 add()这个方法是否添加了事件;说明,这是框架特地提供了一个初始化的方法 
- 这个初始化方式是在官方示例找到的 
- 项目名:Flutter Infinite List Tutorial 
- 项目地址:flutter-infinite-list-tutorial
 
 
 class MainPage extends StatelessWidget {  ...         Widget _buildBg({List<Widget> children}) {    ///创建BlocProvider的,表明该Page,我们是用MainBloc,MainBloc是属于该页面的Bloc了    return BlocProvider(      create: (BuildContext context) => MainBloc()..add(MainInitEvent()),      child: Scaffold(        appBar: AppBar(title: Text('Bloc')),        body: Row(children: children),      ),    );  }      ///下方其余代码省略...........}
   复制代码
 搞定
- OK,经过这样的优化,解决了几个痛点。实际在 view 中反复是要用 BlocBuilder 去更新 view,写起来有点麻烦,这里我们可以写一个,将其中 state 和 context 变量,往提出来的 Widget 方法传值,也是蛮不错的 
- 大家保持观察者模式的思想就行了;观察者(回调刷新控件)和被观察者(产生相应事件,添加事件,去通知观察者),bloc 层是处于观察者和被观察者中间的一层,我们可以在 bloc 里面搞业务,搞逻辑,搞网络请求,不能搞基;拿到 Event 事件传递过来的数据,把处理好的、符合要求的数据返回给 view 层的观察者就行了。 
- 使用框架,不拘泥框架,在观察者模式的思想上,灵活的去使用 flutter_bloc 提供 Api,这样可以大大的缩短我们的开发时间! 
Cubit 范例
创建
新建好后,他会生成三个文件:cubit,state,view;来看下生成的代码
模板代码
 class CounterCubit extends Cubit<CounterState> {  CounterCubit() : super(CounterState().init());}
   复制代码
 
 class CounterState {  CounterState init() {    return CounterState();  }
  CounterState clone() {    return CounterState();  }}
   复制代码
 
 class CounterPage extends StatelessWidget {  final cubit = CounterCubit();
  @override  Widget build(BuildContext context) {    return BlocProvider(      create: (BuildContext context) => cubit,      child: Container(),    );  }}
   复制代码
 实现计时器
效果
实现
实现很简单,三个文件就搞定,看下流程:state ->  cubit -> view
 class CounterState {  int count;
  CounterState init() {    return CounterState()..count = 0;  }
  CounterState clone() {    return CounterState()..count = count;  }}
   复制代码
 
- cubit 
- 这边加了个自增方法:increase() 
- event 层实际是所有行为的一种整合,方便对逻辑过于复杂的页面,所有行为的一种维护;但是过于简单的页面,就那么几个事件,还单独维护,就没什么必要了 
- 在 cubit 层写的公共方法,在 view 里面能直接调用,更新数据使用:emit() 
- cubit 层应该可以算是:bloc 层和 event 层一种结合后的简写 
 class CounterCubit extends Cubit<CounterState> {  CounterCubit() : super(CounterState().init());
  ///自增  void increase() => emit(state.clone()..count = ++state.count);}
   复制代码
 
 class CounterPage extends StatelessWidget {  final cubit = CounterCubit();
  @override  Widget build(BuildContext context) {    return BlocProvider(      create: (BuildContext context) => cubit,      child: Scaffold(        appBar: AppBar(title: const Text('Cubit范例')),        body: Center(          child: BlocBuilder<CounterCubit, CounterState>(            builder: (context, state) {              return Text(                '点击了 ${state.count} 次',                style: TextStyle(fontSize: 30.0),              );            },          ),        ),        floatingActionButton: FloatingActionButton(          onPressed: () => cubit.increase(),          child: const Icon(Icons.add),        ),      ),    );  }}
   复制代码
 总结
在 Bloc 模式里面,如果页面不是过于复杂,使用 Cubit 去写,基本完全够用了;但是如果业务过于复杂,还是需要用 Bloc 去写,需要将所有的事件行为管理起来,便于后期维护
OK,Bloc 的简化模块,Cubit 模式就这样讲完了,对于自己业务写的小项目,我就经常用这个 Cubit 去写
全局 Bloc
说明
什么是全局 Bloc?
- BlocProvider 介绍里面有这样的形容:BlocProvider should be used to create new blocs which will be made available to the rest of the subtree(BlocProvider 应该被用于创建新的 Bloc,这些 Bloc 将可用于其子树) 
- 这样的话,我们只需要在主入口地方使用 BlocProvider 创建 Bloc,就能使用全局的 XxxBloc 了,这里的全局 XxxBloc,state 状态都会被保存的,除非关闭 app,否则 state 里面的数据都不会被还原! 
- 注意:在主入口创建的 XxxBloc,在主入口处创建了一次,在其它页面均不需要再次创建,在任何页面只需要使用 BlocBuilder,便可以定点刷新及其获取全局 XxxBloc 的 state 数据 
使用场景
- 全局的主题色,字体样式和大小等等全局配置更改;这种情况,在需要全局属性的地方,使用 BlocBuilder 对应的全局 XxxBloc 泛型去刷新数据就行了 
- 跨页面去调用事件,既然是全局的 XxxBloc,这就说明,我们可以在任何页面,使用-  BlocProvider.of<XxxBloc>(context)调用全局 XxxBloc 中事件,这就起到了一种跨页面调用事件的效果
 
- 使用全局 Bloc 做跨页面事件时,应该明白,当你关闭 Bloc 对应的页面,对应全局 Bloc 中的并不会被回收,下次进入页面,页面的数据还是上次退出页面修改的数据,这里应该使用 StatefulWidget,在 initState 生命周期处,初始化数据;或者在 dispose 生命周期处,还原数据源 
- 思考下:全局 Bloc 对象存在周期是在整个 App 存活周期,必然不能创建过多的全局 Bloc,跨页面传递事件使用全局 Bloc 应当只能做折中方案 
效果图
使用
来看下怎么创建和使用全局 Bloc 吧!
 void main() {  runApp(MyApp());}
class MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return MaterialApp(      home: MainPage(),      builder: (BuildContext context, Widget child) {        return MultiBlocProvider(          providers: [            ///此处通过BlocProvider创建的Bloc或者Cubit是全局的            BlocProvider<SpanOneCubit>(              create: (BuildContext context) => SpanOneCubit(),            ),          ],          child: child,        );      },    );  }}
   复制代码
 
需要用俩个 Bloc 模块来演示,这里分别用SpanOneCubit和SpanTwoCubit来演示,其中SpanOneCubit是全局的
SpanOneCubit
 class SpanOneState {  int count;
  ///初始化方法  SpanOneState init() {    return SpanOneState()..count = 0;  }
  ///克隆方法,针对于刷新界面数据  SpanOneState clone() {    return SpanOneState()..count = count;  }}
   复制代码
 
- view 
- 这个页面仅仅是展示计数变量的变化,因为在主入口使用了- BlocProvider创建了- SpanOneCubit,所以在这个页面不需要再次创建,直接使用- BlocBuilder便可以获取其 state
 
- 可以发现,这个页面使用了 StatefulWidget,在 initState 周期中,初始化了数据源;这样,每次进入页面,数据源就不会保存为上一次改动的来,都会被初始化为我们想要的值;这个页面能接受到任何页面调用其事件,这样就实现类似于广播的一种效果(- 伪)
 
 class CubitSpanOnePage extends StatefulWidget {  @override  _SpanOnePageState createState() => _SpanOnePageState();}
class _SpanOnePageState extends State<CubitSpanOnePage> {  @override  void initState() {    BlocProvider.of<BlocSpanOneCubit>(context).init();    super.initState();  }
  @override  Widget build(BuildContext context) {    return Scaffold(      backgroundColor: Colors.white,      appBar: AppBar(title: Text('跨页面-One')),      floatingActionButton: FloatingActionButton(        onPressed: () => BlocProvider.of<BlocSpanOneCubit>(context).toSpanTwo(),        child: const Icon(Icons.arrow_forward_outlined),      ),      body: Center(        child: BlocBuilder<BlocSpanOneCubit, BlocSpanOneState>(          builder: (context, state) {            return Text(              'SpanTwoPage点击了 ${state.count} 次',              style: TextStyle(fontSize: 30.0),            );          },        ),      ),    );  }}
   复制代码
 
 class SpanOneCubit extends Cubit<SpanOneState> {  SpanOneCubit() : super(SpanOneState().init());
  void init() {    emit(state.init());  }
  ///跳转到跨页面  void toSpanTwo(BuildContext context) {    Navigator.push(context, MaterialPageRoute(builder: (context) => SpanTwoPage()));  }
  ///自增  void increase() {    state..count = ++state.count;    emit(state.clone());  }}
   复制代码
 
SpanTwoCubit
- state 
- 使用 count,记录下我们点击自增的次数 
 class SpanTwoState {  int count;
  ///初始化方法  SpanTwoState init() {    return SpanTwoState()..count = 0;  }
  ///克隆方法,针对于刷新界面数据  SpanTwoState clone() {    return SpanTwoState()..count = count;  }}
   复制代码
 
 class CubitSpanTwoPage extends StatelessWidget {  final bloc = BlocSpanTwoCubit();
  @override  Widget build(BuildContext context) {    return BlocProvider(      create: (context) => bloc..init(context),      child: Scaffold(        backgroundColor: Colors.white,        appBar: AppBar(title: Text('跨页面-Two')),        floatingActionButton: FloatingActionButton(          onPressed: () {            //改变SpanOneCubit模块数据            BlocProvider.of<BlocSpanOneCubit>(context).increase();
            //改变当前页面数据            bloc.increase();          },          child: const Icon(Icons.add),        ),        body: Center(          child: BlocBuilder<BlocSpanTwoCubit, BlocSpanTwoState>(            builder: (context, state) {              return Text(                '当前点击了 ${state.count} 次',                style: TextStyle(fontSize: 30.0),              );            },          ),        ),      ),    );  }}
   复制代码
 
 class SpanTwoCubit extends Cubit<SpanTwoState> {  SpanTwoCubit() : super(SpanTwoState().init());
  void init(BuildContext context){    emit(state.init());  }
  ///自增  void increase() => emit(state.clone()..count = ++state.count);}
   复制代码
 总结
OK,这样便用全局 Bloc 实现了类似广播的一种效果
Bloc API 说明
BlocBuilder
BlocBuilder 是 Flutter 窗口小部件,需要Bloc和builder函数。BlocBuilder处理构建小部件以响应新状态。BlocBuilder与非常相似,StreamBuilder但具有更简单的 API,可以减少所需的样板代码量。该builder函数可能会被多次调用,并且应该是一个纯函数,它会根据状态返回小部件。
看看BlocListener是否要响应状态更改“执行”任何操作,例如导航,显示对话框等。
如果省略 cubit 参数,BlocBuilder将使用BlocProvider和当前函数自动执行查找BuildContext。
 BlocBuilder<BlocA, BlocAState>(  builder: (context, state) {    // return widget here based on BlocA's state  })
   复制代码
 
仅当您希望提供一个范围仅限于单个窗口小部件且无法通过父级BlocProvider和当前类访问的 bloc 时,才指定该 bloc BuildContext。
 BlocBuilder<BlocA, BlocAState>(  cubit: blocA, // provide the local cubit instance  builder: (context, state) {    // return widget here based on BlocA's state  })
   复制代码
 
为了对何时builder调用该函数进行细粒度的控制,buildWhen可以提供一个可选的选项。buildWhen获取先前的块状态和当前的块状态并返回一个布尔值。如果buildWhen返回 true,builder将使用进行调用,state并且小部件将重新生成。如果buildWhen返回 false,builder则不会调用state且不会进行重建。
 BlocBuilder<BlocA, BlocAState>(  buildWhen: (previousState, state) {    // return true/false to determine whether or not    // to rebuild the widget with state  },  builder: (context, state) {    // return widget here based on BlocA's state  })
   复制代码
 BlocProvider
BlocProvider 是 Flutter 小部件,可通过为其子元素提供块BlocProvider.of<T>(context)。它用作依赖项注入(DI)小部件,以便可以将一个块的单个实例提供给子树中的多个小部件。
在大多数情况下,BlocProvider应使用它来创建新的 bloc,这些 bloc 将可用于其余子树。在这种情况下,由于BlocProvider负责创建块,它将自动处理关闭 bloc。
 BlocProvider(  create: (BuildContext context) => BlocA(),  child: ChildA(),);
   复制代码
 
默认情况下,BlocProvider将懒惰地创建 bloc,这意味着create当通过查找块时将执行该 bloc BlocProvider.of<BlocA>(context)。
要覆盖此行为并强制create立即运行,lazy可以将其设置为false。
 BlocProvider(  lazy: false,  create: (BuildContext context) => BlocA(),  child: ChildA(),);
   复制代码
 
在某些情况下,BlocProvider可用于向小部件树的新部分提供现有的 bloc。当需要将现有 bloc 用于新路线时,这将是最常用的。在这种情况下,BlocProvider由于不会创建 bloc,因此不会自动关闭该 bloc。
 BlocProvider.value(  value: BlocProvider.of<BlocA>(context),  child: ScreenA(),);
   复制代码
 
然后从ChildA或ScreenA中检索BlocA:
 // with extensionscontext.read<BlocA>();
// without extensionsBlocProvider.of<BlocA>(context)复制到剪贴板错误复制的
   复制代码
 MultiBlocProvider
MultiBlocProvider 是 Flutter 小部件,可将多个BlocProvider小部件合并为一个。 MultiBlocProvider提高了可读性,消除了嵌套多个元素的需求BlocProviders。通过使用,MultiBlocProvider我们可以从:
 BlocProvider<BlocA>(  create: (BuildContext context) => BlocA(),  child: BlocProvider<BlocB>(    create: (BuildContext context) => BlocB(),    child: BlocProvider<BlocC>(      create: (BuildContext context) => BlocC(),      child: ChildA(),    )  ))
   复制代码
 
至:
 MultiBlocProvider(  providers: [    BlocProvider<BlocA>(      create: (BuildContext context) => BlocA(),    ),    BlocProvider<BlocB>(      create: (BuildContext context) => BlocB(),    ),    BlocProvider<BlocC>(      create: (BuildContext context) => BlocC(),    ),  ],  child: ChildA(),)
   复制代码
 BlocListener
BlocListener 是 Flutter 小部件,它带有BlocWidgetListener和一个可选Bloc,listener以响应 bloc 中的状态变化。它应用于需要在每次状态更改时发生一次的功能,例如导航,显示 a SnackBar,显示 aDialog等。
 listener`与in和函数不同,每次状态更改(**不**包括初始状态)仅被调用一次。`builder``BlocBuilder``void
   复制代码
 
如果省略 cubit 参数,BlocListener将使用BlocProvider和当前函数自动执行查找BuildContext。
 BlocListener<BlocA, BlocAState>(  listener: (context, state) {    // do stuff here based on BlocA's state  },  child: Container(),)
   复制代码
 
仅当您希望提供无法通过BlocProvider和当前访问的 bloc 时,才指定该 bloc BuildContext。
 BlocListener<BlocA, BlocAState>(  cubit: blocA,  listener: (context, state) {    // do stuff here based on BlocA's state  },  child: Container())
   复制代码
 
为了对何时listener调用该函数进行细粒度的控制,listenWhen可以提供一个可选的选项。listenWhen获取先前的 bloc 状态和当前的 bloc 状态并返回一个布尔值。如果listenWhen返回 true,listener将使用调用state。如果listenWhen返回 false,listener则不会调用state。
 BlocListener<BlocA, BlocAState>(  listenWhen: (previousState, state) {    // return true/false to determine whether or not    // to call listener with state  },  listener: (context, state) {    // do stuff here based on BlocA's state  },  child: Container(),)
   复制代码
 MultiBlocListener
MultiBlocListener 是 Flutter 小部件,可将多个BlocListener小部件合并为一个。 MultiBlocListener提高了可读性,消除了嵌套多个元素的需求BlocListeners。通过使用,MultiBlocListener我们可以从:
 BlocListener<BlocA, BlocAState>(  listener: (context, state) {},  child: BlocListener<BlocB, BlocBState>(    listener: (context, state) {},    child: BlocListener<BlocC, BlocCState>(      listener: (context, state) {},      child: ChildA(),    ),  ),)
   复制代码
 
至:
 MultiBlocListener(  listeners: [    BlocListener<BlocA, BlocAState>(      listener: (context, state) {},    ),    BlocListener<BlocB, BlocBState>(      listener: (context, state) {},    ),    BlocListener<BlocC, BlocCState>(      listener: (context, state) {},    ),  ],  child: ChildA(),)
   复制代码
 BlocConsumer
BlocConsumer 公开builder和listener以便对新状态做出反应。BlocConsumer与嵌套类似BlocListener,BlocBuilder但减少了所需的样板数量。BlocConsumer仅应在需要重建 UI 和执行其他对状态更改进行响应的情况下使用cubit。BlocConsumer取需要BlocWidgetBuilder和BlocWidgetListener和任选的cubit,BlocBuilderCondition和BlocListenerCondition。
如果cubit省略该参数,BlocConsumer将使用BlocProvider和当前函数自动执行查找 BuildContext。
 BlocConsumer<BlocA, BlocAState>(  listener: (context, state) {    // do stuff here based on BlocA's state  },  builder: (context, state) {    // return widget here based on BlocA's state  })
   复制代码
 
可选的listenWhen,buildWhen可以实现,以更精细地控制何时listener和builder被调用。在listenWhen和buildWhen将在每个被调用cubit state的变化。它们各自采用先前的state和当前的,state并且必须返回 a bool,以确定是否将调用builderand / orlistener函数。以前state会被初始化为state的cubit的时候BlocConsumer被初始化。listenWhen并且buildWhen是可选的,如果未实现,则默认为true。
 BlocConsumer<BlocA, BlocAState>(  listenWhen: (previous, current) {    // return true/false to determine whether or not    // to invoke listener with state  },  listener: (context, state) {    // do stuff here based on BlocA's state  },  buildWhen: (previous, current) {    // return true/false to determine whether or not    // to rebuild the widget with state  },  builder: (context, state) {    // return widget here based on BlocA's state  })
   复制代码
 RepositoryProvider
RepositoryProvider 是 Flutter 小部件,它通过为其子节点提供存储库RepositoryProvider.of<T>(context)。它用作依赖项注入(DI)小部件,以便可以将存储库的单个实例提供给子树中的多个小部件。BlocProvider应该用于提供块,而RepositoryProvider只能用于存储库。
 RepositoryProvider(  create: (context) => RepositoryA(),  child: ChildA(),);
   复制代码
 
然后ChildA我们可以通过以下方式检索Repository实例:
 // with extensionscontext.read<RepositoryA>();
// without extensionsRepositoryProvider.of<RepositoryA>(context)
   复制代码
 MultiRepositoryProvider
MultiRepositoryProvider 是 Flutter 小部件,将多个RepositoryProvider小部件合并为一个。 MultiRepositoryProvider提高了可读性,消除了嵌套多个元素的需求RepositoryProvider。通过使用,MultiRepositoryProvider我们可以从:
 RepositoryProvider<RepositoryA>(  create: (context) => RepositoryA(),  child: RepositoryProvider<RepositoryB>(    create: (context) => RepositoryB(),    child: RepositoryProvider<RepositoryC>(      create: (context) => RepositoryC(),      child: ChildA(),    )  ))
   复制代码
 
至:
 MultiRepositoryProvider(  providers: [    RepositoryProvider<RepositoryA>(      create: (context) => RepositoryA(),    ),    RepositoryProvider<RepositoryB>(      create: (context) => RepositoryB(),    ),    RepositoryProvider<RepositoryC>(      create: (context) => RepositoryC(),    ),  ],  child: ChildA(),)
   复制代码
 最后
相关地址
系列文章
评论