Flutter鸿蒙开发指南(八):获取轮播图数据

一、前置准备

1.1 步骤

(1)安装 dio
(2)定义常量数据、基础地址、超时时间、业务状态、请求地址
(3)封装网络请求工具,基础地址,拦截器
(4)请求工具进一步解构,处理 http 状态和业务状态
(5)类工厂转化动态类型到对象类型
(6)封装请求 API 调用工厂函数
(7)初始化数据更新状态

1.2 接口请求成功示例

接口基础地址:https://meikou-api.itheima.net

轮播图地址(get):https://meikou-api.itheima.net/home/banner

请求轮播图成功返回的数据:

1.3 dio使用方法

Dart 复制代码
flutter pub add dio

二、轮播图API请求实现

2.1 编写常量类

首先先修改文件夹的名字,因为我之前写错了,这个文件夹用于放置常量。

lib/constants/index.dart代码

为什么要用static修饰?因为:

(1)非静态每个实例都会创建新的内存。

(2)可以全局访问,无需创建对象。

(3)这些配置在应用生命周期内不会改变。

Dart 复制代码
//全局状态
class GlobalConstants {
  //基础地址
  static String BASE_URL = "https://meikou-api.itheima.net/";

  //超时时间
  static int TIME_OUT = 10;

  //成功状态
  static String SUCCESS_CODE = "1";
}

但是以上代码叫作变量,而我们是不需要改变的,所以加上"const"关键字,变为常量

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";
}

完整代码:

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";
}

2.2 封装请求类

在lib/utils文件夹下新建一个DioRequest.dart进行Dio网络请求的封装。

DioRequest.dart代码

Dart 复制代码
//基于Dio进行二次封装
import 'package:dio/dio.dart';
import 'package:qing_mall/constants/index.dart';

class DioRequest {
  final _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状态码 200 300
      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;
    }
  }

  //进一步处理返回结果的函数
  Future<dynamic> _handleResponse(Response<dynamic> task) async {
    try {
      Response<dynamic> res = await task;
      final data = res.data as Map<String, dynamic>; //data才是我们真实的接口返回的数据
      if (data["code"] == GlobalConstants.SUCCESS_CODE) {
        //如果等于1 才认定http状态和业务状态均为正常 就可以正常的放行通过
        return data["result"]; //只要result结果
      }
      //抛出异常
      throw Exception(data["message"] ?? "加载数据异常");
    } catch (e) {
      throw Exception(e);
    }
  }
}

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

//dio请求工具发出请求->返回的数据通过Response<dynamic>.data
//把所有的接口的data解放出来 拿到真正的数据 要判断业务状态码是不是等于1

2.3 转换动态类型

修改lib/viewmodels/home.dart代码

home.dart代码

Dart 复制代码
class BannerItem {
  String id;
  String imgUrl;

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

  //扩展一个工厂函数 一般用factory来声明 一般用来创建实例对象
  factory BannerItem.formJSON(Map<String, dynamic> json) {
    //必须返回一个BannerItem对象
    return BannerItem(id: json["id"] ?? "", imgUrl: json["imgUrl"] ?? "");
  }
}

//每一个轮播图具体类型

//flutter必须强制转换,没有隐式转化

2.4 封装API

在lib/api/目录下新建home.dart,目的是返回业务所需要的数据结构。

完整代码:

Dart 复制代码
//封装一个api 目的是返回业务侧要的数据结构
import 'package:qing_mall/constants/index.dart';
import 'package:qing_mall/utils/DioRequest.dart';
import 'package:qing_mall/viewmodels/home.dart';

Future<List<BannerItem>> getBannerListAPI() async {
  //返回请求
  final tt =
      ((await dioRequest.get(HttpConstants.BANNER_LIST)) as List).map((item) {
    return BannerItem.formJSON(item as Map<String, dynamic>);
  }).toList();
  return tt;
}

