Flutter从详情页返回列表页重获数据报错:已停用Widget祖先查找不安全
问题分析与解决方案
核心原因
你遇到的报错是因为ReorderableListView的子组件_Exercise属于动态列表项,在页面跳转或列表重排时可能被销毁/卸载,当从详情页返回触发then回调时,子组件的context已失效(标记为DEFUNCT),无法通过它查找祖先的Bloc实例。而页面的FloatingActionButton是页面根Widget的直接子组件,其context属于页面本身,不会轻易被销毁,因此能正常工作。
可行解决方案
方案1:传递页面级Context到子组件
将列表页面的context用于跳转和回调逻辑,避免依赖子组件的失效context。
// 列表页面Widget class ExerciseListPage extends StatelessWidget { final String clientId; final DateTime date; const ExerciseListPage({super.key, required this.clientId, required this.date}); // 页面级的跳转回调方法 void _goToDetailAndRefresh(BuildContext context, Exercise exercise) { context.pushNamed('/exercise-detail', arguments: exercise).then((_) { // 使用页面context获取Bloc,确保有效性 context.read<TrainerExerciseListBloc>().add( TrainerExerciseListEvent.fetchExercises(clientId, date), ); }); } @override Widget build(BuildContext context) { return Scaffold( body: ReorderableListView( children: exercises.map((exercise) { return _Exercise( key: Key(exercise.id), exercise: exercise, // 将页面级方法传递给子组件 onTap: () => _goToDetailAndRefresh(context, exercise), ); }).toList(), ), ); } } // 子组件_Exercise class _Exercise extends StatelessWidget { final Exercise exercise; final VoidCallback onTap; const _Exercise({super.key, required this.exercise, required this.onTap}); @override Widget build(BuildContext context) { return ListTile( onTap: onTap, // 直接调用页面传递的方法 title: Text(exercise.name), ); } }
方案2:使用GlobalKey获取有效Context
在页面根Widget创建GlobalKey,通过它获取始终有效的页面context,避免依赖子组件context。
class ExerciseListPage extends StatelessWidget { final GlobalKey<ScaffoldState> _pageKey = GlobalKey<ScaffoldState>(); final String clientId; final DateTime date; const ExerciseListPage({super.key, required this.clientId, required this.date}); @override Widget build(BuildContext context) { return Scaffold( key: _pageKey, body: ReorderableListView( children: exercises.map((exercise) { return _Exercise( key: Key(exercise.id), exercise: exercise, onTap: () { _pageKey.currentContext!.pushNamed('/exercise-detail', arguments: exercise).then((_) { // 通过GlobalKey获取页面context _pageKey.currentContext!.read<TrainerExerciseListBloc>().add( TrainerExerciseListEvent.fetchExercises(clientId, date), ); }); }, ); }).toList(), ), ); } }
方案3:直接获取Bloc实例(不推荐,但应急可用)
如果必须在子组件内处理,可通过BlocProvider.of并关闭监听来获取Bloc,但仍需确保context未被销毁,优先级低于前两种方案:
// 子组件内的onTap逻辑 onTap: () { context.pushNamed('/exercise-detail', arguments: exercise).then((_) { // 跳过监听直接获取Bloc实例 final bloc = BlocProvider.of<TrainerExerciseListBloc>(context, listen: false); bloc.add(TrainerExerciseListEvent.fetchExercises(_clientId, _date)); }); },
内容的提问来源于stack exchange,提问作者Belz123PL
相关产品推荐
相关产品推荐

