【开源鸿蒙跨平台开发先锋训练营】Flutter实现底部Tab和切换动效
本文介绍了使用Flutter框架实现多页面Tab切换功能的方法。主要内容包括:1)通过BottomNavigationBar组件创建底部4个Tab(首页/发现/消息/我的);2)使用AnimatedSwitcher实现页面切换动画,为不同Tab定制淡入、缩放、位移旋转和滑动等动效;3)统一管理Tab选中/未选中状态的图标、文字颜色和样式;4)调整页面结构,移除冗余Scaffold组件。该方案实现了
欢迎阅读前面的帖子
【开源鸿蒙跨平台开发先锋训练营】使用Flutter请求网络接口并渲染页面
【开源鸿蒙跨平台开发先锋训练营】使用Flutter框架搭建鸿蒙App项目及代码管理
在此前的学习中,处理了单页面的网络数据请求和渲染,以及上拉加载更多、下拉刷新。仅此还不够,大部分的应用其他都是多模块,多页面形态,常见的是通过tab实现底部多个模块的切换。接下来学习如何使用Flutter的Tab组件实现多页面以及item切换使用不同动效,让应用更丰富些。
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)。
- 新文件,提供
-
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
更多推荐



所有评论(0)