Flutter:将FutureBuilder转换为StreamBuilder实现数据持续更新
把FutureBuilder改为StreamBuilder实现实时数据更新
1. 创建StreamController管理数据流
在你的State类中定义一个StreamController,用来发射最新的列表数据(使用broadcast支持多订阅):
final StreamController<List<Map<String, dynamic>>> _itemsStreamController = StreamController.broadcast();
2. 改造数据获取函数
将原来的fetch函数改为能获取数据并发送到Stream的函数,同时处理异常:
Future<void> _fetchAndEmitData() async { try { final session = await account.get(); final sessionid = session.$id; final documentList = await databases.listDocuments( databaseId: '123456789', collectionId: '123456789', queries: [ Query.orderDesc('\$id'), Query.limit(25), ], ); final documents = documentList.documents; // 转换数据格式 final items = documents.map((doc) { final epTitle = doc.data['epTitle']; final epNo = doc.data['epNo']; final epDuration = doc.data['epDuration']; return { ...doc.data, 'epTitle': epTitle, 'epNo': epNo, 'epDuration': epDuration, }; }).toList(); // 将数据发送到Stream _itemsStreamController.add(items); } catch (e) { // 发送错误信息到Stream _itemsStreamController.addError(e); } }
3. 初始化和关闭Stream
在initState中触发首次数据获取,并选择合适的更新方式:
方式一:定时刷新(适合无实时推送的场景)
@override void initState() { super.initState(); // 首次加载数据 _fetchAndEmitData(); // 每30秒自动刷新一次,可按需调整间隔 Timer.periodic(const Duration(seconds: 30), (_) => _fetchAndEmitData()); }
方式二:实时订阅Appwrite文档变化(推荐)
利用Appwrite的实时API,数据库文档变化时自动刷新:
@override void initState() { super.initState(); // 首次加载数据 _fetchAndEmitData(); // 订阅文档变化,有更新时重新获取数据 databases.subscribeDocuments( databaseId: '123456789', collectionId: '123456789', queries: [ Query.orderDesc('\$id'), Query.limit(25), ], callback: (_) => _fetchAndEmitData(), ); }
页面销毁时关闭StreamController,避免内存泄漏:
@override void dispose() { _itemsStreamController.close(); super.dispose(); }
4. 替换FutureBuilder为StreamBuilder
使用StreamBuilder监听Stream并构建UI:
StreamBuilder<List<Map<String, dynamic>>>( stream: _itemsStreamController.stream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Text('waiting'); } else if (snapshot.hasError) { checkInternet(); return Text('${snapshot.error}'); } else if (snapshot.hasData) { final items = snapshot.data!; return ListView.builder( itemCount: items.length, itemBuilder: (context, index) { final item = items[index]; // 替换为你的列表项UI return ListTile( title: Text(item['epTitle'] ?? '无标题'), subtitle: Text('第${item['epNo']}集 | 时长${item['epDuration']}'), ); }, ); } else { return const Text('暂无数据'); } }, )
关键说明
- 原代码中
FutureBuilder的snapshot.length是错误写法,StreamBuilder通过snapshot.data!获取列表数据。 - 使用broadcast类型的StreamController可支持多个Widget订阅同一数据流。
- 实时订阅方式比定时轮询更高效,优先推荐使用Appwrite的实时API。
内容的提问来源于stack exchange,提问作者iamrishan
相关产品推荐
相关产品推荐

