【问题标题】: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 流,它实际上负责显示这些数据。

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

【问题讨论】:

  • 仅供参考,写在代码语句之间的打印语句显示正确的结果。

标签: flutter dart stream bloc dart-async


【解决方案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; 

【讨论】:

    猜你喜欢
    • 2021-09-19
    • 2019-12-09
    • 1970-01-01
    • 2020-03-14
    • 2019-01-14
    • 2019-08-23
    • 2020-07-25
    • 1970-01-01
    • 2019-12-09
    相关资源
    最近更新 更多