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开关控件与这个变量进行关联,我需要在任何页面拨动开关的时候,其它页面对应的开关的状态保持一致。
这里我们可以设想到的输入事件有两种:

  1. 变量的值改变 --> 导致开关状态变化
  2. 用户拨动开关 --> 导致值改变

因此会有两个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的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 研发阶段性总结 基本设计模式MVP

Flutter 研发阶段性总结 基本设计模式MVP

Flutter 研发阶段性总结 基本需求研发总结

Flutter 研发阶段性总结 基本需求研发总结

Flutter 研发阶段性总结 Widget

Flutter 研发阶段性总结 Widget