Flutter中FutureBuilder二次点击按钮时Hive Key为空问题求助
问题根源与修复方案
核心问题分析
- 列表项构建时重复初始化状态列表:在
itemBuilder中每次构建列表项都会遍历整个数据集,往taskIconCheckList中添加元素,最终列表长度变为数据量×数据量,导致状态与数据索引完全不匹配,触发后续流程异常。 - FutureBuilder重复执行异步请求:未缓存
showTaskList()的结果,每次组件重建都会重新发起Hive查询,导致新旧数据混杂,点击时可能拿到过时的对象。 - Task更新逻辑冗余且易出错:通过
where查询目标Task效率低,新建Task对象时丢失原对象的user和taskCat字段,且手动构建对象可能导致Hive的key关联异常。
具体修复步骤
1. 缓存FutureBuilder的异步请求
在页面的initState中缓存异步请求结果,避免每次组件重建都重新查询Hive:
late Future<List<Task>> taskListFuture; @override void initState() { super.initState(); taskListFuture = controller.showTaskList(); }
2. 修复状态列表初始化逻辑
将taskIconCheckList的初始化移到FutureBuilder获取数据后,仅在列表为空或长度变化时执行,确保状态与数据一一对应:
FutureBuilder<List<Task>>( future: taskListFuture, builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return SizedBox( height: Get.height, child: const Center(child: CircularProgressIndicator()), ); default: if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else { final List<Task> data = snapshot.data ?? []; // 仅在必要时初始化状态列表 if (controller.taskIconCheckList.isEmpty || controller.taskIconCheckList.length != data.length) { controller.taskIconCheckList.clear(); for (final task in data) { controller.taskIconCheckList.add(task.status.obs); } } return ListView.separated( scrollDirection: Axis.vertical, physics: const BouncingScrollPhysics(), shrinkWrap: true, itemCount: data.length, itemBuilder: (context, index) { final task = data[index]; return ListTile( leading: Obx( () => PageTransitionSwitcher( transitionBuilder: (child, primaryAnimation, secondaryAnimation) { return SharedAxisTransition( animation: primaryAnimation, secondaryAnimation: secondaryAnimation, transitionType: SharedAxisTransitionType.horizontal, fillColor: Colors.transparent, child: child, ); }, duration: const Duration(milliseconds: 800), child: controller.taskIconCheckList[index].value ? IconButton( icon: const Icon(Icons.check_circle_rounded, color: Colors.lightGreenAccent), onPressed: () { controller.functionTaskIconCheckList(index); controller.updateStatusTask(index, task.key!); }, ) : IconButton( icon: const Icon(Icons.radio_button_unchecked_outlined, color: Colors.red), onPressed: () { controller.functionTaskIconCheckList(index); controller.updateStatusTask(index, task.key!); }, ), ), ), title: Text(task.taskText, style: normalTextForCategory), subtitle: Text( task.date.toString().substring(0, 10), textDirection: TextDirection.ltr, textAlign: TextAlign.right, style: normalTextForSubtitle, ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: () { myDefaultDialog( 'هشدار', 'آیا از حذف این گزینه اطمینان دارید؟', 'بله', 'خیر', () { Get.back(); mySnakeBar( '', 'گزینه مورد نظر با موفقیت حذف شد.', Icons.warning_amber_rounded, Colors.yellow, ); }, ); }, icon: const Icon(Icons.delete), color: Colors.redAccent, ), IconButton( onPressed: () { Get.offNamed(Routs.editTaskScreen, arguments: 'edit'); }, icon: const Icon(Icons.edit_calendar, color: Colors.yellowAccent), ), ], ), ); }, separatorBuilder: (context, index) => const Divider(height: 2, color: Colors.white70), ); } } }, )
3. 优化Task更新函数
直接获取目标Task并修改状态后保存,无需新建对象,避免key关联异常:
Future<void> updateStatusTask(int index, int taskKey) async { final taskBox = await Hive.openBox('task'); final Task? task = taskBox.get(taskKey) as Task?; if (task == null) return; // 修改状态并自动同步到Hive task.status = taskIconCheckList[index].value; await task.save(); }
4. 明确showTaskList的返回类型
指定返回List<Task>,避免类型不确定导致的潜在问题:
Future<List<Task>> showTaskList() async { final sharedPreferences = await SharedPreferences.getInstance(); final userId = sharedPreferences.getInt('key'); if (userId == null) return []; final taskBox = await Hive.openBox('task'); return taskBox.values.where((task) => task.userId == userId).cast<Task>().toList(); }
修复原理
通过缓存异步请求避免重复查询,确保状态列表与数据一一对应,同时使用HiveObject的原生save()方法更新数据,避免手动构建对象导致的key丢失问题,彻底解决二次点击时key为null的异常。
内容的提问来源于stack exchange,提问作者sadegh
相关产品推荐
相关产品推荐

