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
相关产品推荐
相关产品推荐

