You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 03:57:25