本文参考Flutter鸿蒙开发指南(四):主页Tab栏实现-CSDN博客

在上一节中,我们完成了Dio 库实现简单的网络请求。这一节,我们来研究电商 App 底部 Tab 栏的实现。

一、环境准备:Git回退代码(可选)

若上一节开发后有多余代码干扰,可将工作目录和暂存区完全恢复到上一次提交的干净状态,执行以下Git命令即可:

git reset --hard HEAD

提示:若无需回退,可直接跳过本步骤,确保项目无报错即可进入下一步开发。

二、资源准备:导入Tab栏图标资源

Tab栏需要用到“选中/未选中”两种状态的图标,共5个(含备用图标),获取及导入步骤如下:

  1. https://atomgit.com/Deng666/shangcheng访问仓库地址:https://atomgit.com/Deng666/shangcheng
  2. 在仓库中找到「assets」文件夹,下载其中的图标图片
  3. 在自己的Flutter项目中,新建路径「lib/assets/」,将下载的图片复制粘贴到该文件夹下
  4. 配置资源路径:打开项目根目录的「pubspec.yaml」文件,解开「assets」相关注释,并修改为以下配置(确保资源能被Flutter识别):

用到的图标图片:

解开这里注释

然后把代码更改成图中样式

代码如下:

flutter:
  uses-material-design: true
 
  # To add assets to your application, add an assets section, like this:
  assets:
    - lib/assets/
 
 

三、核心开发:编写Tab栏及页面代码

3.1 第一步:配置入口,指定Tab栏为主页

在lib/pages/Main/index.dart路径下,编写代码如下:

import 'package:flutter/material.dart';
 
/*
 * @author:Lionel689
 * @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("主页"),
      ),
    );
  }
}

修改项目入口文件main代码:

import 'package:flutter/material.dart';
import 'package:xiuhu_mall/pages/Main/index.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'xiuhu商城',
      theme: ThemeData(
        // 保留主题配置,也可以根据需要修改
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      // 将首页从默认的 MyHomePage 改为 MainPage(底部Tab栏页面)
      home: const MainPage(),
    );
  }
}

3.2 第二步:编写Tab栏核心代码

此时还无法点击导航栏。原因是缺少了关键的当前选中索引管理。

3.3 第三步:编写4个Tab对应的子页面

新建以下四个文件夹:Cart文件夹下的index.dart,Category文件夹下的index.dart,Home文件夹下的index.dart,Mine文件夹下的index.dart。

新增代码如下:

修改Tab栏:lib/pages/Main/index.dart代码

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:xiuhu_mall/pages/Cart/index.dart';
import 'package:xiuhu_mall/pages/Category/index.dart';
import 'package:xiuhu_mall/pages/Home/home.dart';
import 'package:xiuhu_mall/pages/Mine/mine.dart';

/*
 * @author:Lionel689
 * @date:2025-2-1
 *[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 MineView extends StatefulWidget {
  const MineView({super.key});

  @override
  State<MineView> createState() => _MineViewState();
}

class _MineViewState extends State<MineView> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("我的"),
    );
  }
}

四、运行结果

完成以上代码编写后,运行Flutter项目(双端均可测试,鸿蒙端、Android端效果一致),预期实现以下效果:

  1. App启动后,默认显示「首页」,底部Tab栏默认选中「首页」图标(高亮显示)。​
  2. 点击底部「首页、分类、购物车、我的」Tab,可快速切换到对应页面,选中的Tab图标和文本会高亮。​
  3. 页面切换时,顶部状态栏、底部Tab栏不遮挡内容(SafeArea生效),页面状态保持稳定(IndexedStack生效)。

五、提交代码

完成Tab栏开发并测试通过后,使用Git提交代码,保存当前开发进度,执行以下命令(依次在控制台输入):

git add .
git commit -m "完成主页tab实现"
git push

最后,欢迎加入开源鸿蒙跨平台社区
https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