Flutter鸿蒙开发指南(九):获取分类数据并渲染

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

- Flutter鸿蒙开发指南(九):获取分类数据并渲染
-
- 前言
- 一、实现流程
-
- [1.1 分类接口介绍](#1.1 分类接口介绍)
- 二、代码实现
-
- [2.1 定义接口常量](#2.1 定义接口常量)
- [2.2 构建数据模型](#2.2 构建数据模型)
- [2.3 封装 API 调用](#2.3 封装 API 调用)
- [2.4 更新分类组件](#2.4 更新分类组件)
- [2.5 首页获取数据](#2.5 首页获取数据)
- 三、遇到问题及解决方法
-
- 问题1:空安全类型转换错误
- [问题2:嵌套 children 数组解析失败](#问题2:嵌套 children 数组解析失败)
- [问题3:UI 显示异常](#问题3:UI 显示异常)
- 问题4:组件参数传递错误
- 四、效果展示
- 五、总结
- 源码地址
前言
在电商应用中,分类数据是核心功能之一。本文将详细介绍如何在 Flutter/HarmonyOS 项目中实现分类数据的获取与渲染功能,完整展示从 API 接口定义到 UI 展示的完整流程。
一、实现流程
按照规范的七步开发流程:
(1)定义常量数据、基础地址、超时时间、业务状态、请求地址
(2)封装网络请求工具,基础地址,拦截器
(3)请求工具进一步解构,处理http状态和业务状态
(4)类工厂转化动态类型到对象类型
(5)封装请求API调用工厂函数
(6)更新页面组件接收参数
(7)初始化数据更新状态
1.1 分类接口介绍
接口地址 :https://meikou-api.itheima.net/home/category/head
返回数据示例:
json
{
"code": "1",
"result": [
{
"id": "1181622001",
"name": "气质女装",
"picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/c1/qznz.png",
"children": [
{
"id": "1191110001",
"name": "半裙",
"picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/c2/qznz_bq.png"
}
]
}
]
}
二、代码实现
2.1 定义接口常量
文件 :lib/constants/index.dart
dart
// 存放请求地址接口的常量
class HttpConstants {
// 轮播图接口
static const String BANNER_LIST = "/home/banner";
// 分类列表接口
static const String CATEGORY_LIST = "/home/category/head";
}
2.2 构建数据模型
文件 :lib/viewmodels/home.dart
dart
// 每一个分类具体类型
class CategoryItem {
String id;
String name;
String picture;
List<CategoryItem>? children;
CategoryItem({
required this.id,
required this.name,
required this.picture,
this.children,
});
// 工厂函数:从JSON创建对象
factory CategoryItem.formJSON(Map<String, dynamic> json) {
return CategoryItem(
id: json["id"] ?? "",
name: json["name"] ?? "",
picture: json["picture"] ?? "",
children: json["children"] == null
? null
: (json["children"] as List)
.map((item) => CategoryItem.formJSON(item as Map<String, dynamic>))
.toList(),
);
}
}
关键点:
- 使用
factory关键字声明工厂函数 - 使用空安全操作符
??提供默认值 - 递归处理
children嵌套结构
2.3 封装 API 调用
文件 :lib/api/home.dart
dart
/// 获取分类列表数据
Future<List<CategoryItem>> getCategoryListAPI() async {
// 发起请求并转换数据
final result = ((await dioRequest.get(HttpConstants.CATEGORY_LIST)) as List)
.map((item) {
return CategoryItem.formJSON(item as Map<String, dynamic>);
}).toList();
return result;
}
2.4 更新分类组件
文件 :lib/components/Home/HmCategory.dart
dart
import 'package:flutter/material.dart';
import 'package:harmonyos_day_four/viewmodels/home.dart';
class HmCategory extends StatefulWidget {
// 分类列表
final List<CategoryItem> categoryList;
const HmCategory({super.key, required this.categoryList});
@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: widget.categoryList.length,
itemBuilder: (BuildContext context, int index) {
final categoryItem = widget.categoryList[index];
return Container(
alignment: Alignment.center,
width: 80,
height: 100,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 231, 232, 234),
borderRadius: BorderRadius.circular(40),
),
margin: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network(categoryItem.picture, width: 40, height: 40),
Text(categoryItem.name,
style: const TextStyle(color: Colors.black)),
],
),
);
}),
);
}
}
2.5 首页获取数据
文件 :lib/pages/home/index.dart
dart
class _HomeViewState extends State<HomeView> {
// 分类列表
List<CategoryItem> _categoryList = [];
// 轮播图列表
List<BannerItem> _bannerList = [];
@override
void initState() {
super.initState();
_getBannederList();
_getCategoryList(); // 获取分类数据
}
// 获取分类列表
void _getCategoryList() async {
try {
_categoryList = await getCategoryListAPI();
setState(() {});
} catch (e) {
print('获取分类数据失败: $e');
}
}
// 在滚动视图中使用
List<Widget> _getScrollChildren() {
return [
SliverToBoxAdapter(child: HmSlider(bannerList: _bannerList)),
const SliverToBoxAdapter(child: SizedBox(height: 10)),
SliverToBoxAdapter(child: HmCategory(categoryList: _categoryList)), // 分类组件
// ...
];
}
}
三、遇到问题及解决方法
问题1:空安全类型转换错误
错误现象:
type 'Null' is not a subtype of type 'String'
原因分析 :API 返回的数据中某些字段可能为 null,直接使用 json["id"] 会导致类型错误。
解决方法 :使用空安全操作符 ?? 提供默认值
dart
// ❌ 错误写法
id: json["id"]
// ✅ 正确写法
id: json["id"] ?? ""
问题2:嵌套 children 数组解析失败
错误现象:
type 'List<dynamic>' is not a subtype of type 'List<CategoryItem>'
原因分析 :JSON 解析后的 children 是 List<dynamic> 类型,需要手动转换。
解决方法 :使用 map 进行类型转换
dart
children: json["children"] == null
? null
: (json["children"] as List)
.map((item) => CategoryItem.formJSON(item as Map<String, dynamic>))
.toList(),
问题3:UI 显示异常
错误现象:分类数据加载后,UI 没有更新显示
原因分析 :异步数据获取后没有调用 setState 更新界面
解决方法 :在 setState 回调中更新数据
dart
void _getCategoryList() async {
try {
_categoryList = await getCategoryListAPI();
setState(() {}); // 必须调用 setState 触发 UI 更新
} catch (e) {
print('获取分类数据失败: $e');
}
}
问题4:组件参数传递错误
错误现象:
The named parameter 'categoryList' is required but wasn't provided
原因分析 :HmCategory 组件添加了必需参数 categoryList,但调用时没有传递
解决方法:在组件调用时传递参数
dart
// ❌ 错误写法
const SliverToBoxAdapter(child: HmCategory()),
// ✅ 正确写法
SliverToBoxAdapter(child: HmCategory(categoryList: _categoryList)),
四、效果展示
分类列表以横向滚动方式展示,每个分类项包含:
-
圆角背景容器(灰色)
-
分类图标(网络图片)
-
分类名称(黑色文字)
┌────────────────────────────────────────────────────────────┐
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 图标 │ │ 图标 │ │ 图标 │ │ 图标 │ │ 图标 │ ... │
│ │名称 │ │名称 │ │名称 │ │名称 │ │名称 │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
│ ← 横向滚动 → │
└────────────────────────────────────────────────────────────┘

五、总结
本文介绍了 Flutter/HarmonyOS 电商应用中分类数据获取与渲染的完整实现流程:
- 数据模型:使用工厂模式实现 JSON 到对象的转换
- 网络请求:封装 API 调用,统一处理业务状态码
- UI 渲染:横向滚动列表展示分类数据
- 错误处理:空安全、类型转换、状态更新
通过遵循规范的七步开发流程,可以快速实现类似的数据获取与展示功能。
源码地址
https://atomgit.com/lbbxmx111/haromyos_day_four
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
📕个人领域 :Linux/C++/java/AI
🚀 个人主页 :有点流鼻涕 · CSDN
💬 座右铭 : "向光而行,沐光而生。"
