一、环境配置准备

1.1、实现步骤

1. 安装 dio 库
2. 定义常量(基础地址、超时时间、业务状态、请求地址)
3. 封装网络请求工具(DioRequest)
4. 数据模型添加工厂函数
5. 封装 API 调用
6. 页面中调用 API 并更新 UI

1.2、接口信息

以下是整理后的表格:

项目
基础地址 https://meikou-api.itheima.net/
轮播图接口 /home/banner (GET)
完整地址 https://meikou-api.itheima.net/home/banner
返回数据格式:
{
  "code": "1",
  "message": "success",
  "result": [
    {"id": "1", "imgUrl": "https://xxx.com/1.jpg"},
    {"id": "2", "imgUrl": "https://xxx.com/2.jpg"}
  ]
}

二、代码实现

2.1 安装 dio 库

flutter pub add dio

打开项目根目录下的pubspec.yaml文件
在dependencies部分添加dio的最新版本依赖(当前最新稳定版为5.9.1):

dependencies:
  dio: ^5.9.1

2.2 创建常量类
在 lib/constants/index.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 特性:

    • 编译时确定的常量
    • 不可修改的固定值

优势:

  • 节省内存资源
  • 提高代码可维护性
    2.3 封装网络请求工具
    创建 lib/utils/DioRequest.dart:
// 基于Dio进行二次封装
import 'package:dio/dio.dart';
import 'package:harmonyos_day_four/constants/index.dart';

class DioRequest {
  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();
  }

  // 添加拦截器
  void _addInterceptor() {
    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: (request, handler) {
        // 请求拦截器
        handler.next(request);
      },
      onResponse: (response, handler) {
        // 响应拦截器:处理HTTP状态码
        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;
    }
  }

  // 进一步处理返回结果的函数:处理业务状态码
  Future<dynamic> _handleResponse(Response<dynamic> task) async {
    try {
      Response<dynamic> res = await task;
      final data = res.data as Map<String, dynamic>;
      // 判断业务状态码是否等于1
      if (data["code"] == GlobalConstants.SUCCESS_CODE) {
        return data["result"]; // 只返回result数据
      }
      throw Exception(data["message"] ?? "加载数据异常");
    } catch (e) {
      throw Exception(e);
    }
  }
}

// 单例对象
final dioRequest = DioRequest();

封装核心要点:

  1. 单例模式实现

    • 确保全局共享单一实例
  2. 拦截器机制

    • 统一处理HTTP状态码
    • 标准化业务状态码处理
  3. 错误处理方案

    • 采用统一异常抛出机制
    • 规范化错误处理流程
      2.4 为数据模型添加工厂函数
      修改文件路径:lib/viewmodels/home.dart
class BannerItem {
  String id;
  String imgUrl;

  BannerItem({required this.id, required this.imgUrl});

  // 工厂函数:从JSON创建对象
  factory BannerItem.formJSON(Map<String, dynamic> json) {
    return BannerItem(
      id: json["id"] ?? "",
      imgUrl: json["imgUrl"] ?? "",
    );
  }
}

工厂函数说明:

  • 使用 factory 关键字声明工厂构造函数
  • 可以不创建新实例,而是返回缓存中的现有实例
  • 常用于实现 JSON 反序列化功能
    2.5 封装 API 调用

在 lib/api 目录下创建 home.dart 文件,用于封装首页相关 API 接口。

// 封装轮播图API,返回业务侧需要的数据结构
import 'package:harmonyos_day_four/constants/index.dart';
import 'package:harmonyos_day_four/utils/DioRequest.dart';
import 'package:harmonyos_day_four/viewmodels/home.dart';

/// 获取轮播图列表数据
Future<List<BannerItem>> getBannerListAPI() async {
  // 发起请求并转换数据
  final result = ((await dioRequest.get(HttpConstants.BANNER_LIST)) as List)
      .map((item) {
    return BannerItem.formJSON(item as Map<String, dynamic>);
  }).toList();
  return result;
}

