Flutter如何实现 登录页 和 注册页 不显示底部菜单,其他页面显示底部菜单

要实现登录 / 注册页隐藏底部菜单,其他页面显示 ,核心是将底部菜单封装在 "主容器页面" 中,登录 / 注册页作为独立页面通过路由跳转(不嵌套在主容器中)。以下是更通用、易扩展的实现方案,适配多页面场景(首页、产品页、用户信息页等)。

方案整体思路

  1. 分层结构

    • 「基础层」:MaterialApp 管理全局路由。
    • 「主容器层」:MainScaffold 封装底部菜单,仅对非登录 / 注册页显示。
    • 「业务页层」:登录 / 注册页为独立页面(无底部菜单),首页 / 产品页 / 用户信息页嵌套在主容器中(显示底部菜单)。
  2. 路由控制:通过路由名称判断是否显示底部菜单,无需为每个页面单独配置。

完整实现代码

1. 路由常量(统一管理)

dart 复制代码
// lib/routes/app_routes.dart
class AppRoutes {
  // 无需底部菜单的页面
  static const String login = '/login';
  static const String register = '/register';
  
  // 需要底部菜单的页面
  static const String home = '/'; // 首页(默认页)
  static const String product = '/product'; // 产品页
  static const String userInfo = '/userInfo'; // 用户信息页
}

2. 封装带底部菜单的主容器(核心)

dart 复制代码
// lib/widgets/main_scaffold.dart
import 'package:flutter/material.dart';
import '../routes/app_routes.dart';

class MainScaffold extends StatelessWidget {
  final Widget body; // 页面主体内容
  final int currentTabIndex; // 底部菜单选中项
  final Function(int) onTabChanged; // 菜单切换回调

  const MainScaffold({
    super.key,
    required this.body,
    required this.currentTabIndex,
    required this.onTabChanged,
  });

  // 判断当前页面是否需要显示底部菜单
  bool _shouldShowBottomNav(BuildContext context) {
    final routeName = ModalRoute.of(context)?.settings.name ?? '';
    // 排除登录/注册页,其余页面均显示底部菜单
    return ![AppRoutes.login, AppRoutes.register].contains(routeName);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: body,
      // 仅需显示底部菜单时,渲染BottomNavigationBar
      bottomNavigationBar: _shouldShowBottomNav(context)
          ? BottomNavigationBar(
              currentIndex: currentTabIndex,
              onTap: onTabChanged,
              type: BottomNavigationBarType.fixed, // 适配多选项
              items: const [
                BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
                BottomNavigationBarItem(icon: Icon(Icons.shop), label: '产品'),
                BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
              ],
            )
          : null, // 隐藏时设为null(关键:避免留白)
    );
  }
}

3. 登录 / 注册页(独立页面,无底部菜单)

登录页

dart 复制代码
// lib/pages/login_page.dart
import 'package:flutter/material.dart';
import '../routes/app_routes.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('登录')),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(decoration: const InputDecoration(hintText: '手机号/账号')),
            const SizedBox(height: 16),
            TextField(
              obscureText: true,
              decoration: const InputDecoration(hintText: '密码'),
            ),
            const SizedBox(height: 24),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: () {
                  // 登录成功:跳转到首页(替换路由栈,避免返回登录页)
                  Navigator.pushReplacementNamed(context, AppRoutes.home);
                },
                child: const Text('登录'),
              ),
            ),
            TextButton(
              onPressed: () {
                // 跳转到注册页
                Navigator.pushNamed(context, AppRoutes.register);
              },
              child: const Text('没有账号?去注册'),
            ),
          ],
        ),
      ),
    );
  }
}

注册页

less 复制代码
// lib/pages/register_page.dart
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('注册')),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(decoration: const InputDecoration(hintText: '手机号')),
            const SizedBox(height: 16),
            TextField(decoration: const InputDecoration(hintText: '验证码')),
            const SizedBox(height: 16),
            TextField(
              obscureText: true,
              decoration: const InputDecoration(hintText: '设置密码'),
            ),
            const SizedBox(height: 24),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: () {
                  // 注册成功:返回登录页
                  Navigator.pop(context);
                },
                child: const Text('完成注册'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

4. 带底部菜单的业务页面(首页 / 产品页 / 用户信息页)

首页

dart 复制代码
// lib/pages/home_page.dart(底部菜单容器)
import 'package:flutter/material.dart';
import '../widgets/main_scaffold.dart';
import '../routes/app_routes.dart';
import 'product_page.dart';
import 'user_info_page.dart';

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _currentTabIndex = 0; // 当前选中的底部菜单索引

  // 底部菜单对应的页面(可直接跳转其他业务页)
  final List<Widget> _tabPages = const [
    HomeContent(), // 首页内容
    ProductPage(), // 产品页(独立页面,但嵌套在主容器中)
    UserInfoPage(), // 用户信息页
  ];

  // 切换底部菜单
  void _onTabChanged(int index) {
    setState(() {
      _currentTabIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MainScaffold(
      body: _tabPages[_currentTabIndex],
      currentTabIndex: _currentTabIndex,
      onTabChanged: _onTabChanged,
    );
  }
}

// 首页内容
class HomeContent extends StatelessWidget {
  const HomeContent({super.key});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Text('首页(显示底部菜单)'),
          ElevatedButton(
            onPressed: () {
              // 跳转到产品页(仍显示底部菜单)
              Navigator.pushNamed(context, AppRoutes.product);
            },
            child: const Text('去产品页'),
          ),
        ],
      ),
    );
  }
}

产品页

dart 复制代码
// lib/pages/product_page.dart(产品页)
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('产品页')),
      body: const Center(child: Text('产品页(显示底部菜单)')),
    );
  }
}

用户信息页

dart 复制代码
// lib/pages/user_info_page.dart(用户信息页)
import 'package:flutter/material.dart';
import '../routes/app_routes.dart';

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

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Text('用户信息页(显示底部菜单)'),
          ElevatedButton(
            onPressed: () {
              // 跳转到登录页(隐藏底部菜单)
              Navigator.pushNamed(context, AppRoutes.login);
            },
            child: const Text('退出登录/去登录'),
          ),
        ],
      ),
    );
  }
}

5. 全局路由配置(入口文件)

dart 复制代码
// lib/main.dart
import 'package:flutter/material.dart';
import 'routes/app_routes.dart';
import 'pages/login_page.dart';
import 'pages/register_page.dart';
import 'pages/home_page.dart';
import 'pages/product_page.dart';
import 'pages/user_info_page.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '底部菜单控制示例',
      theme: ThemeData(primarySwatch: Colors.blue),
      // 全局路由表
      routes: {
        AppRoutes.home: (context) => const HomePage(),
        AppRoutes.login: (context) => const LoginPage(),
        AppRoutes.register: (context) => const RegisterPage(),
        AppRoutes.product: (context) => const ProductPage(),
        AppRoutes.userInfo: (context) => const UserInfoPage(),
      },
      initialRoute: AppRoutes.home, // 初始页面为首页
      // 可选:路由跳转动画(保持统一体验)
      pageTransitionsTheme: const PageTransitionsTheme(
        builders: {
          TargetPlatform.android: CupertinoPageTransitionsBuilder(),
          TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
        },
      ),
    );
  }
}

关键细节说明

  1. 底部菜单显示逻辑

    • MainScaffold 中通过 _shouldShowBottomNav 方法,根据当前路由名称判断是否显示底部菜单(仅排除登录 / 注册页)。
    • 底部菜单设为 null 而非 SizedBox.shrink(),避免底部留白。
  2. 页面跳转规则

    • 登录 / 注册页通过 Navigator.pushNamed 跳转,属于独立页面(无主容器包裹,因此无底部菜单)。
    • 首页 / 产品页 / 用户信息页嵌套在 MainScaffold 中,无论是否通过路由跳转(如首页跳产品页),都会显示底部菜单。
    • 登录成功后用 pushReplacementNamed 替换路由栈,避免返回登录页。
  3. 扩展性

    • 新增需要显示底部菜单的页面:只需在路由表中注册,无需修改其他代码(自动继承主容器的底部菜单)。
    • 新增需要隐藏底部菜单的页面:只需在 _shouldShowBottomNav 方法中添加对应的路由名称(如 AppRoutes.forgetPwd)。

优化建议

  1. 登录状态拦截 :结合 onGenerateRoute 实现路由守卫,未登录时访问用户信息页自动跳转到登录页:

    dart

    csharp 复制代码
    // 在MyApp的MaterialApp中添加
    onGenerateRoute: (settings) {
      // 模拟登录状态(实际可通过Provider/Bloc管理)
      bool isLogin = false; 
      if (settings.name == AppRoutes.userInfo && !isLogin) {
        return MaterialPageRoute(builder: (_) => const LoginPage());
      }
      return MaterialPageRoute(builder: (_) => const HomePage());
    },
  2. 底部菜单状态保持 :若需要切换底部菜单时保留页面状态(如产品页滚动位置),可使用 PageView + AutomaticKeepAliveClientMixin 替代 List<Widget>

    dart 复制代码
    // 替换HomePage中的_tabPages为PageView
    PageView(
      controller: PageController(initialPage: _currentTabIndex),
      onPageChanged: _onTabChanged,
      physics: const NeverScrollableScrollPhysics(), // 禁止左右滑动
      children: _tabPages,
    );
  3. 适配不同屏幕 :底部菜单可添加 backgroundColorselectedItemColor 等样式,适配深色模式:

    dart 复制代码
    BottomNavigationBar(
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      selectedItemColor: Theme.of(context).primaryColor,
      unselectedItemColor: Colors.grey,
      // 其他配置...
    );

此方案兼顾了简洁性扩展性,适配多页面场景,且逻辑清晰,易于维护。

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax