Flutter中Stream未触发Widget重建,HackerNews接口数据无法正常展示问题
问题核心原因
- 异步时序问题:你在
ListView.builder的itemBuilder中调用fetchItem往stream塞id,stream的处理是异步的,不会立刻更新items流的缓存Map。当NewsListTile首次build时,缓存Map里还没有对应id的key,直接用[]!取到的是null,传给FutureBuilder的future参数为null时,会永远处于hasData = false的状态。 - 缓存Map引用不变的潜在问题:
ScanStreamTransformer中直接修改原有Map对象并返回,虽然默认stream不会拦截相同引用的事件,但如果后续加了distinct逻辑、或者框架层有对象相等判断,会导致下游收不到更新事件。 - 缺少异常处理:如果API请求、JSON解析环节抛出异常,
FutureBuilder的hasError会为true,你没有对应判断分支,也会一直显示加载中。
修复方案
1. 修改stories_bloc中的转换器逻辑,每次返回新的Map实例
_itemsTransformer() { return ScanStreamTransformer( (Map<int, Future<ItemModel>> cache, int id, index) { // 基于旧缓存创建新Map,避免引用不变导致的问题 final newCache = Map<int, Future<ItemModel>>.from(cache); newCache[id] = _newsRepository.fetchItem(id); return newCache; }, <int, Future<ItemModel>>{}, ); }
2. 修改NewsListTile中的逻辑,增加key存在判断和异常处理
class NewsListTile extends StatelessWidget { final int itemId; const NewsListTile({required this.itemId}); @override Widget build(BuildContext context) { final bloc = StoriesBlocProvider.of(context); return StreamBuilder( stream: bloc.items, builder: (context, AsyncSnapshot<Map<int, Future<ItemModel>>> snapshot) { if (!snapshot.hasData) { return const Text('Fetching item'); } // 先判断当前id是否已经加入缓存 if (!snapshot.data!.containsKey(itemId)) { return Text('Still loading item $itemId'); } return FutureBuilder( future: snapshot.data![itemId]!, builder: (context, AsyncSnapshot<ItemModel> itemSnapshot) { // 新增错误处理,方便排查请求/解析问题 if (itemSnapshot.hasError) { return Text('加载失败 $itemId: ${itemSnapshot.error}'); } if (!itemSnapshot.hasData) { return Text('Still loading item $itemId'); } return Text(itemSnapshot.data!.title); }, ); }, ); } }
额外检查项
确认你的ItemModel.fromJson方法字段映射正确,没有拼写错误,避免JSON解析抛出未捕获的异常。
内容的提问来源于stack exchange,提问作者Divyam Dhadwal
相关产品推荐
相关产品推荐

