概念
Stream
是用於接收異步事件數據的異步函數,它們在一些耗時操作之後返回數據,比如像 IO操作。Stream
可以接收多個Future異步操作的結果(成功或失敗)。另外,Stream 可以被訂閱Subscription、StreamController進行管理。BLoC是Google團隊給出的一套“反應式應用”的開發架構。其中涉及到Stream的使用。
好了,閒話少說,下文將依次爲大家展示各個功能該如何進行使用。
1、Stream 基礎示例
通過調用 Stream 的 fromFuture 方法,可以放入一個 future對象 處理異步操作,接着調用 listen方法 拿到future返回的操作結束 data ,如果出現異常也可以在onError方法中進行打印。示例如下:
Stream.fromFutures([
// 1秒後返回結果
Future.delayed(new Duration(seconds: 1), () {
return "hello 1";
}),
// 拋出一個異常
Future.delayed(new Duration(seconds: 2),(){
throw AssertionError("Error");
}),
// 3秒後返回結果
Future.delayed(new Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){
});
輸出日誌打印結果:
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3
2、StreamSubscription訂閱
通過StreamSubscription對象可以將訂閱一個 Stream 對象,從而可以對 Stream 進行暫停、喚醒、取消的管理操作。
StreamSubscription _streamSubscription;
@override
void initState() {
super.initState();
Stream<String> _streamDemo = Stream.fromFuture(fetchData());
_streamSubscription =
_streamDemo.listen(onData, onError: onError, onDone: onDone);
}
//創建一個Future實例
Future<String> fetchData() async {
String result ;
await Future.delayed(Duration(seconds: 3), () {
return "future 3 seconds is ok!!!";
}).then((data){
result= data;
print("result:$result");
});
return result;
}
void onData(String data) {
print("onData:$data");
}
void onError(error) {
print(error);
}
void onDone() {
print("Done");
}
void _onPauseStream() {
_streamSubscription.pause();
}
void _onResumeStream() {
_streamSubscription.resume();
}
void _onCancelStream() {
_streamSubscription.cancel();
}
3、StreamController控制器
(1)、簡單使用
StreamController是對Stream能力的擴展,可以調用.stream.listen就可以監聽數據;調用.add可以放入一個Future對象返回的數據。
//....省略繼承 StatefulWidget 部分代碼
@override
void initState() {
super.initState();
//創建控制
StreamController<String> _streamController = StreamController<String>();
//監聽Stream
_streamController.stream.listen(onData, onError: onError, onDone: onDone);
//獲取Future
String data = await fetchData();
//添加控制
_streamController.add(data);
}
@override
void dispose() {
//關閉控制
_streamController.close();
super.dispose();
}
//創建一個Future實例
Future<String> fetchData() async {
String result ;
await Future.delayed(Duration(seconds: 3), () {
return "future 3 seconds is ok!!!";
}).then((data){
result= data;
print("result:$result");
});
return result;
}
(2)StreamSink 池
往StreamController中添加數據可以用StreamSink的add方法。StreamSink對象通過StreamController.sink拿到實例。
//....省略繼承 StatefulWidget 部分代碼
@override
void initState() {
super.initState();
//創建控制
StreamController<String> _streamController = StreamController<String>();
//創建水池
StreamSink<String> _streamSink = _streamController.sink;
//監聽Stream
_streamController.stream.listen(onData, onError: onError, onDone: onDone);
//獲取Future
String data = await fetchData();
//添加控制_streamController.add(data);
_streamSink.add(data);
}
@override
void dispose() {
//關閉控制
_streamController.close();
super.dispose();
}
//創建一個Future實例
Future<String> fetchData() async {
String result ;
await Future.delayed(Duration(seconds: 3), () {
return "future 3 seconds is ok!!!";
}).then((data){
result= data;
print("result:$result");
});
return result;
}
(3)多次訂閱
有兩種類型的Stream,一種是隻能夠用一個訂閱,另一種是可以有多次訂閱。只需要修改StreamController的創建方式,將StreamController<String>();修改成調用StreamController的broadcast方法。
_streamController = StreamController<String>();//單次訂閱
_streamController = StreamController.broadcast(); //多次訂閱
4、StreamBuilder 構建部件
使用Flutter中的 StreamBuilder 可以根據Stream的數據進行構建小部件。如果Stream的數據有變化就會刷新界面,不需要再手動調用SetState方法更新UI了。
//....省略繼承 StatefulWidget 部分代碼
@override
void initState() {
super.initState();
//創建控制
StreamController<String> _streamController = StreamController<String>();
//創建水池
StreamSink<String> _streamSink = _streamController.sink;
//監聽Stream
_streamController.stream.listen(onData, onError: onError, onDone: onDone);
//獲取Future
String data = await fetchData();
//添加數據
_streamSink.add(data);
}
@override
void dispose() {
//關閉控制
_streamController.close();
super.dispose();
}
//創建一個Future實例
Future<String> fetchData() async {
String result ;
await Future.delayed(Duration(seconds: 3), () {
return "future 3 seconds is ok!!!";
}).then((data){
result= data;
print("result:$result");
});
return result;
}
//Widget
@override
Widget build(BuildContext context) {
return Container(
child: StreamBuilder(
//設置Stream
stream: _streamController.stream,
//初始化值
initialData: '無須手動setState',
//構建一個Text
builder: (context, snapshot) {
return Text(snapshot.data);
},
),
);
}
5、BLoC 業務邏輯組件
BLoC 是英文Business Logic Component的縮寫,翻譯成中文的意思是“業務邏輯組件”。簡單來說,其實就是將用戶需要的一些邏輯單獨的拿出來,放到一個類裏面,這種類就叫做BLoC。
前面學到StreamController中的sink,它的功能可以往Stream上面添加數據,並且會構建小部件改變UI界面。我們可以創建一個BLoC類,裏面添加一個sink,從而監聽Stream的變化。
下面,我們用一個小案例來加以說明。
(1)首先,創建簡單UI界面
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
title: 'bloc訓練',
home: Scaffold(
appBar: AppBar(
title: Text('bloc訓練'),
),
//創建 界面 類
body: BlocDemo(),
//創建 按鈕 類
floatingActionButton: CounterActionButton(),
),
),
);
}
BloCDemo 界面類
class BlocDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ActionChip(
label: Text('0'),
onPressed: () {
},
),
);
}
}
CounterActionButton 類
class CounterActionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
},
);
}
}
(2) 使用InheritedWidget 傳遞 BLoC
Inherited是繼承的意思。InheritedWidget 用來在小部件繼承之間傳遞數據。
使用時先創建一個類繼承InheritedWidget,然後在類中設置其他小部件需要的數據,最後將這個類放在小部件樹的某一個位置,這樣下面的小部件就可以直接訪問到InheritedWidget小部件裏設置的數據。
BloC類
class BlocCounter {
void log() {
print('Bloc');
}
}
InheritedWidget類
class CounterProvider extends InheritedWidget {
//繼承部件
final Widget child;
final BlocCounter bloc;
CounterProvider({this.child, this.bloc}) : super(child: child);
static CounterProvider of(BuildContext context) =>
context.inheritFromWidgetOfExactType(CounterProvider);
@override
bool updateShouldNotify(CounterProvider oldWidget) {
return true;
}
}
使用InheritedWidget類獲取BloC類,部分代碼修改如下:
爲了能夠讓更多小部件樹能夠獲取到InheritedWidget類中提供的數據——BloC,這裏放到Scaffold部件的父級位置。
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
title: 'bloc',
//放置 InheritedWidget類
home: CounterProvider(
//創建 BloC 類
bloc: BlocCounter(),
child: Scaffold(
appBar: AppBar(
title: Text('bloc訓練'),
),
//創建 界面類
body: BlocDemo(),
//創建 按鈕類
floatingActionButton: CounterActionButton(),
),
),
),
);
}
BloCDemo 界面類
class BlocDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
//通過InheritedWidget獲取BloC類
BlocCounter _bloc = CounterProvider
.of(context)
.bloc;
return Center(
child: ActionChip(
label: Text("0"),
onPressed: () {
//顯示log
_bloc.log();
},
),
);
}
}
CounterActionButton 類
class CounterActionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
//通過InheritedWidget拿到BloC類
BlocCounter _bloc = CounterProvider
.of(context)
.bloc;
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
//顯示log
_bloc.log();
},
);
}
}
(3)用 Stream 輸出數據
我們可以通過點擊按鈕調用 StreamSink的add方法,往Stream中添加一個數據。BloC中監聽該Stream的變化。然後再創建一個Stream,並且使用StreamBuilder構建小部件,實現動態交互刷新UI界面。
class BlocCounter {
//創建 Stream
final _streamController = StreamController<int>();
//創建 Sink
StreamSink<int> get counter => _streamController.sink;
//計數
int _count = 0;
//創建 新Stream ,用於接受點擊add的值的變化
final _streamController2 = StreamController<int>();
Stream<int> get count => _streamController2.stream;
BlocCounter() {
this._streamController.stream.listen(onData);
}
void onData(int data) {
print('$data');
_count = data + _count;
//將值添加到Stream中,觸發StreamBuilder更新界面
_streamController2.add(_count);
}
void disponse() {
_streamController.close();
_streamController2.close();
}
void log() {
print('Bloc');
}
}
BloCDemo 界面類
class BlocDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
//通過InheritedWidget獲取BloC類
BlocCounter _bloc = CounterProvider
.of(context)
.bloc;
return Center(
child: StreamBuilder(builder: (context, snapshot) {
return ActionChip(
label: Text('${snapshot.data}'),
onPressed: () {
//往sink 裏添加數據 1
_bloc.counter.add(1);
//打印log
_bloc.log();
},
);
}, initialData: 0,
stream: _bloc.count),//設置Stream來源
);
}
}
CounterActionButton 類
class CounterActionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
//通過InheritedWidget拿到BloC類
BlocCounter _bloc = CounterProvider
.of(context)
.bloc;
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
//顯示log
_bloc.log();
//往sink裏添加數據 1
_bloc.counter.add(1);
},
);
}
}
歡迎訂閱公衆號:數據結構、算法、面試經驗、每日新聞、閒聊趣文等。歡迎一起學習!
歡迎加入Android QQ交流學習羣: