我有一个动态的类别列表。
我为每个类别都有一个动态的产品列表。
我的点亮视图构建器垂直显示类别列表。现在,对于每个类别,它都向我的服务器发出请求,以获取并显示该类别下的产品。
这有效,但是垂直滚动比较不稳定,我在调试控制台中经常看到以下错误:
════════ 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;
}
答案 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 使用不当而被调用。