我有一个类别的动态列表。
我有每个类别的动态产品列表。
我的照明视图生成器垂直显示类别列表。现在,对于每个类别,它都会向我的服务器请求获取并显示该类别下的产品。
这是可行的,但是垂直滚动是不稳定的,我在调试控制台中经常看到以下错误:
════════ 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;
}
我认为您面临的问题是在构建方法中调用您的未来,如下所示:
future: vm.getProducts(category)
如果你看一下FutureBuilder的文档,上面写着:
未来必须更早获得,例如State.initState、State.didUpdateWidget或State.didChangeDependencies。在构造FutureBuilder。如果未来与FutureBuilder,那么每次重建FutureBuilders的父级时,异步任务将被重新启动。
您应该在构建方法之前声明Future变量,然后在futurebuilder中传递该变量,这样它就可以在每次重建时被调用。
例如
Future myFuture = vm.getProducts(category);
@override
Widget build(BuildContext context, vm) {
return FutureBuilder(
future: myFuture,
builder: (context, snapshot) {
...
Henok为您指明了正确的方向,在setBusyForObject方法中调用了setState,因为FutureBuilder使用不当而调用了该方法。