【开源鸿蒙跨平台开发先锋训练营】Flutter底部选项卡实现学习
🖼️ 镜像 - 展示现有的 Pipeline 列表(下拉刷新、上拉加载)- 将原有的 Pipeline 列表逻辑提取到。最为开发新手,听人劝,吃饱饭,果断选择了推荐的。Material Design 3 新组件。Material Design 2 风格。甚至将图标都帮我生成好了。(推荐 - Material 3)📋 任务 - 任务页面(待开发)📊 统计 - 统计页面(待开发)👤 我的 -
背景
训练营活动中有一项要求为实现底部选项卡,这其实也是很普遍的一个功能,现在的GUI应用或者Web应用,选项卡或者说标签页的实现是一个很普通的功能,可能很好的帮助组织界面的功能,避免界面的混乱或者用户体验的不便。
实现方案的比较和推荐
在Flutter中实现底部选项(底部导航)的主要技术方案有:
1. BottomNavigationBar(官方标准方案)
Material Design 2 风格
最简单易用
支持3-5个标签项
代码量最少
2. NavigationBar(推荐 - Material 3)
Material Design 3 新组件
现代设计风格
更好的动画效果
无项数限制
3. CupertinoTabScaffold(iOS风格)
iOS原生外观
使用
CupertinoTabBar适合跨平台应用
4. 自定义BottomAppBar + FloatingActionButton
灵活度最高
可创建特殊设计效果
需要更多代码
5. 第三方包
根据你的pubspec.yaml,建议:
persistent_bottom_nav_bar- 功能丰富,动画效果好
bottom_navy_bar- 简洁美观
salomon_bottom_bar- 交互效果强
curved_navigation_bar- 设计独特建议选择:
快速开发:使用
NavigationBar或BottomNavigationBariOS用户多:
CupertinoTabScaffold需要特殊效果:
persistent_bottom_nav_bar或自定义方案
作为开发新手,听人劝,吃饱饭,果断选择了推荐的NavigationBar方案。
变更要点
关键变更:
底部导航栏 - 使用
NavigationBar组件,4个标签页:
🖼️ 镜像 - 展示现有的 Pipeline 列表(下拉刷新、上拉加载)
📋 任务 - 任务页面(待开发)
📊 统计 - 统计页面(待开发)
👤 我的 - 个人页面(待开发)
页面分离 - 将原有的 Pipeline 列表逻辑提取到
MirrorPage类状态管理 - 底部导航切换由
_selectedIndex控制
AI很容易的就将代码给准备好了,基本上没有出现什么问题。
运行验证
鸿蒙模拟器截屏:

