Flutter状态管理:BLoC(Business Logic
2021-01-22 本文已影响0人
青叶小小
一、前言
占位
二、什么是BLoC?
BLoC只是一个概念(Reactive Programming,响应式编程),它是基于“dart:async”中的Stream、StreamController来实现的。
![](https://img.haomeiwen.com/i25416234/0e4c591d218d91dc.png)
- 用StreamBuilder包裹有状态的部件,StreamBuilder将会监听一个流;
- 这个流来自于BLoC;
- 有状态小部件中的数据来自于监听的流;
- 用户交互手势被检测到,产生了事件。例如按了一下按钮;
- 调用bloc的功能来处理这个事件;
- 在bloc中处理完毕后将会吧最新的数据add进流的sink中;
- StreamBuilder监听到新的数据,产生一个新的snapshot,并重新调用build方法;
- Widget被重新构建;
BLoC能够允许我们完美的分离业务逻辑!再也不用考虑什么时候需要刷新屏幕了,一切交给StreamBuilder和BLoC! BLoC由来自Google的两位工程师 Paolo Soares和Cong Hui设计,并在2018年DartConf期间(2018年1月23日至24日)首次展示!
![](https://img.haomeiwen.com/i25416234/5128d068352f8299.png)
三、BLoC创建方式
创建方式有三种:
- 局部模式(类似 setState);
- 全局单例模式(单例模式);
- Scoped模式;
3.1、全局单例模式
全局单例模式并不推荐,原因在于:持久占用Stream而不会释放(dispose)
3.1.1、新建Model
// CountBloc.dart
import 'dart:async';
class CountBloc {
int _count = 0;
StreamController<int> _controller;
Stream<int> get count => _controller.stream;
CountBloc() {
_count = 0;
_controller = StreamController.broadcast();
}
void increment() {
_controller.sink.add(++_count);
}
void dispose() {
_controller.close();
}
}
CountBloc bloc = CountBloc();
3.1.2、新建两个页面(BlocPage和BlocPageTwo)
// BlocPage.dart
import 'package:flutter/material.dart';
import 'package:stateresearch/bloc/models/CountBloc.dart';
import 'package:stateresearch/pages/BlocPageTwo.dart';
class BlocPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _body(context);
}
Widget _body(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("BlocPage"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
StreamBuilder(
stream: bloc.count,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Text("${snapshot.data}");
}),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return BlocPageTwo();
}));
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
// BlocPageTwo.dart
import 'package:flutter/material.dart';
import 'package:stateresearch/bloc/models/CountBloc.dart';
class BlocPageTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _body(context);
}
Widget _body(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("BlocPageTwo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
StreamBuilder(
stream: bloc.count,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Text("${snapshot.data}");
}),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
bloc.increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
3.1.3、修改main文件
import 'package:flutter/material.dart';
import 'package:stateresearch/pages/BlocPage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter状态管理',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: BlocPage(),
);
}
}
3.2、Scoped模式
推荐这种模式!该方式类似Provider。
3.2.1、新建Model
import 'dart:async';
class CountBloc {
int _count = 0;
StreamController<int> _controller;
Stream<int> get count => _controller.stream;
CountBloc() {
_count = 0;
_controller = StreamController.broadcast();
}
void increment() {
_controller.sink.add(++_count);
}
void dispose() {
_controller.close();
}
}
3.2.2、新建两个页面(BlocPage和BlocPageTwo)
import 'package:flutter/material.dart';
import 'package:stateresearch/bloc/providers/CountProvider.dart';
import 'package:stateresearch/pages/BlocPageTwo.dart';
class BlocPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _body(context);
}
Widget _body(BuildContext context) {
final _bloc = CountProvider.of(context);
return Scaffold(
appBar: AppBar(
title: Text("BlocPage"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
StreamBuilder(
stream: _bloc.count,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Text("${snapshot.data}");
}),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return BlocPageTwo();
}));
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:stateresearch/bloc/providers/CountProvider.dart';
class BlocPageTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _body(context);
}
Widget _body(BuildContext context) {
final _bloc = CountProvider.of(context);
return Scaffold(
appBar: AppBar(
title: Text("BlocPageTwo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
StreamBuilder(
stream: _bloc.count,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Text("${snapshot.data}");
}),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_bloc.increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
3.2.3、修改main文件
import 'package:flutter/material.dart';
import 'package:stateresearch/bloc/providers/CountProvider.dart';
import 'package:stateresearch/pages/BlocPage.dart';
void main() {
runApp(CountProvider(child: MyApp()));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter状态管理',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: BlocPage(),
);
}
}
四、总结
我们发现,BLoC 和 ScopedModel / Provider 在跨页面间共享数组,
- 相同点:
- 都需要在顶层包一层(即包裹 MaterialApp);
- 包裹的这一层实际是继承于 InheritedWidget ;
- 不同点在于Model:
- ScopedModel / Provider 的 Model 是继承于 Listenable,且需要主动调用 notifyListeners;
- BLoC 的 Model 是通过 StreamController / Stream / Sink / StreamBuilder 的方式来异步刷新;
- 三者 Model 都可以类似 extends / mixins 方式,将多个 Model / Provider 全并起来做全局共享,
然而,因为 BLoC 是基于 Stream方式,当观察的 Model 数量太多时,性能可能会急剧下降;
下篇会讲到 Redux ,它是一个很优秀的全局共享解决方案,可以考虑 全局redux + 局部bloc(rxdart) 管理方案!