【HarmonyOS】DAY8:Flutter 轮播图数据获取完整指南(从API到UI展示)
// 轮播图数据模型});/// 从 JSON 创建对象?'',?'',步骤内容1页面初始化调用 API2Dio 发送网络请求3服务器返回 JSON 数据4使用 fromJSON 转换数据5setState 更新 UI6Image.network 加载网络图片API 调用放在initState中使用fromJSON工厂函数转换数据使用mounted检查防止内存泄漏使用和处理图片加载添加刷新功能添加
·
Flutter 轮播图数据获取完整指南(从API到UI展示)
前言
在前面的文章中,我们已经实现了轮播图的UI组件。本篇文章将介绍如何从服务器获取真实的轮播图数据,并将数据展示到页面上。
本文重点:
- ✅ API接口调用流程
- ✅ JSON数据转换
- ✅ 数据传递到组件
- ✅ 网络图片加载
- ✅ 加载和错误状态处理
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
一、数据流程
┌─────────────┐
│ 首页组件 │
│ initState │
└──────┬──────┘
│
▼
┌─────────────┐
│调用 API │
│getBannerListAPI()
└──────┬──────┘
│
▼
┌─────────────┐
│ Dio 发送请求│
│ /home/banner│
└──────┬──────┘
│
▼
┌─────────────┐
│ 服务器返回 │
│ JSON 数据 │
└──────┬──────┘
│
▼
┌─────────────┐
│fromJSON转换│
│→ List<BannerItem>
└──────┬──────┘
│
▼
┌─────────────┐
│ setState │
│ 更新 UI │
└──────┬──────┘
│
▼
┌─────────────┐
│轮播图显示 │
│网络图片 │
└─────────────┘
二、API接口信息
| 项目 | 值 |
|---|---|
| 接口地址 | https://meikou-api.itheima.net/home/banner |
| 请求方式 | GET |
| 超时时间 | 10秒 |
返回数据示例:
{
"code": "1",
"message": "success",
"result": [
{
"id": "1",
"imgUrl": "https://image.example.com/banner1.jpg"
},
{
"id": "2",
"imgUrl": "https://image.example.com/banner2.jpg"
}
]
}
三、数据模型定义
文件:lib/viewmodels/home.dart
/// 轮播图数据模型
class BannerItem {
final String id;
final String imgUrl;
BannerItem({
required this.id,
required this.imgUrl,
});
/// 从 JSON 创建对象
factory BannerItem.fromJSON(Map<String, dynamic> json) {
return BannerItem(
id: json['id']?.toString() ?? '',
imgUrl: json['imgUrl'] ?? '',
);
}
}
四、API接口封装
文件:lib/api/home.dart
import 'package:harmonyos_day_four/utils/DioRequest.dart';
import 'package:harmonyos_day_four/viewmodels/home.dart';
import 'package:harmonyos_day_four/constants/index.dart';
/// 获取轮播图列表数据
Future<List<BannerItem>> getBannerListAPI() async {
// 调用 Dio 请求
final result = await dioRequest.get(HttpConstants.BANNER_LIST);
// 转换为 List
final list = result as List;
// 遍历转换每个 item
return list.map((item) {
return BannerItem.fromJSON(item as Map<String, dynamic>);
}).toList();
}
封装说明:
- 调用
dioRequest.get()发送请求 - 返回的是
result数组 - 使用
map遍历转换每个对象 - 使用
fromJSON工厂函数转换
五、页面中调用API
文件:lib/pages/home/index.dart
import 'package:flutter/material.dart';
import 'package:harmonyos_day_four/api/home.dart';
import 'package:harmonyos_day_four/components/Home/HmSlider.dart';
import 'package:harmonyos_day_four/viewmodels/home.dart';
class HomeView extends StatefulWidget {
const HomeView({super.key});
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
// 轮播图数据
List<BannerItem> _bannerList = [];
void initState() {
super.initState();
// 页面初始化时获取数据
_getBannerList();
}
/// 获取轮播图数据
void _getBannerList() async {
try {
// 调用 API
_bannerList = await getBannerListAPI();
// 更新 UI
setState(() {});
} catch (e) {
// 打印错误信息
print('获取轮播图数据失败: $e');
}
}
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
// 轮播图组件
SliverToBoxAdapter(
child: HmSlider(bannerList: _bannerList),
),
const SliverToBoxAdapter(child: SizedBox(height: 10)),
// 其他组件...
],
);
}
}
六、轮播图组件支持网络图片
文件:lib/components/Home/HmSlider.dart
class HmSlider extends StatefulWidget {
// 接收轮播图数据参数
final List<BannerItem> bannerList;
const HmSlider({super.key, required this.bannerList});
// ... 其他代码
Widget build(BuildContext context) {
final double screenWidth = MediaQuery.of(context).size.width;
// 如果没有数据,显示加载中
if (bannerList.isEmpty) {
return SizedBox(
height: 300,
child: Container(
color: Colors.grey[300],
child: const Center(
child: CircularProgressIndicator(),
),
),
);
}
return SizedBox(
height: 300,
child: PageView.builder(
controller: _pageController,
itemCount: bannerList.length,
itemBuilder: (context, index) {
// 使用 Image.network 加载网络图片
return Image.network(
bannerList[index].imgUrl,
width: screenWidth,
height: 300,
fit: BoxFit.cover,
// 加载中显示进度
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.grey[200],
child: Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
// 加载失败显示占位图
errorBuilder: (context, error, stackTrace) {
return Container(
color: Colors.grey[300],
child: const Center(
child: Icon(Icons.broken_image, size: 50),
),
);
},
);
},
),
);
}
}
七、数据转换过程详解
7.1 JSON 转换流程
服务器返回的 JSON:
{
"code": "1",
"result": [
{"id": "1", "imgUrl": "https://xxx.com/1.jpg"}
]
↓
DioRequest 处理后:
提取 result 字段 → [{"id": "1", "imgUrl": "https://xxx.com/1.jpg"}]
↓
List.map 遍历:
for each item in list:
BannerItem.fromJSON(item)
↓
最终数据:
List<BannerItem>[
BannerItem(id: "1", imgUrl: "https://xxx.com/1.jpg"),
...
]
7.2 fromJSON 工厂函数
// 原始 JSON
{"id": "1", "imgUrl": "https://xxx.com/1.jpg"}
↓
// fromJSON 处理
BannerItem(
id: json['id']?.toString() ?? '', // "1"
imgUrl: json['imgUrl'] ?? '' // "https://xxx.com/1.jpg"
)
为什么使用 ?? 空合并运算符?
- 防止服务器返回 null
- 提供默认空字符串
- 避免空指针异常
八、常见问题与解决方案
问题1:轮播图不显示任何内容
原因排查:
- 检查数据是否为空
print('轮播图数据: $_bannerList'); // 调试打印
- 检查 API 是否被调用
void _getBannerList() async {
print('开始获取数据...');
_bannerList = await getBannerListAPI();
print('获取到 ${_bannerList.length} 条数据');
setState(() {});
}
- 检查网络请求是否成功
try {
_bannerList = await getBannerListAPI();
print('API 调用成功');
} catch (e) {
print('API 调用失败: $e');
}
问题2:网络图片显示空白
原因分析:
- 网络图片链接无效
- HarmonyOS 网络权限配置问题
- 图片链接需要 HTTPS
解决方案:
- 使用 errorBuilder 查看错误
Image.network(
url,
errorBuilder: (context, error, stackTrace) {
print('图片加载失败: $error');
return Container(
color: Colors.grey[300],
child: const Icon(Icons.broken_image),
);
},
)
- 检查图片链接
// 确保图片链接是 HTTPS
imgUrl: json['imgUrl'] ?? '',
问题3:JSON 解析报错
错误信息:
type 'List<dynamic>' is not a subtype of type 'List<String>'
原因分析:
服务器返回的数据结构与预期不符。
解决方案:
// 使用 as List 转换
final list = result as List;
// 检查每个 item 的类型
list.map((item) {
print('item type: ${item.runtimeType}');
return BannerItem.fromJSON(item as Map<String, dynamic>);
}).toList();
问题4:setState 报错 “setState() called after dispose()”
原因分析:
异步请求完成后,页面已经销毁。
解决方案:
void _getBannerList() async {
try {
_bannerList = await getBannerListAPI();
// 检查组件是否还存在
if (!mounted) return;
setState(() {});
} catch (e) {
print('获取数据失败: $e');
}
}
问题5:数据格式不匹配
问题描述:
服务器返回的字段名与代码不一致。
解决方案:
factory BannerItem.fromJSON(Map<String, dynamic> json) {
return BannerItem(
id: json['id']?.toString() ?? json['bannerId']?.toString() ?? '',
imgUrl: json['imgUrl'] ?? json['image'] ?? '',
);
}
九、完整数据流程示例
// 1. 页面初始化
void initState() {
super.initState();
_getBannerList(); // 开始获取数据
}
// 2. 调用 API
void _getBannerList() async {
try {
// 3. 发起网络请求
_bannerList = await getBannerListAPI();
// 4. 更新 UI
if (!mounted) return;
setState(() {});
// 5. PageView 重建,显示网络图片
} catch (e) {
print('获取数据失败: $e');
}
}
// 6. 轮播图组件显示
PageView.builder(
itemBuilder: (context, index) {
return Image.network(
_bannerList[index].imgUrl, // 7. 加载网络图片
fit: BoxFit.cover,
);
},
)
十、项目结构
lib/
├── api/
│ └── home.dart # getBannerListAPI()
├── constants/
│ └── index.dart # BANNER_LIST 常量
├── utils/
│ └── DioRequest.dart # 网络请求工具
├── viewmodels/
│ └── home.dart # BannerItem.fromJSON()
├── components/Home/
│ └── HmSlider.dart # 轮播图组件
└── pages/home/
└── index.dart # _getBannerList()
十一、总结
本文实现了从 API 获取轮播图数据的完整流程:
| 步骤 | 内容 |
|---|---|
| 1 | 页面初始化调用 API |
| 2 | Dio 发送网络请求 |
| 3 | 服务器返回 JSON 数据 |
| 4 | 使用 fromJSON 转换数据 |
| 5 | setState 更新 UI |
| 6 | Image.network 加载网络图片 |
关键要点:
- API 调用放在
initState中 - 使用
fromJSON工厂函数转换数据 - 使用
mounted检查防止内存泄漏 - 使用
loadingBuilder和errorBuilder处理图片加载
后续优化:
- 添加刷新功能
- 添加本地缓存
- 添加加载骨架屏
- 支持数据刷新
十二、参考资料
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)