Flutter中FutureBuilder的ListView.builder删除条目后如何更新UI?
功能实现方案
问题根因
你遇到的现象由以下几个错误写法共同导致:
- 直接修改
AsyncSnapshot的data属性:该数据是Future执行完成后的只读快照,且你每次触发setState页面重绘时,FutureBuilder都会重新执行_myFutureBuilder()从数据库拉取旧数据,覆盖你对快照的修改 - 给
ListView.builder加了UniqueKey:每次setState都会强制整个列表全量重建,无法实现增量条目更新 - 数据库返回的
QueryResultSet属于不可变集合,直接调用removeAt方法不会实际修改集合内容
修复步骤
- 在State类中新增本地可变列表缓存数据源,同时新增变量存储Future实例,避免每次重绘重复执行异步请求
- 拿到异步返回的查询结果后,先转换为可变List再赋值给本地缓存
- 列表数据源完全使用本地缓存,删除操作先执行数据库删除逻辑,成功后再修改本地缓存触发UI更新
- 移除不必要的
UniqueKey,可根据条目唯一标识设置ValueKey优化列表渲染性能
完整修改示例
首先是State类的变量与初始化部分:
class _YourPageState extends State<YourPage> { // 存储异步请求实例,避免重复执行 late Future _dataFuture; // 本地可变数据源,所有UI渲染、列表操作都基于该变量 List _installedMuseums = []; @override void initState() { super.initState(); // 页面初始化时仅执行一次异步请求 _dataFuture = _myFutureBuilder(); }
然后是FutureBuilder部分的修改:
FutureBuilder( future: _dataFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } else if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { return const Text('Error'); } else if (snapshot.hasData) { // 将不可变的查询结果转为可变List赋值给本地缓存 _installedMuseums = List.from(snapshot.data); return Padding( padding: const EdgeInsets.all(20.0), child: Column( children: [ Flexible( child: ListView.builder( // 移除UniqueKey,避免全量重建 itemCount: _installedMuseums.length, itemBuilder: (context, index) { final currentItem = _installedMuseums[index]; return Card( elevation: 4, // 用条目唯一标识做key,优化渲染性能 key: ValueKey(currentItem['name']), child: ListTile( trailing: const Icon(Icons.delete), title: Text(currentItem["name"]), onTap: () async { // 先执行数据库删除操作 await _deleteOps(currentItem["name"]); // 删除成功后修改本地缓存触发UI更新 setState(() { _installedMuseums.removeAt(index); }); }, ), ); }, ), ), ], ), ); } else { return const Text('Empty data'); } } else { return Padding( padding: const EdgeInsets.all(20.0), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: const [ CircularProgressIndicator(), SizedBox(height: 20), Text( "loading...", style: TextStyle(fontWeight: FontWeight.bold), ), ], ), ), ); } } )
可选优化方案
如果你的数据量不大,不想维护本地缓存,可以删除成功后直接重新触发一次数据库查询:
onTap: () async { await _deleteOps(currentItem["name"]); setState(() { // 重新执行异步请求拉取最新数据 _dataFuture = _myFutureBuilder(); }); }
内容的提问来源于stack exchange,提问作者RexLudus
相关产品推荐
相关产品推荐

