Flutter中的StreamController完全指南
引言
在Flutter应用开发中,数据流的管理是一个核心话题。StreamController作为Dart中dart:async库提供的一个重要工具,为我们处理异步数据序列提供了强大支持。本文将深入探讨StreamController的各种用法、最佳实践以及常见陷阱。
什么是StreamController?
简单来说,StreamController是Stream(数据流)的控制器。它允许我们手动向Stream中添加数据,并控制Stream的生命周期。可以把StreamController想象成一个水管的管理员,既能向水管中注入水(数据),也能控制水流的开始和结束。
StreamController的基本用法
- 创建简单的StreamController
dart
import 'dart:async';
void main() {
// 创建StreamController
final controller = StreamController<String>();
// 监听Stream
controller.stream.listen((data) {
print('收到数据: $data');
});
// 添加数据
controller.add('Hello');
controller.add('World');
// 关闭StreamController
controller.close();
}
// 输出:
// 收到数据: Hello
// 收到数据: World
- 同步与异步StreamController
dart
// 异步StreamController(默认)
final asyncController = StreamController<int>();
// 同步StreamController(立即分发事件)
final syncController = StreamController<int>(sync: true);
asyncController.stream.listen((data) {
print('异步: $data');
});
syncController.stream.listen((data) {
print('同步: $data');
});
asyncController.add(1); // 事件会在下一个事件循环中处理
syncController.add(2); // 事件立即处理
// 输出:
// 同步: 2
// 异步: 1
- 广播StreamController
普通StreamController只能有一个订阅者,广播模式允许多个订阅者:
dart
// 创建广播StreamController
final broadcastController = StreamController<int>.broadcast();
// 多个订阅者
broadcastController.stream.listen((data) {
print('订阅者1: $data');
});
broadcastController.stream.listen((data) {
print('订阅者2: $data');
});
broadcastController.add(42);
// 输出:
// 订阅者1: 42
// 订阅者2: 42
高级用法
- 带缓冲区的StreamController
dart
final controller = StreamController<String>(
// 设置缓冲区大小
onListen: () => print('开始监听'),
onPause: () => print('暂停'),
onResume: () => print('恢复'),
onCancel: () => print('取消监听'),
);
controller.stream.listen(null,
onData: (data) => print('数据: $data'),
onError: (error) => print('错误: $error'),
onDone: () => print('完成'),
);
- 使用Sink添加数据
StreamController提供了Sink接口用于添加数据:
dart
final controller = StreamController<int>();
final sink = controller.sink;
sink.add(1);
sink.add(2);
sink.add(3);
// 等价于
controller.add(1);
controller.add(2);
controller.add(3);
// 关闭sink
sink.close();
- 错误处理
dart
final controller = StreamController<int>();
controller.stream.listen(
(data) => print('数据: $data'),
onError: (error, stackTrace) {
print('捕获错误: $error');
print('堆栈: $stackTrace');
},
onDone: () => print('流已关闭'),
);
controller.add(1);
controller.addError(Exception('出错了!'));
controller.add(2);
controller.close();
// 输出:
// 数据: 1
// 捕获错误: Exception: 出错了!
// 数据: 2
// 流已关闭
实际应用场景
- 实现简单的事件总线
dart
class EventBus {
final StreamController _controller = StreamController.broadcast();
void emit(event) {
_controller.add(event);
}
Stream<T> on<T>() {
return _controller.stream.where((event) => event is T).cast<T>();
}
void dispose() {
_controller.close();
}
}
// 使用示例
final eventBus = EventBus();
// 监听特定类型事件
eventBus.on<String>().listen((message) {
print('收到消息: $message');
});
eventBus.on<int>().listen((number) {
print('收到数字: $number');
});
// 发送事件
eventBus.emit('Hello');
eventBus.emit(42);
- 数据层与UI层的通信
dart
class UserRepository {
final StreamController<List<User>> _userController =
StreamController.broadcast();
Stream<List<User>> get users => _userController.stream;
Future<void> addUser(User user) async {
// 添加用户的逻辑
final users = await fetchUsers();
_userController.add(users);
}
void dispose() {
_userController.close();
}
}
class UserWidget extends StatefulWidget {
@override
_UserWidgetState createState() => _UserWidgetState();
}
class _UserWidgetState extends State<UserWidget> {
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
final userRepo = UserRepository();
_subscription = userRepo.users.listen((users) {
setState(() {
// 更新UI
});
});
}
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
}
- 防抖和节流
dart
class SearchService {
final StreamController<String> _searchController = StreamController();
SearchService() {
_searchController.stream
.debounceTime(Duration(milliseconds: 500))
.listen(_performSearch);
}
void search(String query) {
_searchController.add(query);
}
void _performSearch(String query) async {
print('搜索: $query');
// 执行实际的搜索逻辑
}
void dispose() {
_searchController.close();
}
}
extension DebounceExtension<T> on Stream<T> {
Stream<T> debounceTime(Duration duration) {
return transform(StreamTransformer.fromHandlers(
handleData: (data, sink) async {
await Future.delayed(duration);
sink.add(data);
},
));
}
}
最佳实践和注意事项
- 总是关闭StreamController
dart
class MyController {
final StreamController _controller = StreamController();
void dispose() {
_controller.close(); // 必须关闭,否则会内存泄漏
}
}
- 避免在StreamController关闭后添加数据
dart
final controller = StreamController();
controller.close();
try {
controller.add(1); // 这会抛出异常
} catch (e) {
print('错误: 不能在关闭后添加数据');
}
- 使用async*和StreamController的选择
dart
// 使用async*(推荐用于生成流)
Stream<int> countUpTo(int max) async* {
for (int i = 1; i <= max; i++) {
yield i;
await Future.delayed(Duration(seconds: 1));
}
}
// 使用StreamController(需要手动控制时使用)
Stream<int> countUpToWithController(int max) {
final controller = StreamController<int>();
var count = 1;
Timer.periodic(Duration(seconds: 1), (timer) {
if (count <= max) {
controller.add(count);
count++;
} else {
timer.cancel();
controller.close();
}
});
return controller.stream;
}
- 处理StreamSubscription的内存泄漏
dart
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
StreamSubscription? _subscription;
@override
void initState() {
super.initState();
final controller = StreamController();
_subscription = controller.stream.listen((data) {
// 处理数据
});
}
@override
void dispose() {
_subscription?.cancel(); // 取消订阅,避免内存泄漏
super.dispose();
}
}
性能优化技巧
- 使用缓存避免重复订阅
dart
class DataService {
static final DataService _instance = DataService._internal();
factory DataService() => _instance;
DataService._internal();
final StreamController<int> _controller = StreamController.broadcast();
int _lastValue = 0;
bool _hasData = false;
Stream<int> get dataStream async* {
if (_hasData) {
yield _lastValue;
}
yield* _controller.stream;
}
void addData(int value) {
_lastValue = value;
_hasData = true;
_controller.add(value);
}
}
- 控制缓冲区大小
dart
final controller = StreamController<int>(
// 设置缓冲区大小限制
onListen: () => print('开始监听'),
);
// 监听前添加数据会被缓冲
controller.add(1);
controller.add(2);
controller.add(3);
// 监听后才开始接收数据
controller.stream.listen((data) {
print(data); // 会输出1,2,3
});
总结
StreamController是Flutter/Dart开发中处理异步数据流的强大工具。掌握它的用法能够帮助你:
- 实现组件间的解耦通信
- 优雅地处理异步数据序列
- 构建响应式的应用架构
记住核心要点:明确流的类型(单订阅/广播),及时关闭资源,正确处理错误,选择合适的应用场景。随着Flutter应用的复杂度提升,StreamController将成为你工具箱中不可或缺的一部分。
延伸阅读:
· Dart官方文档关于Stream的说明
· RxDart库(提供了更强大的流操作符)
· BLoC模式中的Stream使用
希望这篇文章对你有所帮助!如果有任何问题,欢迎在评论区讨论。