Flutter中Stream的各种使用场景和实现方式

我来为您详细介绍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的主要使用场景:

  1. 网络请求:HTTP请求、WebSocket通信、实时数据推送
  2. 用户交互:搜索框输入、按钮点击、表单验证
  3. 状态管理:计数器、购物车、应用状态变化
  4. 文件操作:大文件读取、文件监控、批量处理
  5. 定时任务:倒计时、动画帧、轮询检查
  6. 数据转换:数据过滤、映射、组合多个数据源

主要的实现方式:

  1. StreamController:创建自定义数据流
  2. Stream.fromXXX:从Future、Iterable等创建Stream
  3. async* 函数:使用生成器模式产生多个值
  4. Stream.periodic:创建周期性事件流
  5. Stream.transform:使用转换器处理数据流
  6. StreamBuilder:在Flutter中构建响应式UI

这些场景和实现方式覆盖了Flutter开发中Stream的绝大多数应用,可以根据具体需求选择合适的实现方式。

相关推荐
CptW3 小时前
第1篇(Ref):搞定 Vue3 Reactivity 响应式源码
前端·面试
葡萄城技术团队3 小时前
基于 SpreadJS 的百万级数据在线数据透视表解决方案:技术解析与实践
前端
爱隐身的官人3 小时前
XSS平台xssplatform搭建
前端·xss
jiangzhihao05153 小时前
升级到webpack5
前端·javascript·vue.js
哆啦A梦15883 小时前
36 注册
前端·javascript·html
华仔啊3 小时前
面试官:说说async/await?我差点翻车!原来还可以这么用
前端
菥菥爱嘻嘻4 小时前
输出---修改ant样式
前端·react.js·anti-design-vue
该用户已不存在4 小时前
这6个网站一旦知道就离不开了
前端·后端·github
Ai行者心易4 小时前
10天!前端用coze,后端用Trae IDE+Claude Code从0开始构建到平台上线
前端·后端