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

如何实现持久化的Material 3 NavigationBar组件

原生Material3 NavigationBar持久化实现示例

以下是满足你需求的简化实现方案,用原生Material3组件实现持久化底部导航栏,支持页面跳转时底部栏不消失、切换标签保留页面状态:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Persistent NavigationBar Demo',
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.blue,
      ),
      home: const MainPage(),
    );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  int _selectedIndex = 0;
  // 为每个底部标签分配独立的Navigator全局键
  final List<GlobalKey<NavigatorState>> _navigatorKeys = [
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 用Stack+Offstage管理多个Navigator的显示状态
      body: Stack(
        children: List.generate(3, (index) {
          return Offstage(
            offstage: index != _selectedIndex,
            child: Navigator(
              key: _navigatorKeys[index],
              onGenerateRoute: (settings) {
                return MaterialPageRoute(
                  builder: (context) => _getPage(index),
                );
              },
            ),
          );
        }),
      ),
      bottomNavigationBar: NavigationBar(
        selectedIndex: _selectedIndex,
        onDestinationSelected: _onItemTapped,
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.home),
            label: '首页',
          ),
          NavigationDestination(
            icon: Icon(Icons.search),
            label: '搜索',
          ),
          NavigationDestination(
            icon: Icon(Icons.person),
            label: '我的',
          ),
        ],
      ),
    );
  }

  // 返回对应标签的初始页面
  Widget _getPage(int index) {
    switch (index) {
      case 0:
        return const HomePage();
      case 1:
        return const SearchPage();
      case 2:
        return const ProfilePage();
      default:
        return const HomePage();
    }
  }
}

// 首页示例(包含跳转按钮)
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('首页')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 跳转使用当前标签页的Navigator上下文,不会影响底部栏
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => const DetailPage()),
            );
          },
          child: const Text('跳转到详情页'),
        ),
      ),
    );
  }
}

// 详情页(带返回按钮)
class DetailPage extends StatelessWidget {
  const DetailPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('详情页')),
      body: const Center(child: Text('这是详情页面,底部导航栏依然存在')),
    );
  }
}

// 搜索页示例
class SearchPage extends StatelessWidget {
  const SearchPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('搜索页')),
      body: const Center(child: Text('搜索页面')),
    );
  }
}

// 个人中心页示例
class ProfilePage extends StatelessWidget {
  const ProfilePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('我的页面')),
      body: const Center(child: Text('个人中心页面')),
    );
  }
}

核心实现逻辑

  • 独立Navigator栈:每个底部标签对应一个带全局键的Navigator,页面跳转仅在当前标签的子栈内进行,根页面的Scaffold(包含底部导航栏)始终保持渲染。
  • Offstage状态管理:用Stack和Offstage控制不同标签页的显示/隐藏,未选中的标签页只是隐藏而非销毁,因此页面状态会被保留。
  • 上下文隔离:页面内的Navigator.push使用当前标签页的上下文,不会触发根Navigator的页面替换,避免底部导航栏消失。

为什么之前的代码会失效

你之前的跳转代码使用了根Navigator的上下文,跳转后会替换整个根页面内容,导致底部导航栏被移除。而本方案通过子Navigator实现了页面栈的隔离,完美适配Material3的持久化导航栏要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 14:43:23