如何根据选中的GoRouter路由动态修改AppBar标题?
解决方案
问题核心是在Widget的build方法(GoRoute的builder回调)中直接修改Provider状态,此时Flutter正处于构建流程中,更新状态会触发依赖组件重新构建,导致冲突。以下是几种可行的解决方式:
方法一:利用GoRouter元数据传递标题(推荐)
直接通过GoRouter的路由元数据携带标题,无需Riverpod中转,是最简洁高效的方案:
- 路由配置时添加标题元数据
GoRoute( path: DashboardScreenWeb.routeLocation, name: DashboardScreenWeb.routeName, // 用meta存储页面标题 meta: {'title': DashboardScreenWeb.title}, builder: (context, state) => const DashboardScreenWeb(), ), GoRoute( path: BusinessDataScreen.routeLocation, name: BusinessDataScreen.routeName, meta: {'title': BusinessDataScreen.title}, builder: (context, state) => const BusinessDataScreen(), ),
- 在ShellRoute中读取元数据并传递给AppBar
ShellRoute( builder: (BuildContext context, GoRouterState state, Widget child) { // 从当前路由的meta中获取标题,设置默认值避免空指针 final currentTitle = state.meta['title'] ?? '默认标题'; return Scaffold( body: child, appBar: CustomAppBar(title: currentTitle), ); }, routes: [...], )
- 简化CustomAppBar为普通无状态组件
class CustomAppBar extends StatelessWidget { final String title; const CustomAppBar({super.key, required this.title}); @override Widget build(BuildContext context) { return AppBar( title: Text(title), ); } }
方法二:延迟更新Riverpod状态(保留Riverpod)
如果必须使用Riverpod,避免在构建流程中直接修改状态,改用WidgetsBinding延迟到构建完成后更新:
修改GoRoute的builder回调:
GoRoute( path: DashboardScreenWeb.routeLocation, name: DashboardScreenWeb.routeName, builder: (context, state) { // 延迟到当前帧构建完成后再更新状态 WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(titleProvider.notifier).state = DashboardScreenWeb.title; }); return const DashboardScreenWeb(); }, ),
或者通过监听路由状态自动更新标题(更优雅):
首先定义一个获取当前GoRouterState的Provider:
final goRouterStateProvider = Provider<GoRouterState>((ref) { final router = ref.watch(goRouterProvider); return router.routerDelegate.currentConfiguration; });
然后在ShellRoute的builder中添加监听:
ShellRoute( builder: (BuildContext context, GoRouterState state, Widget child) { final ref = ProviderScope.containerOf(context); // 监听路由变化,自动更新标题 ref.listen<GoRouterState>( goRouterStateProvider, (previous, current) { final title = switch(current.name) { DashboardScreenWeb.routeName => DashboardScreenWeb.title, BusinessDataScreen.routeName => BusinessDataScreen.title, _ => '默认标题', }; ref.read(titleProvider.notifier).state = title; }, fireImmediately: true, // 首次加载时触发一次 ); return Scaffold( body: child, appBar: CustomAppBar(), ); }, routes: [...], )
内容的提问来源于stack exchange,提问作者schneida
相关产品推荐
相关产品推荐

