Flutter鸿蒙开发指南(四):主页Tab栏实现
本文介绍了Flutter电商应用底部导航栏的实现过程。首先通过git回退代码到初始状态,然后分三步实现:1)基础Tab栏搭建,处理图片资源路径问题;2)添加导航交互逻辑,解决点击无效问题;3)优化为模块化结构,使用IndexedStack保持页面状态,并适配安全区域。最终实现了包含首页、分类、购物车和个人中心四个模块的底部导航功能,在安卓和鸿蒙系统上均运行正常。代码采用状态管理控制当前选中索引,并
一、需求分析
以常见的电商平台举例,如淘宝、京东、拼多多都有底部导航栏。本章节要实现的内容就是实现四个底部导航栏:首页、分类、购物车、我的。

淘宝

京东

拼多多
二、使用git回退代码
在Flutter鸿蒙开发指南(三)使用dio库实现网络请求这一节中,我们完成了网络请求。但是这个和我们要做的电商App所需接口不符合。所以我们要对代码进行回撤。
俗话说:“世上没有后悔药“。
但是在代码的世界是有后悔药的,我们要使用Git的操作进行回撤退就相当于后悔药了。
使用Git回退会将工作目录和暂存区完全恢复到上一次提交的状态,所有未提交的更改都将永久丢失,所以大家需要根据情况而用。我这里是完全不需要上次的代码了,所以直接全部丢失也无所谓。
git reset --hard HEAD

当执行完上面的Git命令,你会发现代码变成了我们第一次提交“搭建基础路由和组件”时候的状态(变成了我们第一次提交时候的代码)
三、实现主页Tab栏实现
首先将文件夹中的八张图片拷贝,分别是底部导航栏的选中和未选中。复制粘贴到项目中的"assets"文件夹中。大家可以在我的代码仓库就获取这八张图片,顺便给我点个Star。
青商城 - AtomGit | GitCode
https://atomgit.com/Deng666/shangcheng


按住Ctrl+/解开这里的注释

改成如图所示,并且点击右上角的Pub get,如果不会改就复制粘贴以下代码:
flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- lib/assets/

然后开始在lib/Main/index.dart开始编写代码

3.1 第一次编写代码
lib/Main/index.dart,第一次编写的代码如下,图标显示不正常。这个时候可能有三种解决方法:
(1)再次运行项目即可
(2)pubspec.yaml未执行Pub get
(3)图片的路径错误,修改好正确路径即可
import 'package:flutter/material.dart';
/*
* @author:幻影
* @date:2025-1-25
*[Tabs:首页]
*/
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
//定义数据 根据数据进行渲染4个导航 List<Map>:key value <String,String>是泛型
//一般应用程序的导航是固定的
final List<Map<String, String>> _tabList = [
{
"icon": "lib/assets/ic_public_home_normal.png", //未选中
"active_icon": "lib/assets/ic_public_home_active.png",//选中
"text": "首页",
},
{
"icon": "lib/assets/ic_public_pro_normal.png", //未选中
"active_icon": "lib/assets/ic_public_pro_active.png",//选中
"text": "分类",
},
{
"icon": "lib/assets/ic_public_cart_normal.png", //未选中
"active_icon": "lib/assets/ic_public_cart_active.png",//选中
"text": "购物车",
},
{
"icon": "lib/assets/ic_public_my_normal.png", //未选中
"active_icon": "lib/assets/ic_public_my_active.png",//选中
"text": "我的",
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("主页"),
),
);
}
}

3.2 第二次编写代码
此时,底部导航栏的Tab和文字都显示正常。但是当你点击的时候,你会发现无法点击。原因是缺少了关键的当前选中索引管理,我们通过"currentIndex"状态来跟踪选中的标签,并且添加onTap回调来处理切换即可。(请看第三次编写代码)
import 'package:flutter/material.dart';
/*
* @author:幻影
* @date:2025-1-25
*[Tabs:首页]
*/
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
//定义数据 根据数据进行渲染4个导航 List<Map>:key value <String,String>是泛型
//一般应用程序的导航是固定的
final List<Map<String, String>> _tabList = [
{
"icon": "lib/assets/ic_public_home_normal.png", //未选中
"active_icon": "lib/assets/ic_public_home_active.png", //选中
"text": "首页",
},
{
"icon": "lib/assets/ic_public_pro_normal.png", //未选中
"active_icon": "lib/assets/ic_public_pro_active.png", //选中
"text": "分类",
},
{
"icon": "lib/assets/ic_public_cart_normal.png", //未选中
"active_icon": "lib/assets/ic_public_cart_active.png", //选中
"text": "购物车",
},
{
"icon": "lib/assets/ic_public_my_normal.png", //未选中
"active_icon": "lib/assets/ic_public_my_active.png", //选中
"text": "我的",
},
];
//返回底部渲染的四个分类
List<BottomNavigationBarItem> _getTabBarWidget() {
return List.generate(_tabList.length, (int index) {
return BottomNavigationBarItem(
icon: Image.asset(
_tabList[index]["icon"]!, //正常显示图标
width: 30,
height: 30,
),
activeIcon: Image.asset(
_tabList[index]["active_icon"]!,
width: 30,
height: 30,
),
label: _tabList[index]["text"],
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("主页"),
),
bottomNavigationBar: BottomNavigationBar(items: _getTabBarWidget(),type: BottomNavigationBarType.fixed)
);
}
}
3.3 第三次编写代码
第三次编码在第二次的编码基础上增加了以下四点:
(1)独立模块化:四个页面(首页、分类、购物车、我的)拆分成了独立组件,代码结构清晰,便于维护和团队协作。
(2)IndexedStack保持状态:切换页面时保留滚动位置和表单数据,提升用户体验和性能
(3)SafeArea安全适配:自动避让刘海屏和系统UI区域,确保内容显示完整不被遮挡
(4)导航栏样式优化:统一选中与未选中颜色,增强视觉效果和美观度。
新建以下四个文件夹以及代码:Cart文件夹下的index.dart,Category文件夹下的index.dart,Home文件夹下的index.dart,Mine文件夹下的index.dart。

lib/pages/Main/index.dart代码
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:qing_mall/pages/Cart/index.dart';
import 'package:qing_mall/pages/Category/index.dart';
import 'package:qing_mall/pages/Home/home.dart';
import 'package:qing_mall/pages/Mine/mine.dart';
/*
* @author:幻影
* @date:2025-1-25
*[Tabs:首页]
*/
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
//定义数据 根据数据进行渲染4个导航 List<Map>:key value <String,String>是泛型
//一般应用程序的导航是固定的
final List<Map<String, String>> _tabList = [
{
"icon": "lib/assets/ic_public_home_normal.png", //未选中
"active_icon": "lib/assets/ic_public_home_active.png", //选中
"text": "首页",
},
{
"icon": "lib/assets/ic_public_pro_normal.png", //未选中
"active_icon": "lib/assets/ic_public_pro_active.png", //选中
"text": "分类",
},
{
"icon": "lib/assets/ic_public_cart_normal.png", //未选中
"active_icon": "lib/assets/ic_public_cart_active.png", //选中
"text": "购物车",
},
{
"icon": "lib/assets/ic_public_my_normal.png", //未选中
"active_icon": "lib/assets/ic_public_my_active.png", //选中
"text": "我的",
},
];
int _currentIndex = 0;
//返回底部渲染的四个分类
List<BottomNavigationBarItem> _getTabBarWidget() {
return List.generate(_tabList.length, (int index) {
return BottomNavigationBarItem(
icon: Image.asset(
_tabList[index]["icon"]!, //正常显示图标
width: 30,
height: 30,
),
activeIcon: Image.asset(
_tabList[index]["active_icon"]!,
width: 30,
height: 30,
),
label: _tabList[index]["text"],
);
});
}
List<Widget> _getChildren() {
return [HomeView(), CategoryView(), CartView(), MineView()];
}
@override
Widget build(BuildContext context) {
return Scaffold(
//SafeArea会避开安全区组件
body: SafeArea(
child: IndexedStack(
index: _currentIndex,
children: _getChildren(), //放置四个组件
)),
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels: true,
unselectedItemColor: Colors.black,
selectedItemColor: Colors.black,
onTap: (int index) {
//index就是当前点击的索引
_currentIndex = index;
setState(() {});
},
items: _getTabBarWidget(),
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex, //选中下标
));
}
}
lib/pages/Cart/index.dart代码
import 'package:flutter/cupertino.dart';
//View结尾表示不是页面,Page结尾一般才是页面
class CartView extends StatefulWidget {
const CartView({super.key});
@override
State<CartView> createState() => _CartViewState();
}
class _CartViewState extends State<CartView> {
@override
Widget build(BuildContext context) {
return Center(
child: Text("购物车"),
);
}
}
lib/pages/Category/index.dart代码
import 'package:flutter/cupertino.dart';
class CategoryView extends StatefulWidget {
const CategoryView({super.key});
@override
State<CategoryView> createState() => _CategoryViewState();
}
class _CategoryViewState extends State<CategoryView> {
@override
Widget build(BuildContext context) {
return Center(
child: Text("分类"),
);
}
}
lib/pages/Home/index.dart代码
import 'package:flutter/cupertino.dart';
class HomeView extends StatefulWidget {
const HomeView({super.key});
@override
State<HomeView> createState() => _CartViewState();
}
class _CartViewState extends State<HomeView> {
@override
Widget build(BuildContext context) {
return Center(
child: Text("首页"),
);
}
}
lib/pages/Mine/index.dart代码
import 'package:flutter/cupertino.dart';
class HomeView extends StatefulWidget {
const HomeView({super.key});
@override
State<HomeView> createState() => _CartViewState();
}
class _CartViewState extends State<HomeView> {
@override
Widget build(BuildContext context) {
return Center(
child: Text("首页"),
);
}
}
提交代码:

3.4 运行效果
最终的运行效果是点击底部导航栏对应的Tab显示对应的内容,比如点击我的就显示Mine/index.dart代码中的内容,点击购物车就显示Cart/index.dart代码中的的内容
安卓端:


鸿蒙端:


感谢您的观看,如果本篇文章对你有帮助,请你点个赞支持一下~您的支持就是我的动力。
最后,欢迎加入开源鸿蒙跨平台社区
https://openharmonycrossplatform.csdn.net
更多推荐




所有评论(0)