Flutter开源鸿蒙跨平台训练营 Day8获取轮播图网络数据并实现展示

Flutter鸿蒙开发指南:获取轮播图网络数据并实现展示

在前面的Flutter鸿蒙开发内容中,我们已经完成了轮播图的UI搭建,包含搜索框、导航指示器等基础组件的实现。本文将聚焦轮播图网络数据的获取与渲染,从环境准备、网络请求封装到数据解析、页面展示,完整实现从API接口拉取真实轮播图数据并在鸿蒙端展示的功能,全程基于Dio库实现网络请求,遵循Flutter工程化开发规范。

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


一、前置准备

1.1 核心实现步骤

本次开发的核心流程遵循工程化思想,分步骤实现网络请求与数据渲染,确保代码的可维护性和复用性:

  1. 安装Dio网络请求库,实现HTTP请求基础能力
  2. 定义全局常量,统一管理接口地址、超时时间等配置
  3. 二次封装Dio网络请求工具,封装GET/POST方法及拦截器
  4. 创建数据模型并添加工厂函数,实现JSON数据与实体类的解析
  5. 封装轮播图API调用方法,解耦网络请求与页面逻辑
  6. 在轮播图页面调用API,获取数据并更新UI
  7. 适配网络图片,完成轮播图的最终展示

1.2 接口信息说明

本次使用的轮播图接口为公开测试接口,请求方式为GET,接口详细信息如下:

项目 具体值
接口基础地址 https://meikou-api.itheima.net/
轮播图接口路径 /home/banner
完整请求地址 https://meikou-api.itheima.net/home/banner
请求方式 GET
响应数据格式 JSON

接口返回数据示例

json 复制代码
{
  "code": "1",
  "message": "success",
  "result": [
    {"id": "1", "imgUrl": "https://xxx.com/1.jpg"},
    {"id": "2", "imgUrl": "https://xxx.com/2.jpg"}
  ]
}
  • code:业务状态码,1表示请求成功
  • message:请求结果描述,成功时返回success
  • result:轮播图核心数据,数组格式,包含图片ID和网络地址

二、代码实现

2.1 安装Dio网络请求库

Dio是Flutter生态中常用的网络请求库,支持拦截器、请求取消、超时配置等功能,适合工程化开发。执行以下命令安装最新稳定版:

bash 复制代码
flutter pub add dio

安装完成后,pubspec.yaml文件的dependencies节点会自动添加Dio依赖,示例如下(版本号以实际安装为准):

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  dio: ^5.9.1 # Dio网络请求库

添加后执行flutter pub get同步依赖,确保库能正常引入。

2.2 创建全局常量类

为了统一管理项目中的接口配置、常量参数,创建常量类文件,实现一处定义、全局使用 ,减少硬编码问题。

创建文件lib/constants/index.dart,代码如下:

dart 复制代码
// 全局通用常量:基础地址、超时时间、业务状态码等
class GlobalConstants {
  // 接口基础地址
  static const String BASE_URL = "https://meikou-api.itheima.net/";
  // 网络请求超时时间(单位:秒)
  static const int TIME_OUT = 10;
  // 业务请求成功状态码
  static const String SUCCESS_CODE = "1";
}

// 网络请求接口地址常量:统一管理所有接口路径
class HttpConstants {
  // 轮播图数据请求接口
  static const String BANNER_LIST = "/home/banner";
}

设计说明 :使用static const定义常量的原因

  1. static:无需创建类的实例,直接通过类名访问,简化调用
  2. const:编译时常量,值不可修改,避免运行时意外篡改
  3. 全局共享:所有页面和工具类均可引用,节省内存且便于后期维护

2.3 二次封装Dio网络请求工具

原生Dio的调用方式较为零散,本次对其进行二次封装 ,封装通用的GET/POST方法、配置全局拦截器、统一处理响应数据,降低页面层的代码耦合。

创建文件lib/utils/DioRequest.dart,代码如下:

dart 复制代码
// 基于Dio的网络请求工具类,二次封装实现通用请求能力
import 'package:dio/dio.dart';
import 'package:harmonyos_day_four/constants/index.dart';

