You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 20:24:01