我来为您详细介绍Flutter中Stream的各种使用场景和实现方式,并提供具体的代码实现。
Stream基础概念
Stream是Dart中处理异步数据流的强大工具,它代表一系列异步事件的序列。以下是Stream的各种使用场景和实现方式:
1. Stream基础使用场景
1.1 网络请求数据流
dart
import 'dart:convert';
import 'package:http/http.dart' as http;
/// 网络请求的Stream使用示例
class NetworkStreamExample {
/// 场景1: 实时数据流 - 模拟股票价格更新
Stream<double> getStockPriceStream(String stockSymbol) async* {
// async* 关键字允许函数返回Stream并产生多个值
int count = 0;
while (count < 10) { // 模拟10次价格更新
await Future.delayed(Duration(seconds: 2)); // 每2秒更新一次
// 模拟价格波动 (-5% 到 +5%)
double basePrice = 100.0; // 基础价格
double fluctuation = (Random().nextDouble() * 10 - 5) / 100;
double currentPrice = basePrice * (1 + fluctuation);
yield currentPrice; // 产生新的价格数据
count++;
}
}
/// 场景2: HTTP请求响应流
Stream<String> fetchUserDataStream(int userId) {
return Stream.fromFuture(
http.get(Uri.parse('https://api.example.com/users/$userId'))
).asyncMap((response) async {
if (response.statusCode == 200) {
return response.body;
} else {
throw Exception('Failed to load user data');
}
});
}
}
/// 使用示例
void demoNetworkStream() {
final networkExample = NetworkStreamExample();
// 订阅股票价格流
final stockSubscription = networkExample.getStockPriceStream('AAPL').listen(
(price) {
print('📈 股票价格更新: \$${price.toStringAsFixed(2)}');
},
onError: (error) {
print('❌ 股票价格获取错误: $error');
},
onDone: () {
print('✅ 股票价格流结束');
}
);
// 5秒后取消订阅
Future.delayed(Duration(seconds: 5), () {
stockSubscription.cancel();
print('⏹️ 取消股票价格订阅');
});
}
2. UI交互场景的Stream使用
2.1 用户输入处理
dart
import 'package:flutter/material.dart';
/// 用户输入处理的Stream示例
class UserInputStreamExample {
final StreamController<String> _searchController = StreamController<String>.broadcast();
final StreamController<int> _pageController = StreamController<int>.broadcast();
/// 场景1: 搜索框实时搜索
Stream<List<String>> getSearchResultsStream() {
return _searchController.stream
.distinct() // 去重,避免连续相同的搜索词
.debounceTime(Duration(milliseconds: 300)) // 防抖,300ms内只取最后一次
.asyncMap((searchTerm) async {
if (searchTerm.isEmpty) return <String>[];
// 模拟网络搜索延迟
await Future.delayed(Duration(milliseconds: 500));
// 模拟搜索结果
return [
'$searchTerm 结果1',
'$searchTerm 结果2',
'$searchTerm 结果3'
];
});
}
/// 场景2: 分页加载
Stream<List<String>> getPaginatedDataStream() {
return _pageController.stream
.asyncExpand((page) async* {
// 模拟分页数据加载
for (int i = 0; i < 3; i++) {
await Future.delayed(Duration(milliseconds: 200));
yield '第${page}页-数据${i + 1}';
}
});
}
/// 更新搜索词
void updateSearchTerm(String term) {
_searchController.add(term);
}
/// 加载下一页
void loadNextPage(int page) {
_pageController.add(page);
}
void dispose() {
_searchController.close();
_pageController.close();
}
}
/// 防抖时间扩展
extension DebounceExtension<T> on Stream<T> {
Stream<T> debounceTime(Duration duration) {
var lastEventTime = DateTime.now();
return transform(StreamTransformer<T, T>((input, cancelOnError) {
final controller = StreamController<T>();
Timer? timer;
input.listen(
(T data) {
timer?.cancel();
timer = Timer(duration, () {
controller.add(data);
});
},
onError: controller.addError,
onDone: controller.close,
cancelOnError: cancelOnError,
);
return controller.stream.listen(null);
});
}
}
3. 状态管理场景
3.1 简单的状态管理
dart
import 'dart:async';
/// 基于Stream的状态管理示例
class CounterStateManager {
final StreamController<int> _counterController = StreamController<int>();
int _count = 0;
/// 获取计数器Stream
Stream<int> get counterStream => _counterController.stream;
/// 增加计数
void increment() {
_count++;
_counterController.add(_count);
print('➕ 计数器增加: $_count');
}
/// 减少计数
void decrement() {
_count--;
_counterController.add(_count);
print('➖ 计数器减少: $_count');
}
/// 重置计数
void reset() {
_count = 0;
_counterController.add(_count);
print('🔄 计数器重置: $_count');
}
void dispose() {
_counterController.close();
}
}
/// 更复杂的状态管理 - 购物车示例
class ShoppingCartManager {
final StreamController<List<CartItem>> _cartController = StreamController<List<CartItem>>.broadcast();
final List<CartItem> _items = [];
Stream<List<CartItem>> get cartStream => _cartController.stream;
/// 添加商品到购物车
void addItem(CartItem item) {
final existingIndex = _items.indexWhere((i) => i.id == item.id);
if (existingIndex >= 0) {
// 商品已存在,增加数量
_items[existingIndex] = _items[existingIndex].copyWith(
quantity: _items[existingIndex].quantity + 1
);
} else {
// 新商品
_items.add(item);
}
_notifyListeners();
print('🛒 添加商品: ${item.name}');
}
/// 移除商品
void removeItem(String itemId) {
_items.removeWhere((item) => item.id == itemId);
_notifyListeners();
print('🗑️ 移除商品: $itemId');
}
/// 更新商品数量
void updateQuantity(String itemId, int quantity) {
final index = _items.indexWhere((item) => item.id == itemId);
if (index >= 0) {
_items[index] = _items[index].copyWith(quantity: quantity);
_notifyListeners();
print('📊 更新商品数量: $itemId -> $quantity');
}
}
/// 计算总价
Stream<double> get totalPriceStream => cartStream.map((items) {
return items.fold(0.0, (total, item) => total + item.price * item.quantity);
});
void _notifyListeners() {
_cartController.add(List.from(_items)); // 发送新的列表副本
}
void dispose() {
_cartController.close();
}
}
/// 购物车商品模型
class CartItem {
final String id;
final String name;
final double price;
final int quantity;
CartItem({
required this.id,
required this.name,
required this.price,
this.quantity = 1,
});
CartItem copyWith({
String? id,
String? name,
double? price,
int? quantity,
}) {
return CartItem(
id: id ?? this.id,
name: name ?? this.name,
price: price ?? this.price,
quantity: quantity ?? this.quantity,
);
}
@override
String toString() => 'CartItem($name, \$$price, x$quantity)';
}
4. 文件操作和I/O场景
4.1 文件读取和写入
dart
import 'dart:io';
import 'dart:convert';
/// 文件操作的Stream示例
class FileStreamExample {
/// 场景1: 逐行读取大文件
Stream<String> readLargeFileLineByLine(String filePath) {
return File(filePath)
.openRead() // 返回Stream<List<int>>
.transform(utf8.decoder) // 转换为字符串
.transform(LineSplitter()) // 按行分割
.handleError((error) {
print('❌ 文件读取错误: $error');
});
}
/// 场景2: 实时文件监控
Stream<FileSystemEvent> watchFileChanges(String filePath) {
final file = File(filePath);
final directory = file.parent;
return directory.watch(events: FileSystemEvent.all).where((event) {
// 只关注目标文件的变化
return event.path == filePath;
});
}
/// 场景3: 批量文件处理
Stream<ProcessResult> processMultipleFiles(List<String> filePaths) {
return Stream.fromIterable(filePaths).asyncMap((filePath) async {
// 模拟对每个文件进行处理
await Future.delayed(Duration(milliseconds: 100));
return ProcessResult(0, 0, '处理完成: $filePath', '');
});
}
}
/// 使用示例
void demoFileStream() async {
final fileExample = FileStreamExample();
// 读取文件示例
final fileStream = fileExample.readLargeFileLineByLine('example.txt');
final subscription = fileStream.listen(
(line) {
print('📄 读取到行: $line');
},
onDone: () {
print('✅ 文件读取完成');
}
);
// 文件监控示例
final watchSubscription = fileExample.watchFileChanges('config.json').listen(
(event) {
switch (event.type) {
case FileSystemEvent.create:
print('📁 文件创建: ${event.path}');
break;
case FileSystemEvent.modify:
print('✏️ 文件修改: ${event.path}');
break;
case FileSystemEvent.delete:
print('🗑️ 文件删除: ${event.path}');
break;
default:
print('🔍 文件事件: ${event.type}');
}
}
);
}
5. 定时器和动画场景
5.1 定时任务和动画
dart
import 'dart:async';
/// 定时器和动画的Stream示例
class TimerAndAnimationStreamExample {
/// 场景1: 倒计时计时器
Stream<int> createCountdownTimer(int seconds) {
return Stream.periodic(Duration(seconds: 1), (count) {
return seconds - count - 1; // 倒计时
}).take(seconds); // 只取指定数量的值
}
/// 场景2: 动画帧流
Stream<double> createAnimationStream(Duration duration, {int fps = 60}) {
final totalFrames = (duration.inMilliseconds / (1000 / fps)).round();
return Stream.periodic(
Duration(milliseconds: (1000 / fps).round()),
(frame) => frame / totalFrames // 返回0.0到1.0的进度
).take(totalFrames);
}
/// 场景3: 轮询检查
Stream<bool> createPollingStream(
Future<bool> Function() checkFunction,
Duration interval
) {
return Stream.periodic(interval).asyncMap((_) => checkFunction());
}
}
/// 使用示例
void demoTimerStream() {
final timerExample = TimerAndAnimationStreamExample();
// 倒计时示例
print('⏰ 开始10秒倒计时:');
timerExample.createCountdownTimer(10).listen(
(remaining) {
print('⏳ 剩余时间: ${remaining}秒');
},
onDone: () {
print('🎉 倒计时结束!');
}
);
// 动画示例
timerExample.createAnimationStream(Duration(seconds: 3)).listen(
(progress) {
final percentage = (progress * 100).toStringAsFixed(1);
print('🎬 动画进度: $percentage%');
}
);
// 轮询检查示例
timerExample.createPollingStream(() async {
// 模拟检查某个条件
await Future.delayed(Duration(milliseconds: 100));
return Random().nextBool(); // 随机返回true/false
}, Duration(seconds: 2)).listen(
(result) {
print(result ? '✅ 条件满足' : '❌ 条件不满足');
}
);
}
6. 高级Stream操作和组合
6.1 多个Stream的组合操作
dart
import 'dart:async';
/// 高级Stream操作示例
class AdvancedStreamOperations {
/// 场景1: 合并多个Stream
Stream<dynamic> mergeMultipleStreams(
List<Stream<dynamic>> streams,
Duration timeout,
) {
return StreamGroup.merge(streams).timeout(
timeout,
onTimeout: (sink) {
sink.addError(TimeoutException('Stream操作超时'));
sink.close();
},
);
}
/// 场景2: Stream的Zip操作(同时接收多个Stream的最新值)
Stream<List<dynamic>> zipStreams(List<Stream<dynamic>> streams) {
// 简化实现 - 实际中可以使用rxdart库的ZipStream
final controllers = List.generate(
streams.length,
(_) => StreamController<dynamic>()
);
final latestValues = List<dynamic?>.filled(streams.length, null);
var completedCount = 0;
for (int i = 0; i < streams.length; i++) {
streams[i].listen(
(value) {
latestValues[i] = value;
// 当所有Stream都有值时,发出组合值
if (latestValues.every((v) => v != null)) {
controllers.forEach((controller) {
controller.add(List.from(latestValues));
});
}
},
onError: (error) {
controllers.forEach((controller) {
controller.addError(error);
});
},
onDone: () {
completedCount++;
if (completedCount == streams.length) {
controllers.forEach((controller) {
controller.close();
});
}
},
);
}
return controllers.first.stream;
}
/// 场景3: 重试机制
Stream<T> withRetry<T>(
Stream<T> Function() streamFactory, {
int maxRetries = 3,
Duration retryDelay = const Duration(seconds: 1),
}) {
return Stream<T>.asyncExpand((_) async* {
int attempt = 0;
while (attempt <= maxRetries) {
try {
yield* streamFactory();
break; // 成功则退出循环
} catch (e) {
attempt++;
if (attempt > maxRetries) {
rethrow; // 超过重试次数,抛出异常
}
print('🔄 第$attempt次重试...');
await Future.delayed(retryDelay * attempt); // 递增延迟
}
}
});
}
}
/// 使用示例
void demoAdvancedStream() {
final advancedOps = AdvancedStreamOperations();
// 创建多个测试Stream
final stream1 = Stream.periodic(Duration(seconds: 1), (i) => 'A$i').take(5);
final stream2 = Stream.periodic(Duration(seconds: 2), (i) => 'B$i').take(3);
final stream3 = Stream.periodic(Duration(seconds: 3), (i) => 'C$i').take(2);
// 合并Stream示例
print('🔀 开始合并多个Stream:');
advancedOps.mergeMultipleStreams([stream1, stream2, stream3], Duration(seconds: 10))
.listen(print);
// 带重试的Stream示例
int attempt = 0;
final retryStream = advancedOps.withRetry(() {
attempt++;
if (attempt < 3) {
return Stream.error(Exception('模拟失败'));
} else {
return Stream.value('最终成功的数据');
}
}, maxRetries: 3);
retryStream.listen(
(data) => print('✅ 最终结果: $data'),
onError: (e) => print('❌ 所有重试都失败了: $e'),
);
}
7. Flutter Widget中的Stream使用
7.1 StreamBuilder的实际应用
dart
import 'package:flutter/material.dart';
/// Flutter Widget中的Stream使用示例
class StreamBuilderExamples extends StatelessWidget {
final CounterStateManager counterManager = CounterStateManager();
final ShoppingCartManager cartManager = ShoppingCartManager();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('StreamBuilder示例')),
body: ListView(
children: [
_buildCounterExample(),
_buildShoppingCartExample(),
_buildSearchExample(),
],
),
);
}
/// 示例1: 计数器
Widget _buildCounterExample() {
return Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text('计数器示例', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
StreamBuilder<int>(
stream: counterManager.counterStream,
builder: (context, snapshot) {
return Column(
children: [
Text('当前计数: ${snapshot.data ?? 0}',
style: TextStyle(fontSize: 24, color: Colors.blue)),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: counterManager.decrement,
child: Text('-'),
),
SizedBox(width: 20),
ElevatedButton(
onPressed: counterManager.increment,
child: Text('+'),
),
],
),
],
);
},
),
],
),
),
);
}
/// 示例2: 购物车
Widget _buildShoppingCartExample() {
return Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text('购物车示例', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
StreamBuilder<List<CartItem>>(
stream: cartManager.cartStream,
builder: (context, snapshot) {
final items = snapshot.data ?? [];
return Column(
children: [
if (items.isEmpty)
Text('购物车为空', style: TextStyle(color: Colors.grey))
else
...items.map((item) => ListTile(
title: Text(item.name),
subtitle: Text('单价: \$${item.price}'),
trailing: Text('x${item.quantity}'),
)),
StreamBuilder<double>(
stream: cartManager.totalPriceStream,
builder: (context, snapshot) {
return Text('总价: \$${snapshot.data?.toStringAsFixed(2) ?? '0.00'}',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold));
},
),
ElevatedButton(
onPressed: () {
cartManager.addItem(CartItem(
id: DateTime.now().millisecondsSinceEpoch.toString(),
name: '商品${items.length + 1}',
price: 10.0 + items.length * 5,
));
},
child: Text('添加商品'),
),
],
);
},
),
],
),
),
);
}
/// 示例3: 实时搜索
Widget _buildSearchExample() {
final searchManager = UserInputStreamExample();
return Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text('实时搜索示例', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
TextField(
onChanged: searchManager.updateSearchTerm,
decoration: InputDecoration(
hintText: '输入搜索关键词...',
prefixIcon: Icon(Icons.search),
),
),
SizedBox(height: 10),
StreamBuilder<List<String>>(
stream: searchManager.getSearchResultsStream(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
final results = snapshot.data ?? [];
return Column(
children: [
if (results.isEmpty)
Text('暂无搜索结果', style: TextStyle(color: Colors.grey))
else
...results.map((result) => ListTile(
title: Text(result),
leading: Icon(Icons.search),
)),
],
);
},
),
],
),
),
);
}
@override
void dispose() {
counterManager.dispose();
cartManager.dispose();
}
}
8. 关键总结
Stream的主要使用场景:
- 网络请求:HTTP请求、WebSocket通信、实时数据推送
- 用户交互:搜索框输入、按钮点击、表单验证
- 状态管理:计数器、购物车、应用状态变化
- 文件操作:大文件读取、文件监控、批量处理
- 定时任务:倒计时、动画帧、轮询检查
- 数据转换:数据过滤、映射、组合多个数据源
主要的实现方式:
- StreamController:创建自定义数据流
- Stream.fromXXX:从Future、Iterable等创建Stream
- async* 函数:使用生成器模式产生多个值
- Stream.periodic:创建周期性事件流
- Stream.transform:使用转换器处理数据流
- StreamBuilder:在Flutter中构建响应式UI
这些场景和实现方式覆盖了Flutter开发中Stream的绝大多数应用,可以根据具体需求选择合适的实现方式。