Flutter Web中GoRouter调用State Notifier触发构建报错问题求助
Flutter Web深度链接路由切换时SetState构建报错解决
问题背景
需要实现Flutter Web深度链接功能:用户访问www.myapp.com/#/projects/{projectShortId}时,GoRouter提取projectShortId参数,传入State Notifier加载对应项目详情。现有实现代码如下:
class MyApp extends StatelessWidget { MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp.router( routeInformationProvider: _router.routeInformationProvider, routeInformationParser: _router.routeInformationParser, routerDelegate: _router.routerDelegate, ); } final GoRouter _router = GoRouter( initialLocation: '/login', routes: <GoRoute>[ GoRoute(path: '/', builder: (context, state) => LoginSignupPage()), GoRoute(path: '/login', builder: (context, state) => LoginSignupPage()), GoRoute( path: '/projects/:projectShortId', builder: (context, state) { var projectShortId = state.params['projectShortId']!; return Consumer( builder: (context, ref, child) { ref .read(projectStateNotifierProvider.notifier) .setProjectFromCustomURI(projectShortId); return ViewProjectPage(); }, ); }, ) ], ); }
报错现象
首次访问/projects/{userId1}正常,但切换到/projects/{userId2}时,触发构建错误:
Error: setState() or markNeedsBuild() called during build. C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49 throw_ packages/flutter/src/widgets/framework.dart 4549:11 <fn> packages/flutter/src/widgets/framework.dart 4563:14 markNeedsBuild packages/flutter_riverpod/src/framework.dart 287:5 [_debugCanModifyProviders] packages/riverpod/src/framework/provider_base.dart 513:42 <fn> packages/riverpod/src/framework/provider_base.dart 337:7 setState packages/riverpod/src/state_notifier_provider/auto_dispose.dart 118:10 listener packages/state_notifier/state_notifier.dart 225:23 set state packages/myapp_mvp/state/providers/project_state_provider/project_state_provider.dart 33:5 setProjectFromCustomURI
问题原因
报错本质是在Widget构建流程中直接修改了状态。切换路由时,Consumer的builder方法正在执行构建逻辑,此时调用setProjectFromCustomURI会立即修改State Notifier的状态,触发关联Widget的重建操作,而Flutter禁止在构建期间发起状态变更,因此抛出异常。
解决方案
方案1:延迟到当前帧构建完成后执行状态更新
使用WidgetsBinding.instance.addPostFrameCallback,将状态修改逻辑放到当前Widget帧构建完成后执行,避开构建流程:
GoRoute( path: '/projects/:projectShortId', builder: (context, state) { var projectShortId = state.params['projectShortId']!; return Consumer( builder: (context, ref, child) { WidgetsBinding.instance.addPostFrameCallback((_) { ref .read(projectStateNotifierProvider.notifier) .setProjectFromCustomURI(projectShortId); }); return ViewProjectPage(); }, ); }, )
方案2:监听路由参数变化触发更新(更优雅)
通过ref.listen监听路由参数的变化,仅当参数实际改变时才执行状态更新,避免重复操作:
GoRoute( path: '/projects/:projectShortId', builder: (context, state) { var projectShortId = state.params['projectShortId']!; return Consumer( builder: (context, ref, child) { // 监听参数变化,仅在参数更新时执行 ref.listen( Provider((ref) => projectShortId), (prevId, newId) { if (prevId != newId) { ref .read(projectStateNotifierProvider.notifier) .setProjectFromCustomURI(newId); } }, ); return ViewProjectPage(); }, ); }, )
额外优化建议
- 若
projectStateNotifierProvider使用了autoDispose修饰,确保路由切换时状态能正确重置或更新,避免残留旧数据。 - 可将状态监听逻辑迁移到
ViewProjectPage内部,让路由仅负责页面跳转,页面自身处理状态加载,职责划分更清晰。
内容的提问来源于stack exchange,提问作者Shazamo Morebucks
相关产品推荐
相关产品推荐

