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

如何在Navigator路由间保持NavigationDrawer组件持久化?

解决方案:将抽屉持久化固定在大屏左侧

问题根源

你当前的实现是在每个路由页面中都嵌入CustomDrawer,导致路由切换时抽屉会被重新构建,不仅丢失滚动状态,还会跟着页面切换动效一起变化。

核心思路

把CustomDrawer从路由内部抽离,作为App根布局的固定组件,仅在根布局中初始化一次;右侧区域单独用Navigator管理所有页面内容,页面切换只更新右侧区域,抽屉全程保持持久化。

具体实现代码

1. 重构App根布局

class AppRoot extends StatelessWidget {
  const AppRoot({super.key});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        // 抽屉仅初始化一次,全程持久化
        const CustomDrawer(),
        // 右侧区域用Expanded包裹Navigator,所有页面切换在这里完成
        Expanded(
          child: Navigator(
            initialRoute: '/home',
            onGenerateRoute: (settings) {
              Widget page;
              switch (settings.name) {
                case '/home':
                  page = const HomePage();
                  break;
                case '/profile':
                  page = const ProfilePage();
                  break;
                // 按需添加其他路由
                default:
                  page = const HomePage();
              }
              // 路由返回的页面不再包含抽屉
              return MaterialPageRoute(
                builder: (context) => Scaffold(
                  appBar: AppBar(title: Text(_getPageTitle(settings.name))),
                  body: page,
                ),
              );
            },
          ),
        ),
      ],
    );
  }

  String _getPageTitle(String? routeName) {
    switch (routeName) {
      case '/home':
        return '首页';
      case '/profile':
        return '个人中心';
      default:
        return '默认页面';
    }
  }
}

2. 抽屉内的页面切换逻辑

在CustomDrawer的列表项点击事件中,直接调用Navigator切换右侧页面即可,抽屉本身不会触发重建:

// CustomDrawer中的列表项示例
ListTile(
  title: const Text('首页'),
  selected: ModalRoute.of(context)?.settings.name == '/home',
  onTap: () {
    Navigator.of(context).pushReplacementNamed('/home');
  },
)

额外适配:大屏/小屏兼容

如果需要同时支持小屏设备的抽屉滑出模式,可以通过屏幕宽度判断动态切换布局:

class AppRoot extends StatelessWidget {
  const AppRoot({super.key});

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    // 定义大屏阈值,可根据需求调整
    final isLargeScreen = screenWidth >= 1000;

    if (isLargeScreen) {
      // 大屏布局:固定抽屉+右侧Navigator
      return Row(
        children: [
          const CustomDrawer(),
          Expanded(child: _buildNavigator()),
        ],
      );
    } else {
      // 小屏布局:常规抽屉式Scaffold
      return Scaffold(
        drawer: const CustomDrawer(),
        body: _buildNavigator(),
      );
    }
  }

  Widget _buildNavigator() {
    return Navigator(
      initialRoute: '/home',
      onGenerateRoute: (settings) {
        Widget page;
        switch (settings.name) {
          case '/home':
            page = const HomePage();
            break;
          case '/profile':
            page = const ProfilePage();
            break;
          default:
            page = const HomePage();
        }
        return MaterialPageRoute(
          builder: (context) => Scaffold(
            appBar: AppBar(title: Text(_getPageTitle(settings.name))),
            body: page,
          ),
        );
      },
    );
  }

  String _getPageTitle(String? routeName) {
    switch (routeName) {
      case '/home':
        return '首页';
      case '/profile':
        return '个人中心';
      default:
        return '默认页面';
    }
  }
}

效果说明

  • 抽屉仅初始化一次,滚动状态、选中状态等都会全程保留;
  • 页面切换时只有右侧区域有动效,抽屉完全固定在左侧,无任何跟随动效;
  • 路由管理逻辑清晰,所有页面内容都由右侧的Navigator统一处理。

内容的提问来源于stack exchange,提问作者Anakhand

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 21:26:09