Flutter × OpenHarmony:从零实现电商首页 + 底部导航栏(完整流程 + 踩坑记录)

🌸你好呀!我是 lbb小魔仙
🌟 感谢陪伴~ 小白博主在线求友
🌿 跟着小白学Linux/Java/Python
📖 专栏汇总:
《Linux》专栏 | 《Java》专栏 | 《Python》专栏

- [Flutter × OpenHarmony:从零实现电商首页 + 底部导航栏(完整流程 + 踩坑记录)](#Flutter × OpenHarmony:从零实现电商首页 + 底部导航栏(完整流程 + 踩坑记录))
-
- 前言
- 一、项目目标
- 二、详细开发流程
-
- [2.1 创建 Flutter 项目](#2.1 创建 Flutter 项目)
- [2.2 创建项目目录结构](#2.2 创建项目目录结构)
- [2.3 创建组件模板](#2.3 创建组件模板)
- [2.4 实现各个组件](#2.4 实现各个组件)
-
- [(1)轮播图组件 `HmSlider.dart`](#(1)轮播图组件
HmSlider.dart) - [(2)分类组件 `HmCategory.dart`(横向滚动)](#(2)分类组件
HmCategory.dart(横向滚动)) - [(3)推荐组件 `HmSuggestion.dart`](#(3)推荐组件
HmSuggestion.dart) - [(4)爆款推荐组件 `HmHot.dart`](#(4)爆款推荐组件
HmHot.dart) - [(5)商品列表组件 `HmMoreList.dart`(网格布局)](#(5)商品列表组件
HmMoreList.dart(网格布局))
- [(1)轮播图组件 `HmSlider.dart`](#(1)轮播图组件
- [2.5 组装首页 `pages/home/index.dart`](#2.5 组装首页
pages/home/index.dart) - [2.6 创建其他页面](#2.6 创建其他页面)
-
- [分类页面 `pages/category/index.dart`](#分类页面
pages/category/index.dart) - [购物车页面 `pages/cart/index.dart`](#购物车页面
pages/cart/index.dart) - [我的页面 `pages/profile/index.dart`](#我的页面
pages/profile/index.dart)
- [分类页面 `pages/category/index.dart`](#分类页面
- [2.7 创建底部导航栏主框架 `pages/main/index.dart`](#2.7 创建底部导航栏主框架
pages/main/index.dart) - [2.8 修改应用入口 `main.dart`](#2.8 修改应用入口
main.dart)
- 三、核心知识点总结
-
- [3.1 CustomScrollView + Sliver 体系](#3.1 CustomScrollView + Sliver 体系)
- [3.2 布局组件对比](#3.2 布局组件对比)
- [3.3 BottomNavigationBar 使用要点](#3.3 BottomNavigationBar 使用要点)
- 四、遇到的问题及解决方案
-
- [问题 1:CustomScrollView 报错 "All children must be Sliver widgets"](#问题 1:CustomScrollView 报错 "All children must be Sliver widgets")
- [问题 2:ListView 横向滚动后高度失效](#问题 2:ListView 横向滚动后高度失效)
- [问题 3:底部导航栏图标不显示/显示异常](#问题 3:底部导航栏图标不显示/显示异常)
- [问题 4:组件内边距不生效](#问题 4:组件内边距不生效)
- [问题 5:DevEco Studio 路径问题](#问题 5:DevEco Studio 路径问题)
- 五、项目运行
-
- [在 DevEco Studio 中运行](#在 DevEco Studio 中运行)
- 运行效果
- 六、总结
前言
本文将详细记录使用 Flutter 开发鸿蒙电商应用首页的完整过程,包括组件化设计、CustomScrollView 滚动布局、底部导航栏实现,以及在开发过程中遇到的问题和解决方案。
项目环境:
- Flutter SDK 3.x
- Dart 3.x
- DevEco Studio
- HarmonyOS API 9+
一、项目目标
- 实现电商首页基础布局(轮播图、分类、推荐、爆款、商品列表)
- 实现底部导航栏(首页、分类、购物车、我的)
- 采用组件化设计,提升代码复用性
二、详细开发流程
2.1 创建 Flutter 项目
bash
flutter create harmonyos_day_four
cd harmonyos_day_four
2.2 创建项目目录结构
lib/
├── components/
│ └── Home/
│ ├── HmSlider.dart # 轮播图组件
│ ├── HmCategory.dart # 分类组件
│ ├── HmSuggestion.dart # 推荐组件
│ ├── HmHot.dart # 爆款推荐组件
│ └── HmMoreList.dart # 商品列表组件
├── pages/
│ ├── home/
│ │ └── index.dart # 首页
│ ├── category/
│ │ └── index.dart # 分类页面
│ ├── cart/
│ │ └── index.dart # 购物车页面
│ ├── profile/
│ │ └── index.dart # 我的页面
│ └── main/
│ └── index.dart # 底部导航主框架
└── main.dart # 应用入口
创建目录命令:
bash
cd lib
mkdir components components/Home
mkdir pages pages/home pages/category pages/cart pages/profile pages/main
2.3 创建组件模板
在每个组件文件中输入 stful 快捷键,自动生成 StatefulWidget 模板:
dart
import 'package:flutter/material.dart';
class HmSlider extends StatefulWidget {
const HmSlider({super.key});
@override
State<HmSlider> createState() => _HmSliderState();
}
class _HmSliderState extends State<HmSlider> {
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
2.4 实现各个组件
(1)轮播图组件 HmSlider.dart
dart
import 'package:flutter/material.dart';
class HmSlider extends StatefulWidget {
const HmSlider({super.key});
@override
State<HmSlider> createState() => _HmSliderState();
}
class _HmSliderState extends State<HmSlider> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10), // 添加左右内边距
child: Container(
color: Colors.blue,
height: 300,
alignment: Alignment.center,
child: const Text(
'轮播图',
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
);
}
}
(2)分类组件 HmCategory.dart(横向滚动)
dart
import 'package:flutter/material.dart';
class HmCategory extends StatefulWidget {
const HmCategory({super.key});
@override
State<HmCategory> createState() => _HmCategoryState();
}
class _HmCategoryState extends State<HmCategory> {
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal, // 横向滚动
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Container(
alignment: Alignment.center,
width: 80,
height: 100,
color: Colors.blue,
child: Text("分类$index", style: const TextStyle(color: Colors.white)),
margin: const EdgeInsets.symmetric(horizontal: 10),
);
},
),
);
}
}
(3)推荐组件 HmSuggestion.dart
dart
import 'package:flutter/material.dart';
class HmSuggestion extends StatefulWidget {
const HmSuggestion({super.key});
@override
State<HmSuggestion> createState() => _HmSuggestionState();
}
class _HmSuggestionState extends State<HmSuggestion> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10), // 添加左右内边距
child: Container(
color: Colors.blue,
alignment: Alignment.center,
height: 300,
child: const Text(
"推荐",
style: TextStyle(color: Colors.white),
),
),
);
}
}
(4)爆款推荐组件 HmHot.dart
dart
import 'package:flutter/material.dart';
class HmHot extends StatefulWidget {
const HmHot({super.key});
@override
State<HmHot> createState() => _HmHotState();
}
class _HmHotState extends State<HmHot> {
@override
Widget build(BuildContext context) {
return Container(
height: 200,
color: Colors.blue,
alignment: Alignment.center,
child: const Text(
'爆款推荐',
style: TextStyle(color: Colors.white),
),
);
}
}
(5)商品列表组件 HmMoreList.dart(网格布局)
dart
import 'package:flutter/material.dart';
class HmMorelist extends StatefulWidget {
const HmMorelist({super.key});
@override
State<HmMorelist> createState() => _HmMorelistState();
}
class _HmMorelistState extends State<HmMorelist> {
@override
Widget build(BuildContext context) {
return SliverGrid.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 两列网格
mainAxisSpacing: 10, // 纵向间距
crossAxisSpacing: 10, // 横向间距
),
itemBuilder: (BuildContext context, int index) {
return Container(
child: const Text(
"商品",
style: TextStyle(color: Colors.white),
),
color: Colors.blue,
alignment: Alignment.center,
);
},
);
}
}
2.5 组装首页 pages/home/index.dart
dart
import 'package:flutter/cupertino.dart';
import 'package:harmonyos_day_four/components/Home/HmCategory.dart';
import 'package:harmonyos_day_four/components/Home/HmHot.dart';
import 'package:harmonyos_day_four/components/Home/HmMoreList.dart';
import 'package:harmonyos_day_four/components/Home/HmSlider.dart';
import 'package:harmonyos_day_four/components/Home/HmSuggestion.dart';
class HomeView extends StatefulWidget {
const HomeView({super.key});
@override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
// 获取滚动容器的内容
List<Widget> _getScrollChildren() {
return [
const SliverToBoxAdapter(child: HmSlider()), // 轮播图
const SliverToBoxAdapter(child: SizedBox(height: 10)),
const SliverToBoxAdapter(child: HmCategory()), // 分类
const SliverToBoxAdapter(child: SizedBox(height: 10)),
const SliverToBoxAdapter(child: HmSuggestion()), // 推荐
const SliverToBoxAdapter(child: SizedBox(height: 10)),
// Flex + Expanded 实现横向均分布局(两个爆款推荐)
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Flex(
direction: Axis.horizontal,
children: [
const Expanded(child: HmHot()),
const SizedBox(width: 10),
Expanded(child: HmHot()),
],
),
),
),
const SliverToBoxAdapter(child: SizedBox(height: 10)),
const HmMorelist(), // 商品网格
];
}
@override
Widget build(BuildContext context) {
// CustomScrollView 要求:必须是 sliver 家族的内容
return CustomScrollView(slivers: _getScrollChildren());
}
}
2.6 创建其他页面
分类页面 pages/category/index.dart
dart
import 'package:flutter/material.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 Scaffold(
appBar: AppBar(
title: const Text('分类'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.category, size: 100, color: Colors.blue),
SizedBox(height: 20),
Text(
'分类页面',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
],
),
),
);
}
}
购物车页面 pages/cart/index.dart
dart
import 'package:flutter/material.dart';
class CartView extends StatefulWidget {
const CartView({super.key});
@override
State<CartView> createState() => _CartViewState();
}
class _CartViewState extends State<CartView> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('购物车'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.shopping_cart, size: 100, color: Colors.orange),
SizedBox(height: 20),
Text(
'购物车页面',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
],
),
),
);
}
}
我的页面 pages/profile/index.dart
dart
import 'package:flutter/material.dart';
class ProfileView extends StatefulWidget {
const ProfileView({super.key});
@override
State<ProfileView> createState() => _ProfileViewState();
}
class _ProfileViewState extends State<ProfileView> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('我的'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.person, size: 100, color: Colors.green),
SizedBox(height: 20),
Text(
'我的页面',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
],
),
),
);
}
}
2.7 创建底部导航栏主框架 pages/main/index.dart
dart
import 'package:flutter/material.dart';
import 'package:harmonyos_day_four/pages/home/index.dart';
import 'package:harmonyos_day_four/pages/category/index.dart';
import 'package:harmonyos_day_four/pages/cart/index.dart';
import 'package:harmonyos_day_four/pages/profile/index.dart';
class MainContainer extends StatefulWidget {
const MainContainer({super.key});
@override
State<MainContainer> createState() => _MainContainerState();
}
class _MainContainerState extends State<MainContainer> {
// 当前选中的页面索引
int _currentIndex = 0;
// 页面列表
final List<Widget> _pages = [
const HomeView(),
const CategoryView(),
const CartView(),
const ProfileView(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
label: '分类',
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
label: '购物车',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '我的',
),
],
),
);
}
}
2.8 修改应用入口 main.dart
dart
import 'package:flutter/material.dart';
import 'package:harmonyos_day_four/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: 'HarmonyOS Day Four',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MainContainer(),
);
}
}
三、核心知识点总结
3.1 CustomScrollView + Sliver 体系
CustomScrollView 是 Flutter 中实现混合滚动的核心组件,它要求所有子组件必须是 Sliver 家族:
| 组件 | 用途 |
|---|---|
SliverToBoxAdapter |
将普通 Widget 转为 Sliver |
SliverGrid.builder |
网格布局 Sliver |
SliverList.builder |
列表布局 Sliver |
SliverAppBar |
可滚动的应用栏 |
3.2 布局组件对比
| 组件 | 用途 | 关键属性 |
|---|---|---|
Flex |
弹性布局容器 | direction: Axis.horizontal/vertical |
Expanded |
占据剩余空间 | 配合 Flex 使用实现均分 |
Padding |
内边距 | padding: EdgeInsets.symmetric(horizontal: 10) |
SizedBox |
固定尺寸/间距 | height: 10 / width: 10 |
3.3 BottomNavigationBar 使用要点
dart
BottomNavigationBar(
currentIndex: _currentIndex, // 当前选中索引
onTap: (index) { ... }, // 点击回调
type: BottomNavigationBarType.fixed, // 固定模式(4+个Tab必选)
items: [ ... ], // 导航项列表
)
四、遇到的问题及解决方案
问题 1:CustomScrollView 报错 "All children must be Sliver widgets"
错误信息:
FlutterError (All children of a CustomScrollView must be Sliver widgets.
A Container was found, but Slivers are expected.)
原因分析:
CustomScrollView 的 slivers 参数只接受 Sliver 家族组件,不能直接放入普通 Widget 如 Container。
解决方案:
使用 SliverToBoxAdapter 包裹普通组件:
dart
// ❌ 错误写法
CustomScrollView(
slivers: [
Container(...), // 报错
],
)
// ✅ 正确写法
CustomScrollView(
slivers: [
SliverToBoxAdapter(child: Container(...)), // 正确
],
)
问题 2:ListView 横向滚动后高度失效
问题描述:
将 ListView.scrollDirection 设置为 Axis.horizontal 后,容器高度变为 0。
原因分析:
ListView 在横向滚动时,无法自动推断高度,需要父组件指定。
解决方案:
使用 SizedBox 或 Container 包裹并设置高度:
dart
SizedBox(
height: 100, // 必须指定高度
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => Container(...),
),
)
问题 3:底部导航栏图标不显示/显示异常
问题描述:
当底部导航项超过 3 个时,图标和文字显示异常。
原因分析:
BottomNavigationBar 默认是 shifting 模式,只支持 3 个以内的 Tab。
解决方案:
明确设置 type: BottomNavigationBarType.fixed:
dart
BottomNavigationBar(
type: BottomNavigationBarType.fixed, // 固定模式
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
BottomNavigationBarItem(icon: Icon(Icons.shopping_cart), label: '购物车'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
],
)
问题 4:组件内边距不生效
问题描述:
组件与屏幕边缘紧贴,没有间距。
原因分析:
没有使用 Padding 组件包裹内容。
解决方案:
在需要间距的组件外包裹 Padding:
dart
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10), // 左右各10间距
child: Container(...),
);
问题 5:DevEco Studio 路径问题
问题描述:
项目路径与创建时的项目名称不一致。
解决方案:
确认实际项目路径,注意拼写是否正确(如 harmonyos_dya_four vs harmonyos_day_four)。
五、项目运行
在 DevEco Studio 中运行
- 打开 DevEco Studio
File→Open→ 选择项目目录- 确保已安装 Flutter 和 Dart 插件
- 选择 HarmonyOS 模拟器或真机
- 点击运行按钮 ▶️
运行效果
- 首页:完整的电商首页布局,支持上下滚动
- 底部导航栏:4 个 Tab(首页、分类、购物车、我的)
- 页面切换:点击底部导航栏可切换页面

六、总结
通过本文,我们完成了以下内容:
- ✅ Flutter 组件化设计实践
- ✅ CustomScrollView + Sliver 混合滚动布局
- ✅ 底部导航栏完整实现
- ✅ 常见问题及解决方案记录
项目源码:
https://atomgit.com/lbbxmx111/haromyos_day_four
欢迎交流:
如有疑问,欢迎在评论区留言讨论!
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
📕个人领域 :Linux/C++/java/AI
🚀 个人主页 :有点流鼻涕 · CSDN
💬 座右铭 : "向光而行,沐光而生。"
