持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
什么是Bloc?
Bloc全称是Business Logic Component,通过管理状态的方式,实现当一个组件的状态发生改变的时候,可以通知其他组件的状态进行改变。
这里我们以Flutter官方的一个Demo来进行Bloc改造来进行分析,讲解。
- 首先通过Android Studio来新建一个Flutter项目。
- 新建一个BlocBase抽象类,里面有一个方法,叫做dispose,子类必须实现这个方法
abstract class BlocBase{
void dispose();
}
- 新建一个BlocCounter类继承自BlocBase 并对齐做如下改造:
import 'dart:async';
import 'bloc_base.dart';
//继承BlocBase
class BlocCounter extends BlocBase {
//初例化StreamController,数据类型为int
final _controller = StreamController<int>();
//获取到StreamController的sink,即入口可以添加数据
get _counter => _controller.sink;
//获取到StreamController的stream,即出口可以取数据
get counter => _controller.stream;
//增加计算器值
void increment(int count) {
//向流中添加数据
_counter.add(++count);
}
//销毁
void dispose() {
//关闭流
_controller.close();
}
}
4、对main.dart做如下改造
import 'package:flutter/material.dart';
import 'blocs/bloc_counter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bloc示例',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState(BlocCounter());
}
class _MyHomePageState extends State<MyHomePage> {
//组件计数变量
int _counter = 0;
//计数器Bloc
final BlocCounter bloc;
_MyHomePageState(this.bloc);
//计数增加方法
void _incrementCounter() {
//调用bloc的方法
bloc.increment(_counter);
}
@override
void initState() {
//监听Bloc里的数据
bloc.counter.listen((_count) {
//设置状态值
setState(() {
_counter = _count;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bloc示例'),
),
body: Center(
child: Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
),
//增加按钮
floatingActionButton: FloatingActionButton(
//点击事件
onPressed: _incrementCounter,
child: Icon(Icons.add),
),
);
}
}
这样,我们就可以不用通过int类型,存放点击increment的数据,我们将这个数据保存在一个Bloc类里面,这样,如果说我们点击increment按钮,其他Bloc组件也可以监听到我们的改变,有点类似于React的Redux状态管理机制
实现效果
实现原理讲解
- 首先声明一个BlocCounter变量,然后在initState的生命周期上初始化这个变量,给他加一个监听器来监听count数据的改变,通过setState来刷新到页面当中去。但是要注意这点,我们页面加载的时候,默认是没有我们要监听的这个所谓Bloc的count变量的。所以我们还是需要在状态组件定义一个count变量。
//组件计数变量
int _counter = 0;
//计数器Bloc
final BlocCounter bloc;
@override
void initState() {
//监听Bloc里的数据
bloc.counter.listen((_count) {
//设置状态值
setState(() {
_counter = _count;
});
});
super.initState();
}
- 在我们点击下方fab的时候,我们会执行一个increment的方法。这个方法调用bloc里面的increment方法
是的,我们改变数据的方法不会暴露在状态类中,这个要给Bloc自己内部去调用。然后increment方法通过内部自定义的sink来改变这个流中的数据。
//计数增加方法
void _incrementCounter() {
//调用bloc的方法
bloc.increment(_counter);
}
floatingActionButton: FloatingActionButton(
//点击事件
onPressed: _incrementCounter,
child: Icon(Icons.add),
),
好,这样我们通过dart的流特性,就可以实现这种响应式编程的模式了,对于Stream流的理解,我的想法是它好像JS中的setInterval。也是可以规定多久多久对数据进行一次处理,
如果不考虑Websocket的话,可以用来做全双工通信的这样一个处理。可以定时多久多久向后台发送订单状态。然后提示后台清空消息队列。
Bloc是怎么通过监听一个改变通知其他任务队列进行更新的?
这里我们就需要新建一个provider类,对其他同树组件进行消息派发。那么我们就需要做到写一个类用来管理全局的消息状态。那么这个类就必须得要是
有状态的,并且能够跨组件获取状态和他们的共享数据。
1.首先定义一个类,这个类是继承自有状态组件,并且他要管理一个Widget和这个Widget里面所有的blocs。
//返回类型
Type _typeOf<T>() => T;
class BlocProvider<T extends BlocBase> extends StatefulWidget{
BlocProvider({
Key key,
@required this.child,
@required this.blocs
});
final Widget child;
final List<T> blocs;
@override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static List<T> of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<_BlocProviderInherited<T>>();
//通过BuildContext可以跨组件获取对象
//ancestorInheritedElementForWidgetOfExactType方法获得指定类型的InheritedWidget进而获取它的共享数据。
_BlocProviderInherited<T> provider =
context.ancestorInheritedElementForWidgetOfExactType(type)?.widget;
//返回所有的blocs
return provider?.blocs;
}
}
- 由于BlocProvider里面的类必须要互相之间能够传递状态。所以我们又要创建一个自定义的类,这个类它有BlocList和一个Widget,并且继承了InheritedWidget这样我们就可以吧数据在Widget树中向子Widget进行传递。
/**
* InheritedWidget是Flutter的一个功能型的Widget基类
* 它能有效地将数据在当前Widget树中向它的子Widget树传递
*/
class _BlocProviderInherited<T> extends InheritedWidget {
_BlocProviderInherited({
Key key,
@required Widget child,
@required this.blocs,
}) : super(key: key, child: child);
//所有的bloc
final List<T> blocs;
//用来告诉InheritedWidget如果对数据进行了修改,
//是否必须将通知传递给所有子Widget(已注册/已订阅)
@override
bool updateShouldNotify(_BlocProviderInherited oldWidget) => false;
}
3.这样,我们就可以对具体的状态类进行编写了。
class _BlocProviderState<T extends BlocBase> extends State<BlocProvider<T>> {
//重写销毁方法
@override
void dispose() {
//关闭所有的bloc流
widget.blocs.map((bloc) {
bloc.dispose();
});
super.dispose();
}
@override
Widget build(BuildContext context){
return _BlocProviderInherited<T>(
blocs: widget.blocs,
child: widget.child,
);
}
}
- 在main.dart中使用方法如下:
//增加方法
void _incrementCounter() {
//通过BlocProvider的of方法获取到所有bloc
//然后取第一个bloc并调用其increment方法向流中添加数据
BlocProvider.of<BlocCounter>(context).first.increment(_counter);
}
@override
void initState() {
//通过BlocProvider的of方法获取到所有bloc
//然后取第一个bloc并调用其listen进行监听流的数据
BlocProvider.of<BlocCounter>(context).first.counter.listen((_count) {
//设置状态,重新渲染界面
setState(() {
_counter = _count;
});
});
super.initState();
}