Flutter setState触发重载问题:仅需更新底部琥珀色容器
解决Flutter点击列表项仅更新底部容器不重载FutureBuilder的问题
问题分析
当前代码存在两个核心问题:
- FutureBuilder重复执行查询:直接将
read()调用写在future参数中,每次setState触发组件重建时,都会重新执行数据库查询,导致列表重新加载。 - 全局setState触发全量重建:调用
setState会让整个页面的子Widget都重建,包括不需要更新的列表部分。
解决方案
1. 缓存Future避免重复查询
在组件初始化时就执行数据库查询并缓存Future,确保只查询一次:
在State类中添加变量并初始化:
late Future<List?> _productsFuture; @override void initState() { super.initState(); // 仅在组件初始化时执行一次查询 _productsFuture = read( "SELECT ProductAdress, ProductName, NeedCount, ScanCount FROM ScanRest WHERE ProductStation = '${widget.nrStatie}' AND BoxID = '${cutieScan}' ORDER BY ProductName ASC;" ); }
修改FutureBuilder的future参数:
Expanded( child: FutureBuilder<List?>( future: _productsFuture, // 使用缓存的Future builder: (context, snapshot) { // 原有builder逻辑保持不变 switch (snapshot.connectionState) { case ConnectionState.waiting: return const Text('Loading....'); default: if (snapshot.hasError) { print("call error"); return Text('Error: ${snapshot.error}'); } else { print("call success = ${snapshot.data}"); List data = snapshot.data ?? []; return ListView.builder( itemCount: data.length, itemBuilder: (context, index) { return Card( elevation: 2, child: GestureDetector( // 后续修改点击逻辑,见步骤2 onTap: () {}, child: Row(children: [ Expanded( flex: 2, child: Text((data[index] as Map)['ProductAdress'].toString()), ), Expanded( flex: 5, child: Text((data[index] as Map)['ProductName'].toString()), ), Expanded( flex: 2, child: Center(child: Text((data[index] as Map)['NeedCount'].toString())), ), Expanded( flex: 2, child: Center(child: Text((data[index] as Map)['ScanCount'].toString())), ), ]), ), ); }, ); } } }, ), )
2. 用ValueNotifier局部更新底部容器
使用ValueNotifier存储选中的产品信息,配合ValueListenableBuilder仅更新底部容器,避免全局setState:
在State类中定义ValueNotifier:
final ValueNotifier<Map<String, String>> _selectedProduct = ValueNotifier({ 'test': '', 'denumProdus': '', });
修改列表项的点击逻辑:
onTap: () { // 更新ValueNotifier的值,而非调用setState _selectedProduct.value = { 'test': (data[index] as Map)['ProductName'].toString(), 'denumProdus': (data[index] as Map)['ProductSeriesDescr'].toString(), }; },
修改底部容器为ValueListenableBuilder包裹:
Visibility( visible: !vizibil, child: ValueListenableBuilder<Map<String, String>>( valueListenable: _selectedProduct, builder: (context, selectedValue, child) { return Container( color: Colors.amber, width: double.infinity, height: 100, child: Center( child: Column( children: [ Text(selectedValue['test']!), Text(selectedValue['denumProdus']!) ], ), ), ); }, ), )
效果说明
修改后:
- 数据库查询仅在组件初始化时执行一次,后续不会重复触发查询
- 点击列表项时,仅底部琥珀色容器会更新,列表部分不会重建或重新加载
内容的提问来源于stack exchange,提问作者Dima C
相关产品推荐
相关产品推荐

