全面解析Flutter中的Stream用法及实际应用
目录
前言
1. 什么是Stream
2. Stream的分类
1.单订阅
2.广播订阅
3. Stream的基础用法
1.创建
1.使用Stream.periodic
2.使用StreamController
2.监听
1.Stream类的监听
2.StreamController
3.关闭Stream
4.Stream的常用方法
1.empty
2.value
5.实际应用场景
6.完整示例:计数器应用
7.总结
8.参考文章
前言
1. 什么是Stream
在 Flutter 中,Stream 是一种用于处理异步数据流的工具。它的作用类似广播频道,允许连续推送数据流,并让其他部分实时监听这些数据。Stream 广泛应用于需要异步处理的场景,比如网络请求、用户输入、数据流实时更新等。
图1.Stream
如图所示,把我们要处理的每一个事件都看做一个流,每个流处理之后都会被放到Flutter的事件循环中去。
2. Stream的分类
在 Flutter 中,Stream 分为两类:
1.单订阅
默认情况下Streams会被设置成单订阅,点订阅会保持当前的值,直到有其它的订阅。
单订阅Stream(Single-Subscription Stream)一次只能有一个监听器(listener),当我们对单订阅进行监听的时候,程序会被错。
通常用于一次性的事件,比如从 API 获取数据。
2.广播订阅
广播(Broadcast Stream)允许多个监听器,可以同时向多个订阅者推送数据。
这种类型适合用于事件广播,比如用户操作、全局数据推送等。
3. Stream的基础用法
1.创建
可以使用 Stream 的构造方法,也可以通过 StreamController 自定义数据流。
1.使用Stream.periodic
我们可以使用Stream.periodic定时生成数据流:
Stream<int> numberStream = Stream.periodic(Duration(seconds: 1), (count) => count);
2.使用StreamController
Flutter还可以使用StreamController手动推送数据。
final StreamController<int> controller = StreamController<int>();
Stream<int> myStream = controller.stream;
controller.sink.add(1); // 推送数据
2.监听
1.Stream类的监听
当我们使用Stream类直接创建数据流的时候,可以使用StreamBuilder来监听。
使用 listen 方法可以轻松监听 Stream 的数据变化。
例如在下面的代码中,当我点击按钮的时候,开始每隔1秒发送数据,我们可以使用StreamBuilder 来监听 stream,这样一旦流发出数据,界面就会自动更新。
图2.Stream流的监听
代码如如下:
import 'package:flutter/material.dart';
class StreamDemoPage extends StatefulWidget {
const StreamDemoPage({super.key});
@override
State<StreamDemoPage> createState() => _StreamDemoPageState();
}
class _StreamDemoPageState extends State<StreamDemoPage> {
late Stream<int> stream;
late StreamController<int> streamController;
@override
void initState() {
super.initState();
// 初始化一个流
stream = Stream<int>.periodic(const Duration(seconds: 1), (count) => count * count).take(5);
}
void startStream() {
setState(() {
// 每次点击“发送数据”按钮时重新初始化流
stream = Stream<int>.periodic(const Duration(seconds: 1), (count) => count * count).take(5);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: const Text(
'Flutter中的Stream',
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 16),
),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: startStream,
child: const Text('发送数据', style: TextStyle(color: Colors.black, fontSize: 12)),
),
StreamBuilder<int>(
stream: stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text('等待数据...', style: TextStyle(color: Colors.black, fontSize: 12));
} else if (snapshot.hasError) {
return Text('错误: ${snapshot.error}', style: const TextStyle(color: Colors.red, fontSize: 12));
} else if (snapshot.connectionState == ConnectionState.done) {
return const Text('数据发送完成', style: TextStyle(color: Colors.black, fontSize: 12));
} else {
return Text('接收数据: ${snapshot.data}', style: const TextStyle(color: Colors.black, fontSize: 12));
}
},
),
],
),
),
);
}
}
2.StreamController
当我们的数据流比较复杂的时候,可以使用StreamController 来管理流的创建和数据发送。StreamController 可以在我们需要时手动添加事件,适用于更复杂的流控制需求。
还是以上面的代码为例,我们看看如何使用StreamController来实现:
import 'package:flutter/material.dart';
import 'dart:async';
class StreamDemoPage extends StatefulWidget {
const StreamDemoPage({super.key});
@override
State<StreamDemoPage> createState() => _StreamDemoPageState();
}
class _StreamDemoPageState extends State<StreamDemoPage> {
late StreamController<int> streamController;
int counter = 0;
Timer? timer;
@override
void initState() {
super.initState();
streamController = StreamController<int>();
}
void startStream() {
// 重置计数器并关闭之前的计时器(如果存在)
counter = 0;
timer?.cancel();
// 每秒添加一个计数的平方值到流中
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (counter < 5) {
streamController.add(counter * counter); // 将数据添加到流中
counter++;
} else {
timer.cancel(); // 停止计时器
streamController.close(); // 关闭流
}
});
}
@override
void dispose() {
timer?.cancel(); // 确保计时器在页面销毁时取消
streamController.close(); // 关闭流控制器
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: const Text(
'Flutter中的StreamController',
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 16),
),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: startStream,
child: const Text('发送数据', style: TextStyle(color: Colors.black, fontSize: 12)),
),
StreamBuilder<int>(
stream: streamController.stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text('等待数据...', style: TextStyle(color: Colors.black, fontSize: 12));
} else if (snapshot.hasError) {
return Text('错误: ${snapshot.error}', style: const TextStyle(color: Colors.red, fontSize: 12));
} else if (snapshot.connectionState == ConnectionState.done) {
return const Text('数据发送完成', style: TextStyle(color: Colors.black, fontSize: 12));
} else {
return Text('接收数据: ${snapshot.data}', style: const TextStyle(color: Colors.black, fontSize: 12));
}
},
),
],
),
),
);
}
}
3.关闭Stream
使用 StreamController 时,在不再需要时调用 close 方法来释放资源,防止内存泄漏。
controller.close();
4.Stream的常用方法
Stream 提供了许多用于数据流转换和处理的方法,比如 map、where、asyncMap 等。可以使用这些方法高效地处理流数据。
1.empty
这个方法用来创建一个空的数据流,同时数据流的状态为为完成。
例如下面的代码中,我们创建一个空的Stream:
const stream = Stream.empty(); stream.listen((message){ debugPrint('stream: $variables'); },onDone: (){ debugPrint('stream done'); }, onError: (error){ debugPrint('error:$error'); } );
控制台打印信息为stream done!。
2.value
方法用于返回在数据流关闭之前的数据。
例如在下面的代码中,我们调用printThings方法返回的信息为ok。
Future<void> printThings(Stream<String> data)async{ await for (var x in data){ debugPrint(x); } }printThings(Stream<String>.value('ok'));
5.实际应用场景
Flutter中Stream的应用场景如下:
- 网络请求:使用 Stream 可以轻松处理 HTTP 请求响应,尤其适合需要连续多次请求的场景。
- 用户输入监听:监听用户输入变化,比如搜索框的输入变化。
- 实时数据更新:比如股票、天气、聊天应用等需要实时数据推送的场景。
- 多事件处理:多个监听器同时监听并响应同一个事件源,比如按钮点击。
6.完整示例:计数器应用
下面是一个简单的计数器应用示例,展示了 Stream 在应用中如何工作:
import 'dart:async';
import 'package:flutter/material.dart';
class StreamMainPage extends StatefulWidget {
const StreamMainPage({
super.key,
});
@override
State<StreamMainPage> createState() => _StreamMainPageState();
}
class _StreamMainPageState extends State<StreamMainPage> {
final StreamController<int> _controller = StreamController<int>();
int _counter = 0;
@override
void initState() {
super.initState();
// 使用 Timer.periodic 定时自增计数器
Timer.periodic(const Duration(seconds: 1), (timer) {
_counter++;
_controller.sink.add(_counter);
});
}
@override
void dispose() {
_controller.close(); // 关闭 Stream 防止内存泄漏
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: const Text('Stream用法',style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 16),),
),
body: Center(
child: StreamBuilder<int>(
stream: _controller.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Counter: ${snapshot.data}',
style: const TextStyle(fontSize: 24));
} else {
return const CircularProgressIndicator();
}
},
),
),
);
}
}
7.总结
在 Flutter 中,Stream 是一种处理异步事件流的利器,适用于需要实时推送和监听数据的场景。通过 Stream 可以实现高效的数据流处理和转换,尤其适合需要异步响应的 UI 场景。在实际应用中,合理使用 Stream 可以使代码更加简洁和高效,也能够极大地改善用户体验。
8.参考文章
1.Flutter in Focus