class DioRequest {
  // 初始化Dio实例
  final Dio _dio = Dio();

  // 构造函数:初始化Dio配置和拦截器
  DioRequest() {
    _dio.options
      ..baseUrl = GlobalConstants.BASE_URL // 配置基础地址
      ..connectTimeout = Duration(seconds: GlobalConstants.TIME_OUT) // 连接超时
      ..sendTimeout = Duration(seconds: GlobalConstants.TIME_OUT) // 发送超时
      ..receiveTimeout = Duration(seconds: GlobalConstants.TIME_OUT); // 接收超时
    // 添加全局拦截器
    _addInterceptor();
  }

  // 配置Dio拦截器:请求拦截、响应拦截、错误拦截
  void _addInterceptor() {
    _dio.interceptors.add(InterceptorsWrapper(
      // 请求拦截:可在此添加请求头、token等公共参数
      onRequest: (request, handler) {
        handler.next(request);
      },
      // 响应拦截:处理HTTP状态码,过滤非200-300的响应
      onResponse: (response, handler) {
        if (response.statusCode! >= 200 && response.statusCode! < 300) {
          handler.next(response);
          return;
        }
        handler.reject(DioException(requestOptions: response.requestOptions));
      },
      // 错误拦截:统一处理网络请求错误
      onError: (error, handler) {
        handler.reject(error);
      },
    ));
  }

  // 封装GET请求方法
  Future<dynamic> get(String url, {Map<String, dynamic>? params}) async {
    try {
      final response = await _dio.get(url, queryParameters: params);
      return _handleResponse(response);
    } catch (e) {
      rethrow;
    }
  }

  // 封装POST请求方法(预留,后续可用于提交数据)
  Future<dynamic> post(String url, {dynamic data}) async {
    try {
      final response = await _dio.post(url, data: data);
      return _handleResponse(response);
    } catch (e) {
      rethrow;
    }
  }

  // 统一处理响应数据:解析业务状态码,返回核心数据
  dynamic _handleResponse(Response<dynamic> response) {
    final data = response.data as Map<String, dynamic>;
    // 判断业务状态码是否成功
    if (data["code"] == GlobalConstants.SUCCESS_CODE) {
      return data["result"]; // 只返回核心的result数据,简化页面层解析
    }
    // 业务请求失败时,抛出异常并携带错误信息
    throw Exception(data["message"] ?? "加载数据异常");
  }
}

// 创建DioRequest单例对象,全局唯一,避免重复创建实例
final dioRequest = DioRequest();

封装亮点

  1. 单例模式:全局仅创建一个DioRequest实例,避免资源浪费
  2. 全局配置:在构造函数中统一配置基础地址、超时时间,无需重复设置
  3. 拦截器:实现请求、响应、错误拦截,便于后续扩展请求头、统一错误处理
  4. 数据过滤:_handleResponse方法统一处理业务状态码,页面层直接获取核心数据
  5. 异常抛出:请求失败时抛出异常,便于页面层捕获并处理UI提示

2.4 数据模型添加工厂函数

为了规范JSON数据解析,避免直接在页面层使用[]获取数据导致的空指针问题,创建轮播图数据模型,并添加工厂函数实现JSON转实体类

创建文件lib/models/banner_model.dart,代码如下:

dart 复制代码
// 轮播图数据模型
class BannerModel {
  // 轮播图ID
  final String id;
  // 轮播图网络图片地址
  final String imgUrl;

  // 构造函数
  BannerModel({required this.id, required this.imgUrl});

  // 工厂函数:从JSON解析为BannerModel实例
  factory BannerModel.fromJson(Map<String, dynamic> json) {
    return BannerModel(
      id: json["id"] ?? "",
      imgUrl: json["imgUrl"] ?? "",
    );
  }

  // 静态方法:将JSON数组解析为BannerModel列表
  static List<BannerModel> fromJsonList(List<dynamic> jsonList) {
    return jsonList.map((json) => BannerModel.fromJson(json)).toList();
  }
}

