概念
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交流学习群: