Flutter+Beamer+Riverpod热重载触发GlobalKey重复使用错误
问题:Beamer+Riverpod实现标签页热重载时GlobalKey冲突
场景说明
用Beamer实现带独立导航栈的底部标签页,标签列表由Riverpod的routeProvider提供,选中状态由tabSelectionProvider监听,逻辑和Beamer官方示例类似,但标签不是硬编码的。
报错信息
热重载时触发以下错误:
Multiple widgets used the same GlobalKey.
The key [GlobalKey#e2e03 beamer-/hq] was used by multiple widgets. The parents of those widgets were:
- IndexedStack(alignment: AlignmentDirectional.topStart, fit: loose, dependencies: [Directionality], renderObject: RenderIndexedStack#51cdd relayoutBoundary=up1 NEEDS-PAINT)
- IndexedStack(alignment: AlignmentDirectional.topStart, fit: loose, dependencies: [Directionality], renderObject: RenderIndexedStack#090dd relayoutBoundary=up1)
A GlobalKey can only be specified on one widget at a time in the widget tree.
问题分析
核心是根节点的IndexedStack包含多个Beamer组件,已经通过全局缓存beamerCache和delegateCache确保每个标签的Beamer和Delegate只创建一次,保留导航状态。但热重载时,build方法返回新的IndexedStack,旧的IndexedStack未被及时销毁,导致新旧栈中的同一个Beamer(复用了GlobalKey)同时存在于 widget 树中,触发冲突。怀疑是竞态条件或引用循环导致旧组件未释放。
实现代码
Map<String, Beamer> beamerCache = {}; Map<String, BeamerDelegate> delegateCache = {}; /// A widget class that shows the BottomNavigationBar and performs navigation /// between tabs class ScaffoldWithBottomNavBar extends ConsumerWidget { const ScaffoldWithBottomNavBar({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final routes = ref.watch(routeProvider); final selectedTab = ref.watch(tabSelectionProvider).selectedTab; final tabs = routes.tabs; final theme = Theme.of(context); final delegates = [for (var tab in tabs) makeDelegate(tab)]; return Scaffold( body: IndexedStack( index: selectedTab, children: [ for (var delegate in delegates) makeBeamer(delegate.initialPath, delegate) ], ), bottomNavigationBar: BottomNavigationBar( currentIndex: selectedTab, items: [ for (var tab in tabs) BottomNavigationBarItem(label: tab.label, icon: Icon(tab.icon)) ], selectedItemColor: theme.toggleableActiveColor, unselectedItemColor: theme.primaryColorDark, onTap: (index) { if (index != selectedTab) { ref.read(tabSelectionProvider.notifier).setTab(index); delegates[index].update(rebuild: false); } }, ), ); } BeamerDelegate makeDelegate(TabInfo tab) { var delegate = delegateCache[tab.path]; if (delegate != null) { return delegate; } delegate = BeamerDelegate( initialPath: tab.path, locationBuilder: (routeInformation, parameters) { if (routeInformation.location!.contains(tab.path)) { return TabLocation(routeInformation, tab); } return NotFound(path: routeInformation.location!); }); delegateCache[tab.path] = delegate; return delegate; } Beamer makeBeamer(String path, BeamerDelegate delegate) { var beamer = beamerCache[path]; if (beamer != null) { return beamer; } beamer = Beamer( key: GlobalKey(debugLabel: "beamer-$path"), routerDelegate: delegate, ); beamerCache[path] = beamer; return beamer; } }
求解决方案或调试建议
内容的提问来源于stack exchange,提问作者Sam Deane
相关产品推荐
相关产品推荐

