炒沙作縻终不饱,缕冰文章费工巧。这篇文章主要讲述Flutter 专题87 初识状态管理 Bloc#yyds干货盘点#相关的知识,希望能为你提供帮助。
小菜前两天刚学习了基本的 Bloc 状态管理,其中 UI 通过 setState() 方式更新数据,今天进一步了解进阶版的 FlutterBloc 状态管理;
FlutterBloc
FlutterBloc 可以更便利的实现 Bloc,主要是为了与 Bloc 共同使用而构建的;同样需要提前了解几个概念;小菜继续以上一节中的 Demo 进行扩展,添加了 Number 的递增和递减;
BlocBuilder
BlocBuilder 小菜理解为 Bloc 构造器,主要用于构建 Widget 以响应新的状态,相较于 StreamBuilder 更便捷;可替代小菜上一节使用的 setState();
const BlocBuilder(
Key key,
@required this.builder,
B bloc,
BlocBuilderCondition<
S>
condition,
)
分析源码可知,builder 用于相应状态的 Widget,bloc 为当前提供的范围仅限于单个 Widget 且无法通过父级 BlocProvider 和当前级访问的 Bloc 时才使用;而 condition 为可选的过度细粒度,包括两个参数,之前的状态和当前的状态,返回值为 Boolean 类型,true 为更新状态重建 Widget,false 时不重新构建;
@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 Scaffold(
appBar: AppBar(title: Text(Bloc Page)),
body: Center(
child: Column( mainAxisAlignment: MainAxisAlignment.center,
children: <
Widget>
[
Text(当 Number >
30 时,不进行变更, style: TextStyle(fontSize: 20.0, color: Colors.blue)),
SizedBox(height: 20.0),
Text(当前 Number = $_numBloc.state, style: TextStyle(fontSize: 20.0, color: Colors.blue))
])),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <
Widget>
[
FloatingActionButton(
heroTag: addTag, child: Icon(Icons.add),
onPressed: () =>
_numBloc.add(NumberEvent.addEvent)),
SizedBox(height: 20.0),
FloatingActionButton(
heroTag: removeTag, child: Icon(Icons.remove),
onPressed: () =>
_numBloc.add(NumberEvent.removeEvent))
]));
);
文章图片
BlocProvider BlocProvider 为 Bloc 的供应者,创建 Bloc 并供应给其子控件树;
BlocProvider(
Key key,
@required Create<
T>
create,
Widget child,
bool lazy,
)
简单了解源码可知,BlocProvider 通过 create 创建一个 Bloc;通过 child 设置用来响应状态的变更的 Widget;lazy 为是否懒创建(延迟创建),小菜理解的为是否在使用时再进行创建,默认为 true;
class _BlocPageState extends State<
BlocPage>
@override
Widget build(BuildContext context)
return BlocProvider(
create: (BuildContext context) =>
NumberBloc(),
child: BlocBuilder<
NumberBloc, int>
(
condition: (previousState, state)
print(BlocPage.condition->
$previousState==$state);
return state <
= 30 ? true : false;
, builder: (context, count)
return Scaffold(
appBar: AppBar(title: Text(Bloc Page)),
body: Center(
child: Column( mainAxisAlignment: MainAxisAlignment.center,
children: <
Widget>
[
Text(当 Number >
30 时,不进行变更, style: TextStyle(fontSize: 20.0, color: Colors.blue)),
SizedBox(height: 20.0),
Text(当前 Number = $BlocProvider.of<
NumberBloc>
(context).state, style: TextStyle(fontSize: 20.0, color: Colors.blue))
])),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <
Widget>
[
FloatingActionButton(
heroTag: addTag, child: Icon(Icons.add),
onPressed: () =>
BlocProvider.of<
NumberBloc>
(context).add(NumberEvent.addEvent)),
SizedBox(height: 20.0),
FloatingActionButton(
heroTag: removeTag, child: Icon(Icons.remove),
onPressed: () =>
BlocProvider.of<
NumberBloc>
(context).add(NumberEvent.removeEvent))
]));
));
文章图片
BlocListener BlocListener 与 BlocBuilder 应用有相似之处;其中 listener 用于监听状态变更,可在此做出相应的业务处理;
class BlocListener<
B extends Bloc<
dynamic, S>
, S>
extends BlocListenerBase<
B, S>
with BlocListenerSingleChildWidget
final Widget child;
const BlocListener(
Key key,
@required BlocWidgetListener<
S>
listener,
B bloc,
BlocListenerCondition<
S>
condition,
this.child,
)
【Flutter 专题87 初识状态管理 Bloc#yyds干货盘点#】 简单分析源码可得:
- child 为 BlocListener 提供的 Widget 用来响应状态的变更;
- bloc 与 BlocBuilder 对应的 bloc 用法相同,如果省略了 bloc 参数,BlocListener 将使用 BlocProvider 和当前函数自动执行查找 BuildContext;
- condition 为可选的过度细粒度,包括两个参数,之前的状态和当前的状态,返回值为 Boolean 类型,true 为进行 listener 的监听,false 时过滤掉 listener 的监听;此时的过滤与 BlocBuilder 中的 condition 过滤无关;
- listener 在每次状态变更时调用,其中包括上下文环境和当前状态两个参数;
@override Widget build(BuildContext context) return BlocListener< NumberBloc, int> ( bloc: _numBloc, listener: (context, count) print(BlocPage.listene-> $count); , condition: (previousState, state) print(BlocPage.BlocListener.condition-> $previousState==$state); return state < = 20 ? true : false; , child: BlocBuilder< NumberBloc, int> ( bloc: _numBloc, condition: (previousState, state) print(BlocPage.condition-> $previousState==$state); return state < = 30 ? true : false; , builder: (context, count) return Scaffold( appBar: AppBar(title: Text(Bloc Page)), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: < Widget> [ Text(当 Number > 20 时,BlocListener 过滤 listener 监听,与 BlocBuilder 中过滤的状态无关, style: TextStyle(fontSize: 20.0, color: Colors.red)), SizedBox(height: 20.0), Text(当 Number > 30 时,Number 不进行变更, style: TextStyle(fontSize: 20.0, color: Colors.green)), SizedBox(height: 20.0), Text(当前 Number = $_numBloc.state, style: TextStyle(fontSize: 20.0, color: Colors.blue)) ])), floatingActionButton: Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, children: < Widget> [ FloatingActionButton( heroTag: addTag, child: Icon(Icons.add), onPressed: () => _numBloc.add(NumberEvent.addEvent)), SizedBox(height: 20.0), FloatingActionButton( heroTag: removeTag, child: Icon(Icons.remove), onPressed: () => _numBloc.add(NumberEvent.removeEvent)) ])); ));
文章图片
文章图片
TestCode
小菜在测试过程中遇到一些小问题,仅简单记录一下,以防忘记;
Q1: There are multiple heroes that share the same tag within a subtree. 小菜在扩展上一节的 Demo 时,点击进入页面时会黑屏,提示如下错误;
文章图片
A1: 在 FloatingActionButton 中添加 heroTag 区分 以前在学习 Hero Animation 时,在同一个 Page 页面不能用两个相同的 heroTag,小菜这次忽略了 FloatingActionButton 中也应用了 Hero 动画,需要区分一下即可;
floatingActionButton: Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, children: < Widget> [ FloatingActionButton( heroTag: addTag, child: Icon(Icons.add), onPressed: () => _numBloc.add(NumberEvent.addEvent)), FloatingActionButton( heroTag: removeTag, child: Icon(Icons.remove), onPressed: () => _numBloc.add(NumberEvent.removeEvent)) ]));
Q2: BlocProvider.of() called with a context that does not contain a Bloc of type ... 小菜在刚开始尝试 BlocProvider.of< NumberBloc> (context) 方式获取 Bloc 时报如下错误;
文章图片
A2: 在 build() 外创建或通过如下方式创建,并建议与 BlocBuilder 成对设置// build() 方法外创建 NumberBloc _numBloc; @override void initState() super.initState(); _numBloc = NumberBloc();
BlocProvider(
create: (BuildContext context) => NumberBloc(),
child: ....
);
***
&
nbsp;
&
nbsp;
&
nbsp;
&
nbsp;
&
nbsp;
&
nbsp;
小菜刚接触 **FlutterBloc** 很多高级用法还没涉及到,下一节会尝试多种 **Bloc** 共同使用的场景,对各方面理解还不到位,如有错误请多多指导!>
来源:阿策小和尚
推荐阅读
- Linux驱动开发-内核共享工作队列
- APP metrics,loT传感器数据和实时分析数据
- 路由基础之基本的路由策略配置
- 在查询层面统一时序数据和关系数据可消除数据孤岛
- 实战篇(MySQL优化系列--SQL优化实战)
- VUE深度解析
- 前端VueRouter解析
- Zabbix 5.4 Server安装
- Go 入门很简单(如何在 Go 中使用日志包)