Flutter 研发阶段性总结 基本设计模式BLoC
Posted 嗡汤圆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 研发阶段性总结 基本设计模式BLoC相关的知识,希望对你有一定的参考价值。
BLoC(Business Logic Component)设计模式是一个新鲜词汇, 在2018年1月的DartConf上被提出,其实是在Rx系列思想之下发展出来的响应式编程模式。
PS: 有时候从后端研发的角度来看,前端领域的一些想法确实挺前卫的。
其核心在于:将变量的变化看作一种流,并把Widget的状态绑定在流上,从而当变量改变时候,Widget接收到事件并作出响应的改变。因此BLoC可以做到完全将业务逻辑封装起来,Widget仅通过输入、输出和逻辑模块交互,做到完全的解耦。
BLoC的结构盗图一下:
BLoC的具体实现
这里的实现 90% 出自这位知乎大佬的文章,各位可以去围观一下:
在 Flutter 中使用 Bloc 来处理数据并更新 UI
流 Stream 的概念
可以把Stream看作管道,事件从一端输入,监听方从另一端获取事件并作出相应的改变。这里我们的Widget需要使用到StreamBuilder。
在Flutter中 StreamController 管理流, controller.sink 是入口, controller.stream 是出口
基本的BLoC实现
假设我们需要一个环境变量代表开和关,而App有多个页面的Switch开关控件与这个变量进行关联,我需要在任何页面拨动开关的时候,其它页面对应的开关的状态保持一致。
这里我们可以设想到的输入事件有两种:
- 变量的值改变 --> 导致开关状态变化
- 用户拨动开关 --> 导致值改变
因此会有两个Stream: 值的Stream,和用户操作事件Stream
class AppSwitchBLoC
bool _switchValue = false;
StreamController<bool> _appSwitchController = StreamControler();
// 用于值改变事件的输入(私有变量,不暴露出来)
StreamSink<bool> get _inSwitch = _appSwitchController.sink;
// 用于值改变事件的输入(值的输出暴露出来)
Stream<bool> get outSwitchValue = _appSwitchController.stream;
StreamController _switchActionController = StreamController();
// 用户输入触发(暴露出来)
StreamSink get changeSwitchValue = _switchActionController.sink;
AppSwitchBLoC()
// 指定_handleEvent函数处理用户的输入事件
_switchActionController.stream.listen(_handleEvent);
_handleEvent(data)
// 业务处理逻辑,这里就是将_switchValue的值变为用户输入的值
_switchValue = data;
// 将用户输入的值传递给值Stream, 从而触发界面元素Widget改变
_inSwitch.add(_switchValue);
BLoC的状态管理
此代码完全来源于上面提到的知乎大佬,各位自行领悟代码…
abstract class BlocBase
void dispose();
class BlocProvider<T extends BlocBase> extends StatefulWidget
final T bloc;
final Widget child;
BlocProvider(
Key key,
@required this.child,
@required this.bloc,
) : super(key: key);
@override
_BlocProviderState<T> createState() => new _BlocProviderState<T>();
static Type _typeOf<T>() => T;
static T of<T extends BlocBase>(BuildContext context)
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>>
@override
void dispose()
widget.bloc.dispose();
super.dispose();
@override
Widget build(BuildContext context)
return widget.child;
在此基础上,上边的AppSwitchBLoC变为:
class AppSwitchBLoC implements BlocBase
/// 源代码不变
// 添加 dispose 方法
@override
void dispose()
// 释放stream资源
_appSwitchController.close();
_switchActionController.close();
界面与BLoC的绑定
借助上边的Provider即可
首先进入AppSwitch页面时,原先仅需要进入Page实例即可,这里需要用Provider封装一层。
/// 改造前
Navigator.of(context).push(new MaterialPageRoute(builder: (context)
return AppSwitchPage();
));
/// 改造后
Navigator.of(context).push(new MaterialPageRoute(builder: (context)
return BlocProvider<AppSwitchPage>(
bloc: AppSwitchBLoC(),
child: AppSwitchPage()
);
));
此时在AppSwitchPage页面即可获取BLoC实例
class AppSwitchPage extends StatefulWidget
@override
_AppSwitchPageState createState() => _AppSwitchPageState();
class _AppSwitchPageState extends State<AppSwitchPage>
@override
Widget build(BuildContext context)
final AppSwitchBLoC bloc = BlocProvider.of<AppSwitchBLoC>(context);
return Container(
child: StreamBuilder<bool> (
stream: bloc.outSwitchValue,
initialData: false,
builder: (context, AsyncSnapshot<bool> snapshot)
return Switch(
onChange: (value)
// 这里输入用户操作后的开关值
bloc.changeSwitchValue.add(value);
,
// 这里从流里监听值的变化
value: snapshot.data,
);
)
);
以上是关于Flutter 研发阶段性总结 基本设计模式BLoC的主要内容,如果未能解决你的问题,请参考以下文章