如何在NavigationRail中调用自定义函数后正确更新selectedIndex
问题原因
- 你当前把
selectedIndex状态存储在_SidebarDrawerState内部,而你的路由跳转使用的是Navigator.pushNamed,每次跳转都会加载新的页面实例,如果每个页面都单独引用了SidebarDrawer组件,新页面里的侧边栏是全新初始化的,内部的index变量默认值就是0,自然会被重置。 - 另外你使用
pushNamed是往路由栈里叠加页面,不是替换当前页面,多次跳转后路由栈会越来越长,返回时会逐个弹出之前的页面,不符合侧边栏全局导航的使用场景。
修复方案
方案1:将选中状态提升到上层共同父组件(推荐)
把selectedIndex的状态存放在承载所有导航页面的上层Widget(比如全局主页Scaffold父组件),通过参数传递给SidebarDrawer,同时把更新索引的方法也从上层传递下来,不要把状态存在侧边栏内部。这种方案性能最优,只需要实例化一次侧边栏,切换页面时不会重建侧边栏,状态会统一保留。
示例代码如下:
// 上层公共父组件,作为全局导航承载页 class MainPage extends StatefulWidget { @override _MainPageState createState() => _MainPageState(); } class _MainPageState extends State<MainPage> { int _selectedIndex = 0; // 预先定义所有导航对应的页面 final List<Widget> _navPages = [ HomePage(), OrderManagerPage(), ManualControlPage(), ]; @override Widget build(BuildContext context) { return Scaffold( body: Row( children: [ // 侧边栏只实例化一次 SidebarDrawer( selectedIndex: _selectedIndex, onIndexUpdate: (newIndex) { setState(() => _selectedIndex = newIndex); }, callbackOnScreenChange: () { // 你的页面切换回调逻辑 }, ), // 页面展示区域,根据选中索引切换 Expanded(child: _navPages[_selectedIndex]), ], ), ); } }
改造后的SidebarDrawer代码:
class SidebarDrawer extends StatefulWidget { final int selectedIndex; final Function(int) onIndexUpdate; final VoidCallback callbackOnScreenChange; SidebarDrawer({ @required this.selectedIndex, @required this.onIndexUpdate, @required this.callbackOnScreenChange, }); @override _SidebarDrawerState createState() => _SidebarDrawerState(); } class _SidebarDrawerState extends State<SidebarDrawer> { // 把展开状态存在侧边栏内部即可,不需要提至上层 bool isExtended = false; @override Widget build(BuildContext context) => NavigationRail( extended: isExtended, onDestinationSelected: (index) => updateRoute(index), // 用外部传入的选中索引,而非内部变量 selectedIndex: widget.selectedIndex, leading: IconButton( icon: Icon(Icons.reorder), onPressed: () => setState(() => isExtended = !isExtended), ), destinations: <NavigationRailDestination>[ // 你的NavigationRailDestination配置 ], ); void updateRoute(int index) { // 调用上层方法更新选中索引 widget.onIndexUpdate(index); if (widget.callbackOnScreenChange != null) { widget.callbackOnScreenChange(); } } }
该方案不需要再使用navigateToScreen做路由跳转,切换页面仅修改展示的子组件即可,性能更好也不会出现状态重置问题。
方案2:改造路由跳转逻辑
如果你一定要用命名路由跳转,就把pushNamed改成pushReplacementNamed,同时在跳转时把选中的索引作为参数传递给新页面,新页面初始化时把索引传给侧边栏。该方案性能不如方案1,每次跳转都会重建整个页面,仅适合特殊场景使用。
额外注意点
你代码中的isExtended变量没有声明在State中,记得补充该变量的定义,否则运行时会报错。
内容的提问来源于stack exchange,提问作者Simon LE FOURN
相关产品推荐
相关产品推荐