设计说明

  1. 成员变量使用final,保证不可变性,符合Flutter的不可变设计思想
  2. 工厂函数fromJson:实现单个JSON对象到实体类的解析,对空值做默认处理
  3. 静态方法fromJsonList:实现JSON数组到实体类列表的解析,直接为轮播图提供数据列表

2.5 封装轮播图API调用方法

为了解耦网络请求与页面逻辑,创建API封装层,专门管理所有接口的调用方法,页面层只需调用方法即可获取数据,无需关注底层请求细节。

创建文件lib/api/banner_api.dart,代码如下:

dart 复制代码
// 轮播图API封装层,统一管理轮播图相关接口调用
import 'package:harmonyos_day_four/constants/index.dart';
import 'package:harmonyos_day_four/models/banner_model.dart';
import 'package:harmonyos_day_four/utils/DioRequest.dart';

class BannerApi {
  // 获取轮播图数据列表
  static Future<List<BannerModel>> getBannerList() async {
    // 调用DioRequest的GET方法,请求轮播图接口
    final result = await dioRequest.get(HttpConstants.BANNER_LIST);
    // 将返回的JSON数组解析为BannerModel列表
    return BannerModel.fromJsonList(result);
  }
}

解耦优势:后续若接口地址、请求方式发生变化,只需修改此文件,所有调用处无需改动,降低维护成本。

2.6 在页面中调用API并更新UI

在之前实现的轮播图页面中,引入封装的API,在页面初始化时请求数据,并通过setState更新UI,实现数据的渲染。

lib/pages/home_page.dart为例,核心代码如下:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:harmonyos_day_four/api/banner_api.dart';
import 'package:harmonyos_day_four/models/banner_model.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // 轮播图数据列表
  List<BannerModel> _bannerList = [];
  // 加载状态标识
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    // 页面初始化时调用,获取轮播图数据
    _loadBannerData();
  }

  // 加载轮播图数据
  Future<void> _loadBannerData() async {
    try {
      // 调用API获取数据
      final bannerList = await BannerApi.getBannerList();
      // 更新状态,渲染UI
      setState(() {
        _bannerList = bannerList;
        _isLoading = false;
      });
    } catch (e) {
      // 捕获异常,更新加载状态并提示错误
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text("轮播图数据加载失败:$e")),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("首页轮播图")),
      body: _isLoading
          ? // 加载中展示加载圈
          const Center(child: CircularProgressIndicator())
          : // 数据加载完成后展示轮播图
          _bannerList.isEmpty
              ? const Center(child: Text("暂无轮播图数据"))
              : _buildBannerView(),
    );
  }

  // 构建轮播图视图(沿用之前实现的UI代码,替换数据源为_bannerList)
  Widget _buildBannerView() {
    return PageView.builder(
      itemCount: _bannerList.length,
      itemBuilder: (context, index) {
        final banner = _bannerList[index];
        return Image.network(
          banner.imgUrl,
          fit: BoxFit.cover,
        );
      },
    );
  }
}

核心要点

  1. initState中调用数据加载方法,保证页面初始化时立即请求数据
  2. 增加加载状态_isLoading,优化用户体验,加载中展示加载圈
  3. 增加异常捕获,数据加载失败时给出Toast提示,避免页面崩溃
  4. 轮播图UI的数据源替换为网络请求获取的_bannerList,实现数据驱动UI

2.7 轮播图支持网络图片

Flutter中通过Image.network组件加载网络图片,直接传入图片的网络地址即可,上述代码中已实现:

dart 复制代码
Image.network(
  banner.imgUrl, // 从数据模型中获取网络图片地址
  fit: BoxFit.cover, // 图片适配方式,铺满容器
  // 可添加加载中和加载失败的占位图
  loadingBuilder: (context, child, loadingProgress) {
    if (loadingProgress == null) return child;
    return const Center(child: CircularProgressIndicator());
  },
  errorBuilder: (context, error, stackTrace) {
    return const Center(child: Icon(Icons.image_not_supported));
  },
);

可通过loadingBuildererrorBuilder添加占位图,进一步优化网络图片的加载体验。

三、数据流程图

本次轮播图数据获取的整体流程遵循分层设计,数据流转清晰,各层职责明确,流程图如下:

