其全稱爲 Business Logic Component,表示爲業務邏輯組件,簡稱 BLoC。從其名字來看感受和 業務邏輯有關係。由下圖json
看出,BLoC 是獨立處理業務邏輯,網絡數據請求等等邏輯的一個模塊,經過流的 Sinks, Streams 發佈監聽業務處理後的數據結果,其只關心業務處理,而 Widget 着重看中業務處理後的結果顯示。可見,Bloc 使得業務邏輯和 UI 相分離,這有幾點好處:api
當業務邏輯內部改動時,儘量的減小 UI 程序的改動。bash
當咱們改動 UI 時不會對業務邏輯產生影響markdown
方便咱們測試業務邏輯功能網絡
若是你不知道什麼是 Stream,那能夠點此 什麼是 Stream? Dart 學習。app
咱們簡單地實現一個記錄按鈕點擊次數的 demo。less
import 'dart:async';
class CounterBLoC{
//記錄按鈕點擊的次數
//被流包裹的數據(能夠是任何類型)
int _counter =0;
//流控制
final _counterStreamController =new StreamController<int>();
//流
Stream<int> get stream_counter=> _counterStreamController.stream;
// 經過sink.add發佈一個流事件
void addCount(){
_counterStreamController.sink.add(++_counter);
}
//釋放流
void dispose(){
_counterStreamController.close();
}
}
複製代碼
這個 CounterBLoC 的業務邏輯就是維護_counter 的值,除了 addCount 方法,你還能夠寫其餘改變_counter 值的方法,但要想改變事後的值被監聽到,則要調用 sink.add。async
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'counter_bloc.dart';
class CountPage extends StatefulWidget {
@override
_CountPageState createState() => _CountPageState();
}
class _CountPageState extends State<CountPage> {
//把一些相關的數據請求,實體類變換抽到CounterBLoC這個類裏
//實例化CounterBLoC
final _bloc = new CounterBLoC();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("CountBloc"),),
body: StreamBuilder(
//監聽流,當流中的數據發生變化(調用過sink.add時,此處會接收到數據的變化而且刷新UI)
stream: _bloc.stream_counter,
initialData: 0,
builder: (BuildContext context,AsyncSnapshot<int> snapshot){
return Center(
child: Text(snapshot.data.toString(),style: TextStyle(fontSize: 40,fontWeight: FontWeight.w300),),
);
},
),
floatingActionButton: _getButton(),
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
//關閉流
_bloc.dispose();
}
Widget _getButton(){
return FloatingActionButton(child: Icon(Icons.add),
onPressed: (){
// 點擊添加;其實也是發佈一個流事件
_bloc.addCount();
});
}
}
複製代碼
當咱們點擊 floatActionButton 的時候,bloc 中的_counter 會增長,而後 sink.add 會通知監聽者(CountPage) _counter 數據發生變化,CountPage 刷新 UI。ide
這個案例雖然小,可是也能說明以上的好處,就是:性能
咱們能夠修改業務類 CounterBLoC 邏輯,而儘量的減小 UI 的改動;反之改動 UI 界面,也不會影響到業務層代碼。
除了 Stream 以外,rxdart 也能幫助咱們實現 Bloc 模式。
RxDart 擴展了原始的 Stream,它和 Stream 之間的對應關係以下:
Dart | RxDart |
---|---|
Stream | Observable |
StreamController | Subject |
在使用 RxDart 時先將其加入依賴:
dependencies:
flutter:
sdk: flutter
rxdart: ^0.22.0
複製代碼
class BeautyModel {
String sId;
String createdAt;
String desc;
List<String> images;
String publishedAt;
String source;
String type;
String url;
bool used;
String who;
BeautyModel(
{this.sId,
this.createdAt,
this.desc,
this.images,
this.publishedAt,
this.source,
this.type,
this.url,
this.used,
this.who});
BeautyModel.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
createdAt = json['createdAt'];
desc = json['desc'];
images =json['images']==null?null:json['images'].cast<String>();
publishedAt = json['publishedAt'];
source = json['source'];
type = json['type'];
url = json['url'];
used = json['used'];
who = json['who'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['_id'] = this.sId;
data['createdAt'] = this.createdAt;
data['desc'] = this.desc;
data['images'] = this.images;
data['publishedAt'] = this.publishedAt;
data['source'] = this.source;
data['type'] = this.type;
data['url'] = this.url;
data['used'] = this.used;
data['who'] = this.who;
return data;
}
@override
String toString() {
// TODO: implement toString
return toJson().toString();
}
}
複製代碼
import 'dart:convert';
import 'package:http/http.dart' show Client;
import 'beauty_model.dart';
class NetApi {
Client client = new Client();
Future<List<BeautyModel>> fetchBeauties() async {
print("Starting get beauties ..");
List models = List();
final response =
await client.get("http://gank.io/api/data/福利/15/1");
if (response.statusCode == 200) {
models = json.decode(response.body)["results"];
return models.map((model){
return BeautyModel.fromJson(model);
}).toList();
} else {
throw Exception('Failed to load dog');
}
}
}
複製代碼
import 'package:rxdart/rxdart.dart';
import '../bloc_provider.dart';
import 'beauty_model.dart';
import 'net_api.dart';
class BeautyBloc {
//網絡請求的實例
NetApi _netApi =new NetApi();
final _beautyFetcher = PublishSubject<List<BeautyModel>>();
//提供被觀察的List<BeautyModel
Observable<List<BeautyModel>> get beauties =>_beautyFetcher.stream;
//獲取網絡數據
fetchBeauties() async{
List models = await _netApi.fetchBeauties();
if(_beautyFetcher.isClosed)return;
_beautyFetcher.sink.add(models);
}
//釋放
dispose(){
_beautyFetcher?.close();
}
}
複製代碼
import 'package:flutter/material.dart';
import 'beauty_bloc.dart';
import 'beauty_model.dart';
class BeautyPage extends StatefulWidget {
@override
_BeautyPageState createState() => _BeautyPageState();
}
class _BeautyPageState extends State<BeautyPage> {
final _beautyBloc = BeautyBloc();
@override
void initState() {
// TODO: implement initState
super.initState();
_beautyBloc.fetchBeauties();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("BeautyPage"),
),
body: Container(
child: StreamBuilder(
//監聽流
stream: _beautyBloc.beauties,
builder: (context, AsyncSnapshot<List<BeautyModel>> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Image.network(
snapshot.data[index].url,
fit: BoxFit.fill,
));
},
itemCount: snapshot.data.length,
);
} else if (snapshot.hasError) {
return Text('Beauty snapshot error!');
}
return Text('Loading Beauties..');
})),
);
}
}
複製代碼
以上就是 Bloc 的簡單使用,可是我發現每次都要 dispose()釋放,感受有點麻煩,因此急需一個通用的 Bloc 管理類,咱們就用 StatefulWidget 把 Bloc 包起來。其代碼以下:
import 'package:flutter/material.dart';
import 'beauty_bloc_example/net_api.dart';
//獲類型
Type _typeOf<T>() {
return T;
}
abstract class BlocBase {
//一些網絡接口API
NetApi netApi = new NetApi();
//釋放
void dispose();
}
//更方便的管理Bloc
class BlocProvider<T extends BlocBase> extends StatefulWidget {
final T bloc; //bloc
final Widget child; //子Widget
//構造
const BlocProvider({Key key,@required this.bloc, @required this.child}) : super(key: key);
//經過ancestorInheritedElementForWidgetOfExactType獲取
//bloc 實例
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<_BlocProviderInherited<T>>();
_BlocProviderInherited<T> provider =
context.ancestorInheritedElementForWidgetOfExactType(type)?.widget;
return provider?.bloc;
}
@override
_BlocProviderState<T> createState() {
// TODO: implement createState
return _BlocProviderState();
}
}
class _BlocProviderState<T extends BlocBase> extends State<BlocProvider<BlocBase>> {
@override
/// 便於資源的釋放
void dispose() {
widget.bloc?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return new _BlocProviderInherited<T>(
child: widget.child,
bloc: widget.bloc,
);
}
}
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(_BlocProviderInherited oldWidget) => false;
}
複製代碼
因爲 InheritedWidget 中並無 dispose() 方法,因此僅僅只在 InheritedWidget 中沒法及時的釋放資源,因此引用了 StatefulWidget。那爲何不簡簡單單的只用 StatefulWidget,還要套一層 InheritedWidget 呢?這是因爲 InheritedWidget 使得性能更好,若是你不瞭解 InheritedWidget,那麼InheritedWidget 的運用與源碼解析將會幫你更好的認識 InheritedWidget。
固然,若是咱們不用 InheritedWidget 也行,只要將 of 方法中的實現改成:
static T of<T extends BlocBase>(BuildContext context){
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
}
複製代碼
,而後 build 方法直接返回 widget.bloc 便可。
可是咱們看 context.ancestorWidgetOfExactType()的方法以下:
@override
Widget ancestorWidgetOfExactType(Type targetType) {
assert(_debugCheckStateIsActiveForAncestorLookup());
Element ancestor = _parent;
while (ancestor != null && ancestor.widget.runtimeType != targetType)
ancestor = ancestor._parent;
return ancestor?.widget;
}
複製代碼
這個方法會不斷地循環往上找,若是嵌套太深,效率上會受到影響,而 InheritedWidget 不會,具體說明看上文。既然咱們用了 InheritedWidget,那爲何咱們用的是 context.ancestorInheritedElementForWidgetOfExactType()方法而不是咱們在 InheritedWidget 文中使用的 context.inheritFromWidgetOfExactType()方法呢?看一下源碼你就知道了:
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
//多出了刷新依賴Widget的邏輯
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
複製代碼
@override
InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
return ancestor;
}
複製代碼
可見 inheritFromWidgetOfExactType 除了獲取最近的 InheritedWidget 還刷新了以來的控件,而咱們這裏並不須要刷新,因此改用 ancestorInheritedElementForWidgetOfExactType。
import 'package:rxdart/rxdart.dart';
import '../bloc_provider.dart';
import 'beauty_model.dart';
import 'net_api.dart';
class BeautyBaseBloc extends BlocBase {
final _beautyFetcher = PublishSubject<List<BeautyModel>>();
//提供被觀察的List<BeautyModel
Observable<List<BeautyModel>> get beauties =>_beautyFetcher.stream;
//獲取網絡數據
fetchBeauties() async{
List models = await netApi.fetchBeauties();
if(_beautyFetcher.isClosed)return;
_beautyFetcher.sink.add(models);
}
@override
void dispose() {
// TODO: implement dispose
_beautyFetcher.close();
}
複製代碼
import 'package:flutter/material.dart';
import 'package:flutter_bloc_example/bloc_provider.dart';
import 'beauty_base_bloc.dart';
import 'beauty_bloc.dart';
import 'beauty_model.dart';
class BeautyBasePage extends StatefulWidget {
@override
_BeautyBasePageState createState() => _BeautyBasePageState();
}
class _BeautyBasePageState extends State<BeautyBasePage> {
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
//拿到 BeautyBaseBloc 實例
BeautyBaseBloc _bloc= BlocProvider.of<BeautyBaseBloc>(context);
//獲取網絡數據
_bloc.fetchBeauties();
return Scaffold(
appBar: AppBar(
title: Text("BeautyPage"),
),
body: Container(
child: StreamBuilder(
stream: _bloc.beauties,
builder: (context, AsyncSnapshot<List<BeautyModel>> snapshot) {
if (snapshot.hasData) {
print('has data');
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Image.network(
snapshot.data[index].url,
fit: BoxFit.fill,
));
},
itemCount: snapshot.data.length,
);
} else if (snapshot.hasError) {
return Text('Beauty snapshot error!');
}
return Text('Loading Beauty..');
})),
);
}
}
複製代碼
class BeautyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: "BeautyBloc Demo",
theme: new ThemeData(
primarySwatch: Colors.blue,
),
//插入 BlocProvider
home: BlocProvider<BeautyBaseBloc>(
child: BeautyBasePage(), bloc: new BeautyBaseBloc()),
);
}
}
複製代碼
至此咱們通用的 BlocProvider 管理類也寫完了,其中它有 InheritedWidget 所擁有的特色,也能在 widget 銷燬的時候自動的關閉流。