感觉还不错吧,图标都是默认自带的。。。
使用定制化图标
因为个人不善于制作图标,也就不准备改为使用定制化的图标了。
不过还是想了解到的方案放在这里以供后面参考。
步骤概览(3 步)
- 把自定义图标文件放到工程的
assets/目录。- 在 pubspec.yaml 注册这些 assets(并可选添加
flutter_svg依赖以支持 SVG)。- 在
NavigationDestination中用icon/selectedIcon替换原来的Icon(...)。示例改动(PNG 位图)
- 在 pubspec.yaml 添加:
flutter: assets: - assets/icons/image.png - assets/icons/assignment.png - assets/icons/bar_chart.png - assets/icons/person.png
- 在 main.dart 中使用
ImageIcon:NavigationDestination( icon: ImageIcon(AssetImage('assets/icons/image.png')), selectedIcon: ImageIcon(AssetImage('assets/icons/image.png')), label: '镜像', ),如果你想选中/未选中显示不同图片(例如带色差),可以提供不同文件:
NavigationDestination( icon: ImageIcon(AssetImage('assets/icons/image_gray.png')), selectedIcon: ImageIcon(AssetImage('assets/icons/image_color.png')), label: '镜像', ),示例改动(SVG,需依赖
flutter_svg)
- 在 pubspec.yaml 的
dependencies:添加:flutter_svg: ^2.0.0
- 注册 svg assets 同上,然后在代码中:
import 'package:flutter_svg/flutter_svg.dart'; NavigationDestination( icon: SvgPicture.asset('assets/icons/image.svg', width:24, height:24, color: Colors.grey), selectedIcon: SvgPicture.asset('assets/icons/image.svg', width:24, height:24, color: Theme.of(context).colorScheme.primary), label: '镜像', ),其它注意点
- 如果使用
ImageIcon并希望改变颜色,确保图标为单色(或使用 PNG 的 alpha 通道),否则颜色覆盖可能不生效。selectedIcon字段用于显示选中状态图标(Material 3 NavigationDestination 支持)。若你的 Flutter 版本不支持,可在NavigationBar的selectedIndex处根据索引条件返回不同图标 widget。- 添加/修改 assets 后运行:
flutter pub get flutter run
- 若图标未显示,请检查 asset 路径拼写和 pubspec.yaml 缩进。
代码主要变更
build方法
整个页面布局变成有appBar, body及bottomNavigationBar三部分组成,body部分负责根据当前选中的标签选项卡显示对应的内容,bottomNavigationBar负责选项卡的显示及事件响应。详细如下:
class _MyHomePageState extends State<MyHomePage> {
+ int _selectedIndex = 0;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ backgroundColor: Theme.of(context).colorScheme.inversePrimary,
+ title: Text(widget.title),
+ ),
+ body: <Widget>[
+ const MirrorPage(),
+ const TaskPage(),
+ const StatisticsPage(),
+ const ProfilePage(),
+ ][_selectedIndex],
+ bottomNavigationBar: NavigationBar(
+ selectedIndex: _selectedIndex,
+ onDestinationSelected: (index) {
+ setState(() {
+ _selectedIndex = index;
+ });
+ },
+ destinations: const [
+ NavigationDestination(
+ icon: Icon(Icons.image),
+ label: '镜像',
+ ),
+ NavigationDestination(
+ icon: Icon(Icons.assignment),
+ label: '任务',
+ ),
+ NavigationDestination(
+ icon: Icon(Icons.bar_chart),
+ label: '统计',
+ ),
+ NavigationDestination(
+ icon: Icon(Icons.person),
+ label: '我的',
+ ),
+ ],
+ ),
+ );
+ }
+}
+
标签页面定义类
MirrorPage
// 镜像页面 - 显示Pipeline列表
class MirrorPage extends StatefulWidget {
const MirrorPage({super.key});
@override
State<MirrorPage> createState() => _MirrorPageState();
}
class _MirrorPageState extends State<MirrorPage> {
//保留原来业务逻辑
....
}
TaskPage/StatisticsPage/ProfilePage
暂时没有具体能实现,仅仅是作为例子,来保证选项卡功能的完整性。
// 任务页面
class TaskPage extends StatelessWidget {
const TaskPage({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.assignment, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('任务页面', style: TextStyle(fontSize: 18)),
SizedBox(height: 8),
Text('功能开发中...', style: TextStyle(color: Colors.grey)),
],
),
);
}
}
// 统计页面
class StatisticsPage extends StatelessWidget {
const StatisticsPage({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.bar_chart, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('统计页面', style: TextStyle(fontSize: 18)),
SizedBox(height: 8),
Text('功能开发中...', style: TextStyle(color: Colors.grey)),
],
),
);
}
}
// 我的页面
class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.person, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('我的页面', style: TextStyle(fontSize: 18)),
SizedBox(height: 8),
Text('功能开发中...', style: TextStyle(color: Colors.grey)),
],
),
);
}
}
总结
1. 因为是比较基本的功能,相对来说默认的支持以及生态上(比如AI)的支持还是比较好的,学习使用起来相对来说还是比较容易的。
2. 虽然选择了NavigationBar方案,说是对选项卡的个数没有限制,不过如果选项卡数太多的话,用户体验也不一定会更好。设想一下如果有10几20几个选项卡的话,在手机端是不是就没有空间去显示业务内容了,或者用户不会发现有那么多的选项卡?
3. 可能存在的其他选项卡方式: 比如顶部,左侧,右侧,在移动端是不是都不如在底部的设计好?在PC端是不是在顶部会好些?平板端呢?没有数据来说明哪一个更好,哪一个差一些,先把疑问留在这里吧。
更多推荐




所有评论(0)