嵌套水平列表视图

时间:2020-09-15 16:32:21

标签: flutter stacked

我有一个动态的类别列表。

我为每个类别都有一个动态的产品列表。

我的点亮视图构建器垂直显示类别列表。现在,对于每个类别,它都向我的服务器发出请求,以获取并显示该类别下的产品。

这有效,但是垂直滚动比较不稳定,我在调试控制台中经常看到以下错误:

════════ Exception caught by foundation library ════════════════════════════════
setState() or markNeedsBuild() called during build.
════════════════════════════════════════════════════════════════════════════════
D/ViewRootImpl@c4f27c2[MainActivity]( 4296): ViewPostIme pointer 0
D/ViewRootImpl@c4f27c2[MainActivity]( 4296): ViewPostIme pointer 1
Another exception was thrown: setState() or markNeedsBuild() called during build.

现在这告诉我我在这里做错了。

这是我的产品视图,我在加载时从服务器获取产品类别:

class ProductsView extends ViewModelBuilderWidget<ProductsViewModel> {
  @override
  bool get reactive => true;

  @override
  bool get createNewModelOnInsert => false;

  @override
  bool get disposeViewModel => true;

  @override
  void onViewModelReady(ProductsViewModel vm) {
    vm.getCategories();
    super.onViewModelReady(vm);
  }

  @override
  Widget builder(BuildContext context, vm, Widget child) {
    return Scaffold(
        body: SafeArea(
            child: Container(
      padding: EdgeInsets.all(1),
      child: vm.isBusy
          ? ShimmerList(
              type: ShimmerType.list,
              count: 5,
            )
          : ListView.builder(
              itemCount: vm.categories.length,
              itemBuilder: (context, index) {
                return CategoryItem(category: vm.categories[index]);
              },
            ),
    )));
  }

  @override
  ProductsViewModel viewModelBuilder(BuildContext context) =>
      ProductsViewModel();
}

这是类别项目小部件:

class CategoryItem extends ViewModelWidget<ProductsViewModel> {
  final Category category;

  const CategoryItem({Key key, this.category})
      : super(key: key, reactive: false);

  @override
  Widget build(BuildContext context, vm) {
    return Column(children: [
      Padding(
        padding: const EdgeInsets.all(15),
        child:
            Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
          Text(category.name, style: TextStyle(fontSize: 18)),
          InkWell(
            child: Text(
              'See All',
              textAlign: TextAlign.end,
              style: TextStyle(color: Color(0xFF8BC34A), fontSize: 18),
            ),
            onTap: () {},
          )
        ]),
      ),
      ProductsList(
        category: category,
      )
    ]);
  }
}

这是我的产品列表视图: 对于每种类别,我都向服务器请求获取基础产品。

class ProductsList extends ViewModelWidget<ProductsViewModel> {
  final Category category;

  const ProductsList({Key key, this.category}) : super(key: key, reactive: false);

  @override
  Widget build(BuildContext context, vm) {
    return FutureBuilder(
        future: vm.getProducts(category),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            List<Product> products = snapshot.data;
            return Container(
                height: 190,
                child: ListView.builder(
                  scrollDirection: Axis.horizontal,
                  itemCount: products.length,
                  itemBuilder: (context, index) {
                    return Column(
                      children: [
                        Container(
                          margin: EdgeInsets.only(left: 8, right: 8, bottom: 8),
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(8),
                            boxShadow: [
                              BoxShadow(
                                color: Colors.black26,
                                blurRadius: 2.0,
                                spreadRadius: 0.0,
                                offset: Offset(
                                    2.0, 2.0), // shadow direction: bottom right
                              )
                            ],
                          ),
                          child: ClipRRect(
                              borderRadius:
                                  BorderRadius.all(Radius.circular(5)),
                              child: Image.network(
                                products[index].thumbnail,
                                width: 110,
                                height: 160,
                                fit: BoxFit.cover,
                              )),
                        ),
                        products[index].currentPrice > 0
                            ? Text('GHS ${products[index].currentPrice}')
                            : Text('Free')
                      ],
                    );
                  },
                ));
          } else {
            return Text('Hey');
          }
        });
  }
}

我正在使用将来的构建器来获取并返回每个类别的结果,并显示产品的水平列表。

我现在想知道的是如何正确有效地实现此功能,这样我就不会出现上述错误,并且我的用户可以获得流畅的滚动体验。

Future<List<Product>> getProducts(Category category) async {
  setBusyForObject(category, true);
  List<Book> books;
  if (productsByCategories[category.slug] == null) {
    books = await _productService.getPublishedByCategory(category.slug);
    productsByCategories[category.slug] = books;
    setBusyForObject(category, false);
    return products;
  } else {
    products = productsByCategories[category.slug];
  }

  return products;
}

1 个答案:

答案 0 :(得分:1)

我认为您面临的问题是像这样在构建方法中调用您的未来:

 future: vm.getProducts(category)

如果您查看 documentation of FutureBuilder,它会说:

<块引用>

future 一定是更早获得的,例如中 State.initState、State.didUpdateWidget 或 State.didChangeDependencies。不得在创建期间创建 State.build 或 StatelessWidget.build 方法在构造时调用 未来建设者。如果未来与 FutureBuilder,那么每次重建FutureBuilder的父级时, 异步任务将重新启动。

您应该在构建方法之前声明 Future 变量,然后在未来构建器中传递该变量,这样它就可以在每次重建时调用。

例如

Future myFuture = vm.getProducts(category);

@override
 Widget build(BuildContext context, vm) {
   return FutureBuilder(
       future: myFuture,
       builder: (context, snapshot) {
       ...

Henok 为您指明了正确的方向,setState 在 setBusyForObject 方法中被调用,该方法是因为 FutureBuilder 使用不当而被调用。

相关问题