state变量是框架内部定义的,会默认保存上一次同步的MainSate对象的值
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; } } }main_event:在这里就能看见,view触发了那些事件了;维护起来也很爽,看看这里,也很快能懂页面在干嘛了
@immutable abstract class MainEvent extends Equatable{ const MainEvent(); } ///切换NavigationRail的tab class 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可选择性的设置读写权限,因为我这边设置的俩个变量全是必用的,读写均要,就设置公有类型,不用下划线“_”去标记私有了。
class MainState{ int selectedIndex; bool isExtended; MainState({this.selectedIndex, this.isExtended}); }对于生成的模板代码,我们在这:去掉@immutable注解,去掉abstract;
这里说下加上@immutable和abstract的作用,这边是为了标定不同状态,拿很典型的列表数据加载说明,列表加载的时候一般有三种状态
获取数据前,列表的布局展示空样式:LoadingBeforeState
获取数据失败,显示出加载失败的布局,或提升重新加载的样式提升:LoadingFailureState
获取数据成功,显示出列表数据:LoadingSuccessState
针对上面三种状态,需要展示不同的布局,这样我们就可以继承抽象的状态类:LoadingState,针对不同状态实现上面三种不同的状态类,不同的状态可以定义不同的参数,然后在view中去判断调用
这种实现不同状态,对不同状态进行管理,有点设计模式中-状态模式的味道
下面代码是对上述描述的一种代码展示,可以瞧瞧;跑demo的时候,这下面的代码就不用抄了,仅做演示
@immutable abstract class LoadingState extends Equatable {} class LoadingInitial extends LoadingState { @override List<Object> get props => []; } class LoadingBeforeSate extends LoadingState{ ///实现相应的字段信息 @override List<Object> get props => []; } class LoadingFailureState extends LoadingState{ ///实现相应的字段信息 @override List<Object> get props => []; } class LoadingSuccessState extends LoadingState{ ///实现相应的字段信息 @override List<Object> get props => []; } ///在View中使用,伪代码 BlocBuilder<MainBloc, MainState>(builder: (context, state) { if(state is LoadingBeforeSate){ return Beforewidget(state.XX,..); } else if(state is LoadingFailureState){ return FailureWidget(state.XX,state.XX,...); } else if(state is LoadingSuccessState){ return SuccessWidget(state.XX); } else { return ErrorWidget(...); } })main_view
这边就是咱们的界面层了,很简单,将需要刷新的组件,用BlocBuilder包裹起来,使用BlocBuilder:提供的state去赋值就ok了,context去添加执行的事件,context用StatelessWidget中提供的或者BlocBuilder提供的都行
void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MainPage(), ); } } class MainPage extends StatelessWidget { @override Widget build(BuildContext context) { ///创建BlocProvider的,表明该Page,我们是用MainBloc,MainBloc是属于该页面的Bloc了 return BlocProvider( create: (BuildContext context) => MainBloc(), child: BodyPage(), ); } } class BodyPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Bloc')), body: totalPage(), ); } } Widget totalPage() { return Row( children: [ navigationRailSide(), Expanded(child: Center( child: BlocBuilder<MainBloc, MainState>(builder: (context, state) { ///看这看这:刷新组件! return Text("selectedIndex:" + state.selectedIndex.toString()); }), )) ], ); } //增加NavigationRail组件为侧边栏 Widget navigationRailSide() { //顶部widget Widget topWidget = 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 Widget bottomWidget = Container( child: BlocBuilder<MainBloc, MainState>( builder: (context, state) { return FloatingActionButton( onPressed: () { ///添加NavigationRail展开,收缩事件 context.bloc<MainBloc>().add(IsExtendEvent()); }, ///看这看这:刷新组件! child: Icon(state.isExtended ? Icons.send : Icons.navigation), ); }, ), ); return BlocBuilder<MainBloc, MainState>(builder: (context, state) { return NavigationRail( backgroundColor: Colors.white12, 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: topWidget, //底部widget trailing: bottomWidget, selectedIndex: state.selectedIndex, onDestinationSelected: (int index) { ///添加切换tab事件 context.bloc<MainBloc>().add(SwitchTabEvent(selectedIndex: index)); }, ); }); } Bloc范例优化 反思从上面的代码来看,实际存在几个隐式问题,这些问题,刚开始使用时候,没异常的感觉,但是使用bloc久了后,感觉肯定越来越强烈
state问题
初始化问题:这边初始化是在bloc里,直接在构造方法里面赋初值的,state中一旦变量多了,还是这么写,会感觉极其难受,不好管理。需要优化