封装优势:

  • 业务层无需关注底层网络请求实现细节
  • 直接返回类型明确的业务数据结构
  • 提高代码可维护性和可测试性
    2.6 API 调用实现
    修改文件路径:lib/pages/home/index.dart
import 'package:flutter/cupertino.dart';
import 'package:harmonyos_day_four/api/home.dart';
import 'package:harmonyos_day_four/components/Home/HmSlider.dart';
// ...其他导入

class _HomeViewState extends State<HomeView> {
  // 轮播图数据(从API获取)
  List<BannerItem> _bannerList = [];

  
  void initState() {
    super.initState();
    _getBannerList();
  }

  /// 获取轮播图数据
  void _getBannerList() async {
    try {
      _bannerList = await getBannerListAPI();
      setState(() {});
    } catch (e) {
      print('获取轮播图数据失败: $e');
    }
  }

  // ...
}

2.7 轮播图支持网络图片功能
修改文件路径:lib/components/Home/HmSlider.dart

Widget _getSlider() {
  final double screenWidth = MediaQuery.of(context).size.width;

  // 如果没有数据,显示加载中
  if (widget.bannerList.isEmpty) {
    return SizedBox(
      width: screenWidth,
      height: 300,
      child: Container(
        color: Colors.grey[300],
        child: const Center(
          child: CircularProgressIndicator(),
        ),
      ),
    );
  }

  return SizedBox(
    width: screenWidth,
    height: 300,
    child: PageView.builder(
      controller: _pageController,
      onPageChanged: (int index) {
        setState(() {
          _currentIndex = index;
        });
      },
      itemCount: widget.bannerList.length,
      itemBuilder: (context, index) {
        return Image.network(
          widget.bannerList[index].imgUrl,
          fit: BoxFit.cover,
          width: screenWidth,
          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),
              ),
            );
          },
        );
      },
    ),
  );
}

三、数据流程图

┌─────────────┐
│ HomeView │
│ (initState)│
└──────┬──────┘


┌─────────────┐
│getBannerListAPI│
└──────┬──────┘


┌─────────────┐
│ dioRequest │
│ .get() │
└──────┬──────┘


┌─────────────┐
│ API Server │
│ /home/banner│
└──────┬──────┘


┌─────────────┐
│_handleResponse│
│(检查状态码) │
└──────┬──────┘


┌─────────────┐
│formJSON() │
│(工厂函数) │
└──────┬──────┘


┌─────────────┐
│ setState │
│ (更新UI) │
└─────────────┘

在这里插入图片描述

四、项目结构

lib/
├── api/
│ └── home.dart # API封装
├── constants/
│ └── index.dart # 全局常量
├── utils/
│ └── DioRequest.dart # 网络请求工具
├── viewmodels/
│ └── home.dart # 数据模型(含工厂函数)
├── components/Home/
│ └── HmSlider.dart # 轮播图组件
└── pages/home/
└── index.dart # 首页(调用API)

五、总结

本文完成了Flutter鸿蒙项目中的网络请求功能实现,核心内容包括:
以下是整理后的功能模块表格:

模块 功能描述
常量管理 包含GlobalConstantsHttpConstants,用于集中管理全局和HTTP相关常量
网络封装 通过DioRequest单例配合拦截器实现网络请求的统一管理和预处理
数据转换 使用factory工厂函数实现原始数据与模型对象的双向转换
API封装 对接口返回数据进行类型强校验,确保数据类型安全
UI集成 initState生命周期发起API请求,通过setState完成数据驱动UI更新

核心要点:

  1. 基于 dio 库实现网络请求功能
  2. 封装通用请求工具类,统一处理:
    • HTTP 状态码校验
    • 业务状态码解析
  3. 采用 factory 模式实现 JSON 数据到业务对象的转换
  4. API 层负责返回标准化数据结构
  5. 组件支持:
    • 网络图片加载
    • 异常状态处理

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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