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

开源EtherCAT主站SOEM入门使用

开源EtherCAT主站SOEM入门使用

文章目录 * 目的 * 上手体验 * 基础使用 * 资料和例程 * 总结 目的 去年工作上用到了EtherCAT,EtherCAT同学是分主从的,作为学习测试来说主站最常用的是TwinCAT。不过TwinCAT支持的网卡信号有限,又没法应用到各种平台上。所以实际使用时又找了一些主站方案,适应性最广的就是SOEM库(Simple Open EtherCAT Master Library)。 项目地址:https://github.com/OpenEtherCATsociety/SOEM 文档地址:https://docs.rt-labs.com/soem/ SOEM库目前主要分为1.4.0版本和2.0.0版本,前者仓库中自带文档,后者文档需要去RT-Labs官网看(需要注册账号),本文将使用2.0.0版本: 本文测试时EtherCAT从站使用了LAN9252和AX58100,找那种支持DIO独立工作的板子: 这两个从站芯片都支持不依赖外部的控制器独立工作在DIO模式下。LAN9252支持总共16通道DIO,AX58100支持总共32路DIO。 上手

By Ne0inhk
Java调用UniHttp接口请求失败?一次开源的深入实践-百度SN签名认证场景下参数乱序问题的三种解决策略

Java调用UniHttp接口请求失败?一次开源的深入实践-百度SN签名认证场景下参数乱序问题的三种解决策略

目录 前言 一、场景重现 1、UniHttp模式下SN接口定义 2、第一次正式调用 3、第一次遇到211 APP SN校验失败 二、问题查找及开源寻求解决方案 1、抽丝剥茧找到问题所在 2、与作者在Issues的交流 3、开源项目交流有感 三、朝着正确的方向解决问题 1、加密顺序按实际请求参数求解 2、一种兼容Get请求参数动态调整的方法 3、使用注解来设置重排序规则 4、重写请求的QueryParam重排序方法 5、三种方法的使用场景及对比 四、总结 前言         在当今数字化时代,不管是内部系统之间还是跟外部系统的对接,接口调用已成为软件开发中不可或缺的一部分。Java作为一种广泛使用的编程语言,在接口调用方面有着丰富的应用。在现代软件架构中,分布式系统和微服务架构的广泛应用使得不同模块之间的通信主要依赖于接口调用。在之前的博文中,我们讲解了许多跟第三方接口对接的详细案例,比如如何调用实时航班数据、接入百度地图、高德地图、天地图、实时景区等多源数据,也讲解了一些数据接入方式,

By Ne0inhk

mofos平台迁移方案:从闭源到阿里开源识别模型的转换步骤

mofos平台迁移方案:从闭源到阿里开源识别模型的转换步骤 背景与迁移动因 随着AI模型生态的开放化趋势加速,越来越多企业开始将原本依赖闭源识别系统的应用,逐步迁移到性能更优、可定制性强且社区支持完善的开源模型体系中。mofos平台作为早期基于私有图像识别服务构建的内容理解系统,在面对中文通用场景下的万物识别任务时,逐渐暴露出模型更新滞后、推理成本高、语义理解局限等问题。 在此背景下,阿里云推出的“万物识别-中文-通用领域”开源识别模型成为理想的替代方案。该模型专为中文语境优化,覆盖超过10万类常见物体与抽象概念,具备强大的细粒度分类能力与上下文感知能力,尤其适用于复杂背景下的多标签识别任务。更重要的是,其完全开源的设计允许深度定制和本地部署,极大提升了系统的可控性与扩展性。 本文将系统阐述从原有闭源识别架构向阿里开源“万物识别-中文-通用领域”模型迁移的完整技术路径,涵盖环境配置、代码适配、文件管理及实际部署中的关键注意事项,帮助开发者高效完成平滑过渡。 阿里开源模型核心特性解析 模型定位与技术优势 “万物识别-中文-通用领域”是阿里巴巴达摩院视觉团队发布的一款面向

By Ne0inhk