流不重建小部件

Posted

技术标签:

【中文标题】流不重建小部件【英文标题】:Stream not rebuilding the widget 【发布时间】:2021-12-31 06:59:33 【问题描述】:

我正在尝试将一个非常简单的项目组合在一起。这里的目的是从 HackerNews API 获取一些数据并将其显示给用户。在我介绍了从 top_ids 列表中获取每个项目的功能之前,代码一直运行良好。我尝试调试它,但我没有看到任何错误。但是,我仍然没有得到所需的输出(即从 top_ids 列表中获取每个项目)

news_list_screen

Widget buildList(StoriesBloc storiesBloc) 
    return StreamBuilder(
      stream: storiesBloc.topIds,
      builder: (context, AsyncSnapshot<List<int>> snapshot) 
        if (!snapshot.hasData) 
          return Center(
            child: CircularProgressIndicator(),
          );
        
        return ListView.builder(
          itemCount: snapshot.data!.length,
          itemBuilder: (context, int index) 
            storiesBloc.fetchItem(snapshot.data![index]);
            return NewsListTile(
              itemId: snapshot.data![index],
            );
          ,
        );
      ,
    );
  


class NewsListTile extends StatelessWidget 
  final int itemId;
  NewsListTile(required this.itemId);
  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 Text('Fetching item');
        
        return FutureBuilder(
          future: snapshot.data![itemId],
          builder: (context, AsyncSnapshot<ItemModel> itemSnapshot) 
            if (!itemSnapshot.hasData) 
          //This gets populated with the correct ids
              return Text('Still loading item $itemId');
            
            return Text(itemSnapshot.data!.title);
          ,
        );
      ,
    );
  

stories_bloc

class StoriesBloc 
  final _newsRepository = NewsRepository();

  final _topIds = PublishSubject<List<int>>();
  final _itemsOutput = BehaviorSubject<Map<int, Future<ItemModel>>>();
  final _itemsFetcher = PublishSubject<int>();

  Stream<List<int>> get topIds => _topIds.stream;
  Stream<Map<int, Future<ItemModel>>> get items => _itemsOutput.stream;

  Function(int) get fetchItem => _itemsFetcher.sink.add;

  StoriesBloc() 
    _itemsFetcher.stream.transform(_itemsTransformer()).pipe(_itemsOutput);
  

  fetchTopIds() async 
    final ids = await _newsRepository.fetchTopIds();
    _topIds.sink.add(ids);
  

  _itemsTransformer() 
    return ScanStreamTransformer(
      (Map<int, Future<ItemModel>> cache, int id, index) 
        cache[id] = _newsRepository.fetchItem(id);
        return cache;
      ,
      <int, Future<ItemModel>>,
    );
  

  dispose() 
    //code
  

news_repository

class NewsRepository 
  List<Source> sources = <Source>[
    newsDbProvider,
    NewsApiProvider(),
  ];

  List<Cache> caches = <Cache>[
    newsDbProvider,
  ];

  Future<List<int>> fetchTopIds() async 
    late List<int> ids;
    Source source;

    for (source in sources) 
      ids = await source.fetchTopIds();
      if (ids.isNotEmpty) 
        break;
      
    

    return ids;
  

  Future<ItemModel> fetchItem(int id) async 
    print('itemID to be fetched inside repo - $id');
    ItemModel? item;
    var source;
    Cache cache;

    for (source in sources) 
      item = await source.fetchItem(id);
      if (item != null) 
        break;
      
    

    for (cache in caches) 
      if (cache != source)
        cache.addItem(item!);
       
    
    return item;
  

news_api_provider

class NewsApiProvider implements Source 
  Client client = Client();

  Future<List<int>> fetchTopIds() async 
    final response = await client.get(Uri.parse('$_root/topstories.json'));
    final parsedJsonIds = json.decode(response.body);
    return parsedJsonIds.cast<int>();
  

  Future<ItemModel> fetchItem(int id) async 
    print('Inside API provider. Tryna fetch $id');
    final response = await client.get(Uri.parse('$_root/item/$id.json'));
    print('Status Code' + response.statusCode.toString());
    print(response.body);
    final parsedJsonItem = json.decode(response.body);
    final itemModel = ItemModel.fromJson(parsedJsonItem);
    return itemModel;
  

现在,有趣的部分是我确实在屏幕上看到“仍在加载项目 - itemID”,但它从未解决,就好像我的 FutureBuilder 从未收到任何数据一样。但是,在调试过程中,我能够看到我的 api 确实在工作,甚至 streamTransformer 中的缓存映射也被填充了。

我仍然无法理解为什么数据没有从 streamTransformer 流到我的 itemsOutput 流,它实际上负责显示这些数据。

我非常努力地让它工作,并检查了我的代码数百万次。如果您能帮我解决这个问题,我将不胜感激。提前致谢!

【问题讨论】:

仅供参考,写在代码语句之间的打印语句显示正确的结果。 【参考方案1】:

好的。所以看起来 - 虽然 api 正在为***帖子发送一些 id,但是当我们通过 api 获取它们时这些 id 不存在(使用 fetchItem 方法)。因此,ItemModel 无法处理导致错误的此类请求。

我就是这样解决的->

ItemModel.fromJson(Map<String, dynamic> parsedJson)
      : id = parsedJson['id'] ?? 0,
        deleted = parsedJson['deleted'] ?? false,
        type = parsedJson['type'] ?? '',
        by = parsedJson['by'] ?? '',
        time = parsedJson['time'] ?? 0,
        text = parsedJson['text'] ?? '',
        dead = parsedJson['dead'] ?? false,
        parent = parsedJson['parent'] ?? 0,
        kids = parsedJson['kids'] ?? [],
        url = parsedJson['url'] ?? '',
        score = parsedJson['score'] ?? 0,
        title = parsedJson['title'] ?? 'Did not find',
        descendants = parsedJson['descendants'] ?? 0; 

【讨论】:

以上是关于流不重建小部件的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 重建父小部件

如何创建自定义颤振 sdk 小部件,重建颤振和使用新的小部件

如何知道是啥触发了我的 Flutter 小部件重建

出现键盘时重建整个小部件树

Flutter:检测任何在屏幕上不可见但在小部件树中的小部件的重建

如何调用无状态小部件的重建?