欢迎阅读前面的帖子 【开源鸿蒙跨平台开发先锋训练营】使用Flutter请求网络接口并渲染页面 【开源鸿蒙跨平台开发先锋训练营】使用Flutter框架搭建鸿蒙App项目及代码管理
在此前的学习中,处理了单页面的网络数据请求和渲染,以及上拉加载更多、下拉刷新。仅此还不够,大部分的应用其他都是多模块,多页面形态,常见的是通过tab实现底部多个模块的切换。接下来学习如何使用Flutter的Tab组件实现多页面以及item切换使用不同动效,让应用更丰富些。
文章目录
-
-
- Tab
-
- 效果
- 概述
- 文件与要点
- 样式与图标
- 切换动效
-
- 结束语
Tab
给项目添加了底部 Tab 容器(4 个 tab),并实现了每个 tab 的基础页面。原 HomePage 作为第一个tab的主页,新建其余3个页面作为其他tab的主页,在功能上可以理解为4个模块,每个模版对应一个主页。
效果

概述
从图片中运行的模拟器可以看到底部多了4个按钮,分别是首页/发现/消息/我的
- 首页:一般应用启动用户最先看到的页面,也是最关键的,决定你应用能不能吸引他继续往下使用。
- 发现:计划设计新闻咨询等内容
- 消息:就是常规的给用户发送的消息内容:可以是用户消息、也可以是系统消息
- 我的:用户设置和个人中心管理等



文件与要点
-
lib/main.dart
- 将 home: 从 HomePage() 改为 RootPage()。
-
lib/pages/root_page.dart(新增/核心实现)
- 新文件,提供 RootPage 组件,包含:
- BottomNavigationBar(4 项),管理当前索引;
- 使用 AnimatedSwitcher 承载不同 tab 的页面切换,基于 _buildTransition 提供 per-tab 动效;
- 选中/未选中颜色与标签样式统一设置(selectedItemColor、unselectedItemColor、selectedLabelStyle、unselectedLabelStyle);
- 关键函数/变量:
- _currentIndex:当前 tab 索引;
- _isForward:用于判断切换方向以控制某些动画方向;
- _buildTransition(child, animation):返回不同的 Widget(FadeTransition/ScaleTransition/SlideTransition/RotationTransition)。
- 新文件,提供 RootPage 组件,包含:
-
lib/pages/home_page.dart(修改)
- 移除 Scaffold,改为返回 Container 包裹的页面内容,以便 RootPage 的 Scaffold 能为整个 app 提供统一框架;
- 保留原有的数据加载、分页逻辑与 UI。注意:HomePage 内使用 MediaQuery.of(context).padding 来放置一些覆盖元素(页码、切换按钮),因此无需额外 SafeArea。
-
lib/pages/search_page.dart, lib/pages/messages_page.dart, lib/pages/profile_page.dart
- 为主体内容添加 SafeArea,以防状态栏/刘海遮挡;
- messages_page.dart 修复了之前由于括号不匹配导致的语法错误;
- profile_page.dart 修复了缩进与括号问题。
-
lib/pages/upload_page.dart
- 仍保留为一个简单示例实现(发布表单)。当前已从导航中移除该 tab;如不需要可删除此文件。
样式与图标
关于tab的设置,能看到的图标和文字,可以根据不同状态来设置样式,如当前选中的tab,使用高亮进行处理,提升用户视觉体验。 这里使用下面的配置情况:
- 选中颜色: 使用主题主色 Theme.of(context).colorScheme.primary,在 lib/pages/root_page.dart 中通过 selectedItemColor 设置。
- 未选中颜色: 使用 Colors.grey,通过 unselectedItemColor 设置。
- 图标处理: 未选中使用 Outlined 风格图标(如 Icons.home_outlined),选中使用实心图标(如 Icons.home),在 BottomNavigationBarItem 中分别使用 icon 和 activeIcon 设置。
- 文本样式: 选中标签使用 TextStyle(fontWeight: FontWeight.w600, fontSize: 12),未选中标签使用 TextStyle(fontWeight: FontWeight.w400, fontSize: 11),分别对应 selectedLabelStyle 与 unselectedLabelStyle。
图标也可以使用自定义的图片,不一定要用系统的Icons类。
- 部分代码
BottomNavigationBar(
selectedItemColor: Theme.of(context).colorScheme.primary,
unselectedItemColor: Colors.grey,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: '首页'),
// …
],
)
切换动效
每个tab点击切换添加不同的转场效果。App的页面切换动效各系统(iOS、安卓)基本大同小异,不同的动效有不同合适的场景和动画体现。
-
每个 Tab 的不同切换动效
-
我为不同的 tab 指定了不同的入场动效,目的是突出页面性质并提升切换感受:
- 首页:淡入(轻微透明过渡)。
- 发现:缩放(从 0.92 放大到 1.0)。
- 消息:组合动画(水平位移 + 轻微旋转)。
- 我的:从底部滑入。
其他更多效果可以查看CurvedAnimation代码,里面有很多其他动效,包括3D动画。
- 实现容器:AnimatedSwitcher,时长 300ms,曲线 Curves.easeInOut;使用 KeyedSubtree(ValueKey<int>(_currentIndex)) 来区分子页面实例,保证切换能触发动画;
- 入场动效(实现于 _buildTransition):
- 首页(index 0):淡入 — FadeTransition;
- 发现(index 1):缩放 — ScaleTransition(0.92 -> 1.0);
- 消息(index 2):位移 + 轻微旋转 — SlideTransition + RotationTransition(位移方向受 _isForward 控制);
- 我的(index 3):从底部滑入 — SlideTransition(begin: Offset(0,1));
部分代码:
Widget _buildTransition(Widget child, Animation<double> animation) {
final curved = CurvedAnimation(parent: animation, curve: Curves.easeInOut);
switch (_currentIndex) {
case 0:
return FadeTransition(opacity: curved, child: child);
case 1:
return ScaleTransition(scale: Tween(begin: 0.92, end: 1.0).animate(curved), child: child);
case 2:
final offsetBegin = _isForward ? Offset(1,0) : Offset(–1,0);
return SlideTransition(
position: Tween(begin: offsetBegin, end: Offset.zero).animate(curved),
child: RotationTransition(turns: Tween(begin: 0.08, end: 0.0).animate(curved), child: child),
);
case 3:
return SlideTransition(position: Tween(begin: Offset(0,1), end: Offset.zero).animate(curved), child: child);
default:
return FadeTransition(opacity: curved, child: child);
}
}
结束语
感谢阅读本帖,如对贴中内容有意见和建议的,欢迎与我联系交流,也欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
网硕互联帮助中心





评论前必须登录!
注册