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的绝大多数应用,可以根据具体需求选择合适的实现方式。

相关推荐
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax