Flutter for OpenHarmony 实战之基础组件:第十一篇 BottomNavigationBar 与 TabBar 多页切换

Flutter for OpenHarmony 实战之基础组件:第十一篇 BottomNavigationBar 与 TabBar 多页切换
在这里插入图片描述

Flutter for OpenHarmony 实战之基础组件:第十一篇 BottomNavigationBar 与 TabBar 多页切换

摘要:一个复杂的 App 通常包含多个功能模块。本文将深入讲解 Flutter 中最核心的两种多页切换模式:底部导航 (BottomNavigationBar) 和顶部选项卡 (TabBar)。我们将探讨 Material 3 风格的新组件 NavigationBar,解决页面切换时的状态丢失问题,并适配鸿蒙系统的底部手势条。

前言

打开你手机里的微信、淘宝或抖音,你会发现它们都有一个共同的架构:底部有 4-5 个图标,点击切换不同的主页面;顶部可能还有“关注/推荐/热榜”这样的分类切换。

这就是移动端最经典的 “底 Tab + 顶 Tab” 双导航架构。

本文你将学到

  • BottomNavigationBar (经典) 与 NavigationBar (Material 3) 的区别
  • TabBar + TabBarView 实现滑动切换
  • 核心难点:如何让页面在切换后不重置?(AutomaticKeepAliveClientMixin)
  • 鸿蒙适配:底部导航栏如何避开系统手势条 (Home Indicator)

一、底部导航:App 的根基

Flutter 提供了两种主流的底部导航组件。

在这里插入图片描述

1.1 经典款:BottomNavigationBar

这是最传统、兼容性最好的组件。

classMainPageextendsStatefulWidget{constMainPage({super.key});@overrideState<MainPage>createState()=>_MainPageState();}class _MainPageState extendsState<MainPage>{ int _currentIndex =0;// 页面列表finalList<Widget> _pages =[constHomePage(),constCategoryPage(),constProfilePage(),];@overrideWidgetbuild(BuildContext context){returnScaffold( body: _pages[_currentIndex],// 根据下标显示对应页面 bottomNavigationBar:BottomNavigationBar( currentIndex: _currentIndex, onTap:(index)=>setState(()=> _currentIndex = index), type:BottomNavigationBarType.fixed,// 超过3个item必须设置fixed selectedItemColor:Colors.blue, unselectedItemColor:Colors.grey, items:const[BottomNavigationBarItem(icon:Icon(Icons.home), label:'首页'),BottomNavigationBarItem(icon:Icon(Icons.category), label:'发现'),BottomNavigationBarItem(icon:Icon(Icons.person), label:'我的'),],),);}}

1.2 新潮款:NavigationBar (M3)

Material 3 引入了更高、更圆润的 NavigationBar。它自带点击涟漪和胶囊状的指示器,视觉效果更好。

NavigationBar( selectedIndex: _currentIndex, onDestinationSelected:(index)=>setState(()=> _currentIndex = index), destinations:const[NavigationDestination( icon:Icon(Icons.home_outlined), selectedIcon:Icon(Icons.home), label:'首页',),NavigationDestination( icon:Icon(Icons.explore_outlined), selectedIcon:Icon(Icons.explore), label:'发现',),// ...],)

二、顶部选项卡:TabBar

TabBar 通常用于在同一个主栏目下,切换不同的子分类(如新闻 App 的频道)。它必须配合 TabController 使用。

在这里插入图片描述

2.1 DefaultTabController (推荐)

最简单的方法是在父级包裹一个 DefaultTabController,这样我们就不用手动管理 Controller 了。

classNewsPageextendsStatelessWidget{constNewsPage({super.key});@overrideWidgetbuild(BuildContext context){returnDefaultTabController( length:3,// Tab 数量 child:Scaffold( appBar:AppBar( title:constText('资讯'), bottom:constTabBar( tabs:[Tab(text:'推荐'),Tab(text:'科技'),Tab(text:'体育'),],),), body:constTabBarView( children:[Center(child:Text('推荐内容列表')),Center(child:Text('科技内容列表')),Center(child:Text('体育内容列表')),],),),);}}

三、核心难点:页面状态保持

在这里插入图片描述

默认情况下,当你从“首页”切换到“我的”,再切回“首页”时,首页会被重建(列表滚动位置丢失,输入框清空)。这是因为 Scaffold.body 直接替换了 Widget。

3.1 解决方案:IndexedStack

如果你希望所有页面在初始化后一直存在,可以使用 IndexedStack。它会一次性加载所有页面(注意内存消耗)。

// 修改 Scaffold body body:IndexedStack( index: _currentIndex, children: _pages,// 所有页面都会被保留在 Widget 树中,只是不可见),

3.2 解决方案:AutomaticKeepAliveClientMixin

如果你使用的是 PageViewTabBarView,更推荐让子页面自己决定是否保持状态。

classHomePageextendsStatefulWidget{constHomePage({super.key});@overrideState<HomePage>createState()=>_HomePageState();}// 1. 混入 AutomaticKeepAliveClientMixinclass _HomePageState extendsState<HomePage>withAutomaticKeepAliveClientMixin{// 2. 重写 wantKeepAlive 返回 true@override bool get wantKeepAlive =>true;@overrideWidgetbuild(BuildContext context){super.build(context);// 3. 必须调用 super.buildreturnListView.builder( itemCount:100, itemBuilder:(c, i)=>ListTile(title:Text('Item $i')),);}}

四、OpenHarmony 鸿蒙适配专题

在这里插入图片描述

4.1 底部导航栏与手势条

现在的鸿蒙手机(如 Mate 60)默认开启全面屏手势,底部有一条“黑条”或“白条” (Home Indicator)。

如果你的 BottomNavigationBar 高度写死,或者没有适配 SafeArea,底部的图标可能会被这个手势条遮挡。

Flutter 的 Scaffold + BottomNavigationBar 默认已经处理了 SafeArea。但如果你使用了自定义的底部栏(比如 Stack 里的 Positioned),务必包裹 SafeArea 并设置 bottom: true

Align( alignment:Alignment.bottomCenter, child:SafeArea( child:Container( height:60, color:Colors.white, child:Row(...),),),)

五、总结

搭建一个 App 的骨架,核心就是“一底一顶”。

核心要点

  1. 底部导航:推荐使用 M3 风格的 NavigationBar,视觉更现代。
  2. 顶部 Tab:使用 DefaultTabController 配合 TabBarView 最省事。
  3. 状态保持:不想每次切换都重加载?请记住 IndexedStack (简单粗暴) 或 AutomaticKeepAliveClientMixin (精细控制)。
  4. 鸿蒙适配:时刻留意底部的安全区域,不要让按钮贴底太近。

下一篇预告

基本的页面结构都有了,但是我们现在的布局还是太“循规蹈矩”了(一行一列)。如果我要做一个 Pinterest 那样的瀑布流,或者像相册一样的网格呢?
《Flutter for OpenHarmony 实战之基础组件:第十二篇 GridView 网格布局详解》
我们将突破线性布局的限制,探索二维空间的布局艺术。


🌐 欢迎加入开源鸿蒙跨平台社区开源鸿蒙跨平台开发者社区

Read more

如何解决前端Axios请求报Net::ERR_CONNECTION_REFUSED连接拒绝问题

如何解决前端Axios请求报Net::ERR_CONNECTION_REFUSED连接拒绝问题

Net::ERR_CONNECTION_REFUSED是前端使用Axios发起HTTP请求时,最常见的网络层错误之一,该错误的出现与Axios语法、接口请求参数无关,也并非前端代码逻辑问题,核心是前端客户端无法与目标服务端建立基础的TCP连接,服务端对客户端发起的连接请求做出了拒绝响应。这类问题的排查需跳出前端代码本身,从「服务端运行状态」「前端请求配置」「网络链路通畅性」「端口/防火墙限制」四个核心维度逐步验证,本地开发环境还需额外检查代理转发配置,以下是从易到难的完整排查流程和针对性解决方案,覆盖本地、局域网、线上生产所有开发场景。 文章目录 * 一、核心认知:错误本质与核心诱因 * 1.1 错误的核心本质 * 1.2 触发错误的四大核心诱因 * 1.3 关键区分:避免与其他错误混淆 * 二、从易到难:分步排查与针对性解决方案 * 步骤1:验证目标服务端是否正常运行,有无进程监听指定端口 * 具体验证方法 * 针对性解决方案 * 步骤2:检查前端Axios请求配置,确保地址/端口/协议完全正确

【前端】Vue3+elementui+ts,给标签设置样式属性style时,提示type check failed for prop,再次请出DeepSeek来解答

【前端】Vue3+elementui+ts,给标签设置样式属性style时,提示type check failed for prop,再次请出DeepSeek来解答

🌹欢迎来到《小5讲堂》🌹 🌹这是《前端》系列文章,每篇文章将以博主理解的角度展开讲解。🌹 🌹温馨提示:博主能力有限,理解水平有限,若有不对之处望指正!🌹 目录 * 前言 * 警告信息 * DeepSeek解答 * 问题原因 * 解决方案 * 关于 !important * 最终建议写法 * Vue小技巧 * Vue 3 实用代码小技巧 * 1. 组合式 API 技巧 * 2. 组件通信技巧 * 3. 模板技巧 * 4. 性能优化技巧 * 5. 组合式函数技巧 * 6. 生命周期技巧 * 7. 路由技巧 (Vue Router) * 8. 状态管理 (Pinia) 技巧 * 9. 调试技巧 * 文章推荐 前言 翻看了下上一篇写前端文章还是一年前,

Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:教室信息管理系统(前后端源码 + 数据库 sql 脚本)

Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:教室信息管理系统(前后端源码 + 数据库 sql 脚本)

🔥博客主页: 【小扳_-ZEEKLOG博客】 ❤感谢大家点赞👍收藏⭐评论✍         1.0 项目介绍         开发工具:IDEA、VScode         服务器:Tomcat, JDK 17         项目构建:maven         数据库:mysql 8.0 系统用户前台和管理后台两部分,项目采用前后端分离         前端技术:vue3 + elementUI         服务端技术:springboot + mybatis + redis + mysql         1.1 项目功能 后台功能:         1)登录、退出系统、首页         2)教室管理                 (1) 教室管理:添加、修改、删除、查询等功能。         3)教师管理

【测试理论与实践】(十)Web 项目自动化测试实战:从 0 到 1 搭建博客系统 UI 自动化框架

【测试理论与实践】(十)Web 项目自动化测试实战:从 0 到 1 搭建博客系统 UI 自动化框架

目录 前言 一、项目背景与测试规划:先明确 "测什么" 和 "怎么测" 1.1 项目介绍 1.2 测试目标 1.3 测试范围与用例设计 编辑 二、环境搭建:3 步搞定自动化测试前置准备 2.1 安装核心依赖包 2.2 浏览器配置 2.3 项目目录结构设计 三、核心模块开发:封装公共工具,提高代码复用性 3.1 驱动管理与截图工具封装(common/Utils.py) 3.2 代码说明与优化点 四、测试用例开发: