Flutter鸿蒙开发指南(四):主页Tab栏实现

一、需求分析

以常见的电商平台举例,如淘宝、京东、拼多多都有底部导航栏。本章节要实现的内容就是实现四个底部导航栏:首页、分类、购物车、我的。

淘宝

京东

拼多多

二、使用git回退代码

在Flutter鸿蒙开发指南(三)使用dio库实现网络请求这一节中,我们完成了网络请求。但是这个和我们要做的电商App所需接口不符合。所以我们要对代码进行回撤。

俗话说:"世上没有后悔药"。

但是在代码的世界是有后悔药的,我们要使用Git的操作进行回撤退就相当于后悔药了。

使用Git回退会将工作目录和暂存区完全恢复到上一次提交的状态,所有未提交的更改都将永久丢失,所以大家需要根据情况而用。我这里是完全不需要上次的代码了,所以直接全部丢失也无所谓。

复制代码
git reset --hard HEAD

当执行完上面的Git命令,你会发现代码变成了我们第一次提交"搭建基础路由和组件"时候的状态(变成了我们第一次提交时候的代码)

三、实现主页Tab栏实现

首先将文件夹中的八张图片拷贝,分别是底部导航栏的选中和未选中。复制粘贴到项目中的"assets"文件夹中。大家可以在我的代码仓库就获取这八张图片,顺便给我点个Star。

青商城 - AtomGit | GitCodehttps://atomgit.com/Deng666/shangcheng

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

改成如图所示,并且点击右上角的Pub get,如果不会改就复制粘贴以下代码:

Dart 复制代码
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)图片的路径错误,修改好正确路径即可

Dart 复制代码
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回调来处理切换即可。(请看第三次编写代码)

Dart 复制代码
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代码

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代码

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代码

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代码

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代码

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

相关推荐
2601_9499706911 小时前
HumanizeDuration 跨端三方库仓颉适配全流程
前端·javascript·flutter
传奇开心果编程12 小时前
【Flutter踩坑排雷】第3课:const 误用坑|const 错误使用引发 UI 异常与性能损耗
flutter·移动开发·跨平台·dart·编程语言理论·编程框架·声明式ui
程序员老刘14 小时前
你的Flutter项目要不要紧急升级3.47.8?
flutter·客户端
Talyra4216 小时前
不想开会员,我写了一个记账 App
flutter
2601_9495758621 小时前
JSON5 跨端三方库仓颉适配全流程
前端·javascript·flutter
法的空间21 小时前
AI 编程时代,为什么 Dart 分析会拖慢大型项目的开发
android·flutter·ios
恋猫de小郭1 天前
Flutter 紧急发布 3.47.8 安全升级版本,建议都升级
android·前端·flutter
好的佩奇2 天前
Dart 之模式
android·flutter·dart
淡淡的香烟2 天前
Flutter实现多语言切换
android·flutter·1024程序员节
事圆则缓2 天前
从零搭建一套 Flutter 架构:先跑通数据流
flutter