Flutter之BLoC模式

BLoC

其全称为 Business Logic Component,表示为业务逻辑组件,简称 BLoC。

要使用BLoC模式,就离不开stream,通过stream可以实现UI与业务逻辑的分离,分离后有以下好处:

  • 业务改动不影响UI页面,或UI可以尽可能少的改动
  • 改动UI时不会对业务逻辑造成影响

使用stream,可以不必setState,大大减少了build次数,使得状态管理更加简洁、统一。

BLoC封装

  • BaseBloc
abstract class BaseBloc {
  void dispose();
}

定义一些公共接口

  • BlocProvider
import 'package:flutter/material.dart';
import 'package:gridleader/blocs/base_bloc.dart';

Type _typeOf<T>() => T;

class BlocProvider<T extends BaseBloc> extends StatefulWidget {
  final T bloc;
  final Widget child;

  const BlocProvider({
    Key key,
    @required this.child,
    @required this.bloc,
  }) : super(key: key);

  @override
  _BlocProviderState createState() => _BlocProviderState<T>();

  static T of<T extends BaseBloc>(BuildContext context) {
    _BlocProviderInherited<T> provider =
        context.getElementForInheritedWidgetOfExactType<_BlocProviderInherited<T>>()?.widget;
    return provider?.bloc;
  }
}

class _BlocProviderState<T extends BaseBloc> extends State<BlocProvider<T>> {
  @override
  Widget build(BuildContext context) {
    return _BlocProviderInherited<T>(
      bloc: widget.bloc,
      child: widget.child,
    );
  }

  @override
  void dispose() {
    widget.bloc?.dispose();
    super.dispose();
  }
}

class _BlocProviderInherited<T> extends InheritedWidget {
  _BlocProviderInherited({
    Key key,
    @required Widget child,
    @required this.bloc,
  }) : super(key: key, child: child);

  final T bloc;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return false;
  }
}

bloc管理类,使用一个StatefulWidget将bloc和page对应起来,使用时一目了然,dispose时会释放bloc,方便了管理。

InheritedWidget 是在树中高效向下传递信息的基类部件。使用InheritedWidget的好处是当在页面中获取对应的bloc时更加快速、性能更好。InheritedWidget 的运用与源码解析

  • BusinessBloc
class BusinessBloc implements BaseBloc {
  static BusinessBloc _instance;

  factory BusinessBloc() => _getInstance();

  static BusinessBloc _getInstance() {
    if (_instance == null) {
      _instance = BusinessBloc._internal();
    }
    return _instance;
  }

  BusinessBloc._internal();

  StreamController<TestBean> _streamController = StreamController.broadcast();
  Stream<TestBean> get stream => _streamController.stream;

  getData(String doorplateId) {
    DioUtil.instance.get(Api.TEST_URL, onSuccess: (resp) {
      TestBean result = BaseResult.fromJson(resp).getObject();
      _streamController.sink.add(result);
    }, onFail: (error) {
      Fluttertoast.showToast(msg: error);
    });
  }

  @override
  void dispose() {
    _streamController.close();
    _instance = null;
  }
}

单例模式的bloc。需根据使用场景绝定是否需要用单例模式。

  • 页面使用
@override
void initState() {
    super.initState();
    SVResidentBloc _bloc = BlocProvider.of(context);
    _bloc.getData(widget.doorplateId);
}

//build中return的Widget
StreamBuilder<TestBean>(
                      stream: _bloc.orgTreeStream,
                      builder: (context, snapdata) => snapdata.data == null
                          ? CircularProgressIndicatorContainer()
                          : Text(snapdata.data.testName))

为防止数据加载过程中snapdata.data为空导致的报错,要判断为空时显示加载进度组件。

  • 页面引用时
BlocProvider(child: TestPage(), bloc: BusinessBloc())