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

Flutter Web迁移:GoRouter用context.go传递自定义模型的问题

解决方案:GoRouter 迁移中使用 context.go 传递数据并解决 extra 类型丢失问题

针对你从 Navigator.push 切换到 GoRouter context.go 的场景,以及 extra 对象在浏览器导航后类型丢失的问题,提供以下几种可行方案:


方案一:使用查询参数传递数据(推荐用于Web端,支持刷新/导航保留数据)

将需要传递的参数转为字符串类型,通过URL查询参数传递,Web端刷新或前后导航时不会丢失数据。

1. 定义路由

GoRoute(
  path: '/goal-detail',
  builder: (context, state) {
    // 从路由状态中解析查询参数
    final goalId = state.queryParams['goalId']!;
    final goalTitle = state.queryParams['goalTitle']!;
    final goalUrl = state.queryParams['goalUrl']!;
    final isScreenGoal = state.queryParams['isScreenGoal'] == 'true';
    final currentStatus = GoalStatus.values.firstWhere(
      (status) => status.name == state.queryParams['currentStatus']!
    );

    return ListenableProvider(
      create: (_) => GoalDetailViewModel(
          currentStatus: currentStatus,
          goalId: goalId,
          goalTitle: goalTitle,
          goalUrl: goalUrl,
          isScreenGoal: isScreenGoal
      ),
      child: const GoalDetailScreen(),
    );
  },
)

2. 发起导航

context.go(
  '/goal-detail',
  queryParams: {
    'goalId': model.id,
    'goalTitle': model.details.title,
    'goalUrl': model.details.url,
    'isScreenGoal': (model.type.toLowerCase() == 'goal').toString(),
    'currentStatus': GoalStatus.open.name,
  },
);

方案二:修复 extra 对象的类型丢失问题

Web端导航时,GoRouter会将extra序列化为Map<String, dynamic>,因此需要给自定义参数类添加序列化/反序列化逻辑,实现类型恢复。

1. 定义可序列化的参数类

class GoalDetailParams {
  final GoalStatus currentStatus;
  final String goalId;
  final String goalTitle;
  final String goalUrl;
  final bool isScreenGoal;

  GoalDetailParams({
    required this.currentStatus,
    required this.goalId,
    required this.goalTitle,
    required this.goalUrl,
    required this.isScreenGoal,
  });

  // 转为Map用于序列化
  Map<String, dynamic> toMap() => {
        'currentStatus': currentStatus.name,
        'goalId': goalId,
        'goalTitle': goalTitle,
        'goalUrl': goalUrl,
        'isScreenGoal': isScreenGoal,
      };

  // 从Map恢复为对象
  factory GoalDetailParams.fromMap(Map<String, dynamic> map) => GoalDetailParams(
        currentStatus: GoalStatus.values.firstWhere(
          (status) => status.name == map['currentStatus']
        ),
        goalId: map['goalId'],
        goalTitle: map['goalTitle'],
        goalUrl: map['goalUrl'],
        isScreenGoal: map['isScreenGoal'],
      );
}

2. 路由中处理 extra 类型

GoRoute(
  path: '/goal-detail',
  builder: (context, state) {
    late final GoalDetailParams params;
    // 兼容原始对象和序列化后的Map
    if (state.extra is GoalDetailParams) {
      params = state.extra as GoalDetailParams;
    } else if (state.extra is Map<String, dynamic>) {
      params = GoalDetailParams.fromMap(state.extra as Map<String, dynamic>);
    } else {
      throw ArgumentError('Invalid extra data for goal detail route');
    }

    return ListenableProvider(
      create: (_) => GoalDetailViewModel(
          currentStatus: params.currentStatus,
          goalId: params.goalId,
          goalTitle: params.goalTitle,
          goalUrl: params.goalUrl,
          isScreenGoal: params.isScreenGoal
      ),
      child: const GoalDetailScreen(),
    );
  },
)

3. 发起导航

context.go(
  '/goal-detail',
  extra: GoalDetailParams(
    currentStatus: GoalStatus.open,
    goalId: model.id,
    goalTitle: model.details.title,
    goalUrl: model.details.url,
    isScreenGoal: model.type.toLowerCase() == 'goal',
  ),
);

方案三:结合状态管理传递数据(适合已有全局状态的场景)

如果目标页面需要的数据已存在于上层状态管理(如你使用的Provider)中,可仅传递唯一标识(如goalId),在目标页面通过状态管理获取完整数据。

1. 定义路由

GoRoute(
  path: '/goal-detail/:goalId',
  builder: (context, state) {
    final goalId = state.pathParameters['goalId']!;
    // 从全局Provider中获取对应model
    final model = context.read<YourGlobalProvider>().getModelById(goalId);

    return ListenableProvider(
      create: (_) => GoalDetailViewModel(
          currentStatus: GoalStatus.open,
          goalId: model.id,
          goalTitle: model.details.title,
          goalUrl: model.details.url,
          isScreenGoal: model.type.toLowerCase() == 'goal'
      ),
      child: const GoalDetailScreen(),
    );
  },
)

2. 发起导航

context.go('/goal-detail/${model.id}');

内容的提问来源于stack exchange,提问作者hemant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 22:55:06