修改lib/pages/Home/index.dart代码

完整代码:

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:qing_mall/api/home.dart';
import 'package:qing_mall/components/Home/HmCategory.dart';
import 'package:qing_mall/components/Home/HmHot.dart';
import 'package:qing_mall/components/Home/HmMoreList.dart';
import 'package:qing_mall/components/Home/HmSlider.dart';
import 'package:qing_mall/components/Home/HmSuggestion.dart';
import 'package:qing_mall/viewmodels/home.dart';

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

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {
  List<BannerItem> _bannerList = [
    // BannerItem(
    //   id: "1",
    //   imgUrl: "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/1.jpg",
    // ),
    // BannerItem(
    //   id: "2",
    //   imgUrl: "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/2.png",
    // ),
    // BannerItem(
    //   id: "3",
    //   imgUrl: "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/3.jpg",
    // ),
  ];

  //获取滚动容器的内容

  List<Widget> _getScrollChildren() {
    return [
      //包裹普通widget的sliver家族的组件内容
      SliverToBoxAdapter(child: HmSlider(bannerList: _bannerList)), //轮播图组件
      //放置分类组件
      SliverToBoxAdapter(child: SizedBox(height: 10)),
      //SliverGrid SliverList指南纵向排列
      SliverToBoxAdapter(child: HmCategory()), //分类组件
      SliverToBoxAdapter(child: SizedBox(height: 10)),
      SliverToBoxAdapter(child: HmSuggestion()), //推荐组件
      SliverToBoxAdapter(child: SizedBox(height: 10)),

      //Flex和Expanded配合起来可以均分比例
      SliverToBoxAdapter(
        child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 10),
            child: Flex(
              direction: Axis.horizontal,
              children: [
                Expanded(child: HmHot()),
                SizedBox(
                  width: 10,
                ),
                Expanded(child: HmHot()),
              ],
            )),
      ),
      SliverToBoxAdapter(child: SizedBox(height: 10)),
      HmMorelist(), //无限滚动列表
    ];
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _getBannederList();
  }

  void _getBannederList() async {
    _bannerList = await getBannerListAPI();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    //CustomScrollview要求:必须是sliver家族的内容
    return CustomScrollView(slivers: _getScrollChildren());
  }
}

以上步骤全部完成后,运行鸿蒙的模拟器,会得到如图所示效果,请求了真实的API接口

三、提交代码

3.1 git可视化提交代码

最后,别忘了提交代码哦。这次我们改用Android Studio的可视化进行git的提交。

选中该图标,如果没有,可能是已经没有装git的插件。

3.2 总结

感谢大家的观看,本项目的地址是:https://AtomGit.com/Deng666/shangcheng.git。如果在文章中复刻遇到问题,可到代码托管仓库克隆一份源码。如果本篇文章对你有帮助,请你点个赞吧!~

最后,欢迎加入开源鸿蒙跨平台社区:

https://openharmonycrossplatform.csdn.net

相关推荐
程序员Ctrl喵1 天前
异步编程:Event Loop 与 Isolate 的深层博弈
开发语言·flutter
前端不太难1 天前
Flutter 如何设计可长期维护的模块边界?
flutter
小蜜蜂嗡嗡1 天前
flutter列表中实现置顶动画
flutter
始持1 天前
第十二讲 风格与主题统一
前端·flutter
始持1 天前
第十一讲 界面导航与路由管理
flutter·vibecoding
始持1 天前
第十三讲 异步操作与异步构建
前端·flutter
新镜1 天前
【Flutter】 视频视频源横向、竖向问题
flutter
黄林晴1 天前
Compose Multiplatform 1.10 发布:统一 Preview、Navigation 3、Hot Reload 三箭齐发
android·flutter
Swift社区1 天前
Flutter 应该按功能拆,还是按技术层拆?
flutter
肠胃炎1 天前
树形选择器组件封装
前端·flutter