复制代码
用户进入页面 → 页面初始化调用API → API层调用网络工具类 → Dio发送GET请求 → 接口返回JSON数据
→ 网络工具类处理业务状态码 → API层解析为实体类列表 → 页面层接收数据并更新UI → 轮播图渲染网络图片

各层职责:

  1. 页面层:负责UI展示、用户交互、调用API获取数据
  2. API层:封装接口调用方法,解耦页面与网络请求
  3. 网络工具层:封装Dio,实现通用的网络请求能力
  4. 模型层:实现JSON数据与实体类的解析,规范数据格式
  5. 常量层:统一管理配置参数,避免硬编码

四、项目工程结构

本次开发后,项目的核心工程结构如下(按功能分层),符合Flutter工程化开发规范,便于团队协作和后期扩展:

复制代码
lib/
├── api/            # API封装层:所有接口调用方法
│   └── banner_api.dart  # 轮播图接口封装
├── constants/      # 常量层:全局配置、接口地址
│   └── index.dart       # 全局常量定义
├── models/         # 模型层:数据实体类
│   └── banner_model.dart # 轮播图数据模型
├── pages/          # 页面层:所有业务页面
│   └── home_page.dart    # 首页(轮播图页面)
├── utils/          # 工具层:通用工具类
│   └── DioRequest.dart   # Dio网络请求工具
└── main.dart       # 项目入口文件

五、总结

本文完成了Flutter鸿蒙开发中轮播图网络数据获取与展示的完整实现,核心知识点和开发规范总结如下:

  1. 网络请求:使用Dio库实现HTTP请求,通过二次封装提升代码复用性和可维护性
  2. 工程化设计:采用分层开发思想,将页面、API、网络、模型、常量分层管理,解耦各模块逻辑
  3. 数据解析:创建实体类并添加工厂函数,规范JSON数据解析,避免空指针问题
  4. 异常处理:在网络请求、数据解析、页面调用等环节增加异常捕获,保证应用的稳定性
  5. 体验优化:增加加载状态、异常提示、网络图片占位图,提升用户体验
  6. 鸿蒙适配:本次开发的代码可直接在鸿蒙端运行,无需额外修改,符合Flutter跨平台特性

通过本次开发,不仅实现了轮播图的网络数据渲染,还掌握了Flutter工程化开发的核心思想,后续可将此套方案复用至其他业务模块的网络请求开发中。在实际项目中,还可在此基础上扩展请求缓存、token鉴权、多环境配置等功能,进一步提升项目的实用性。


✨ 坚持用 清晰的图解 +易懂的硬件架构 + 硬件解析, 让每个知识点都 简单明了 !

🚀 个人主页一只大侠的侠 · CSDN

💬 座右铭 : "所谓成功就是以自己的方式度过一生。"

相关推荐
冬奇Lab1 天前
一天一个开源项目(第35篇):GitHub Store - 跨平台的 GitHub Releases 应用商店
开源·github·资讯
Zsnoin能1 天前
Flutter仿ios液态玻璃效果
flutter
傅里叶2 天前
iOS相机权限获取
flutter·ios
Haha_bj2 天前
Flutter—— 本地存储(shared_preferences)
flutter
心之语歌2 天前
Flutter 存储权限:适配主流系统
flutter
Bigger2 天前
为什么你的 Git 提交需要签名?—— Git Commit Signing 完全指南
git·开源·github
恋猫de小郭2 天前
Android 官方正式官宣 AI 支持 AppFunctions ,Android 官方 MCP 和系统级 OpenClaw 雏形
android·前端·flutter
在人间耕耘2 天前
HarmonyOS Vision Kit 视觉AI实战:把官方 Demo 改造成一套能长期复用的组件库
人工智能·深度学习·harmonyos
MakeZero2 天前
Flutter那些事-布局篇
flutter
王码码20352 天前
Flutter for OpenHarmony:socket_io_client 实时通信的事实标准(Node.js 后端的最佳拍档) 深度解析与鸿蒙适配指南
android·flutter·ui·华为·node.js·harmonyos