Flutter鸿蒙开发指南(十二):推荐列表数据获取

前言

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

功能概述

本次开发的是首页的推荐列表模块,用于展示商品推荐内容。

  • 接口地址GET /home/recommend

  • 返回数据类型 :商品详情项列表(List<GoodDetailItem>

  • 默认加载:10条数据

  • 展示形式:网格布局,展示商品图片、名称和价格

一、添加接口常量

添加请求地址接口的常量

Dart 复制代码
// 推荐列表
  static const String RECOMMEND_LIST = "/home/recommend";

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 String CATEGORY_LIST = "/home/category/head";

  //特惠推荐地址
  static const String PRODUCT_LIST = "/hot/preference";

  // 热榜推荐地址
  static const String IN_VOGUE_LIST = "/hot/inVogue";

  // 一站式推荐地址
  static const String ONE_STOP_LIST = "/hot/oneStop";

  // 推荐列表
  static const String RECOMMEND_LIST = "/home/recommend";
}

二、定义列表项数据类型

在 lib/viewmodels/home.dart 中新增 GoodDetailItem 类,继承自 GoodsItem,扩展 payCount 字段:

Dart 复制代码
class GoodDetailItem extends GoodsItem {
  int payCount = 0;

  /// 商品详情项
  GoodDetailItem({
    required super.id,
    required super.name,
    required super.price,
    required super.picture,
    required super.orderNum,
    required this.payCount,
  }) : super(desc: "");
  // 转化方法
  factory GoodDetailItem.formJSON(Map<String, dynamic> json) {
    return GoodDetailItem(
      id: json["id"]?.toString() ?? "",
      name: json["name"]?.toString() ?? "",
      price: json["price"]?.toString() ?? "",
      picture: json["picture"]?.toString() ?? "",
      orderNum: int.tryParse(json["orderNum"]?.toString() ?? "0") ?? 0,
      payCount: int.tryParse(json["payCount"]?.toString() ?? "0") ?? 0,
    );
  }
}

lib/viewmodels/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"] ?? "");
  }
}

// 商品项
class GoodsItem {
  String id;
  String name;
  String? desc;
  String price;
  String picture;
  int orderNum;

  GoodsItem({
    required this.id,
    required this.name,
    this.desc,
    required this.price,
    required this.picture,
    required this.orderNum,
  });

  factory GoodsItem.fromJSON(Map<String, dynamic> json) {
    return GoodsItem(
      id: json["id"] ?? "",
      name: json["name"] ?? "",
      desc: json["desc"],
      price: json["price"] ?? "",
      picture: json["picture"] ?? "",
      orderNum: json["orderNum"] ?? 0,
    );
  }
}

// 商品列表
class GoodsItems {
  int counts;
  int pageSize;
  int pages;
  int page;
  List<GoodsItem> items;

  GoodsItems({
    required this.counts,
    required this.pageSize,
    required this.pages,
    required this.page,
    required this.items,
  });

  factory GoodsItems.fromJSON(Map<String, dynamic> json) {
    return GoodsItems(
      counts: json["counts"] ?? 0,
      pageSize: json["pageSize"] ?? 0,
      pages: json["pages"] ?? 0,
      page: json["page"] ?? 0,
      items: (json["items"] as List?)
          ?.map((item) => GoodsItem.fromJSON(item as Map<String, dynamic>))
          .toList() ?? [],
    );
  }
}

// 子类型
class SubType {
  String id;
  String title;
  GoodsItems goodsItems;

  SubType({
    required this.id,
    required this.title,
    required this.goodsItems,
  });

  factory SubType.fromJSON(Map<String, dynamic> json) {
    return SubType(
      id: json["id"] ?? "",
      title: json["title"] ?? "",
      goodsItems: GoodsItems.fromJSON(json["goodsItems"] ?? {}),
    );
  }
}

// 特惠推荐结果
class SpecialOfferResult {
  String id;
  String title;
  List<SubType> subTypes;

  SpecialOfferResult({
    required this.id,
    required this.title,
    required this.subTypes,
  });

  factory SpecialOfferResult.fromJSON(Map<String, dynamic> json) {
    return SpecialOfferResult(
      id: json["id"] ?? "",
      title: json["title"] ?? "",
      subTypes: (json["subTypes"] as List?)
          ?.map((item) => SubType.fromJSON(item as Map<String, dynamic>))
          .toList() ?? [],
    );
  }
}



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

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

//根据json推断编写class对象和工厂转化函数
class CategoryItem {
  String id;
  String name;
  String picture;
  List<CategoryItem>? children;

  CategoryItem({
    required this.id,
    required this.name,
    required this.picture,
    this.children,
  });
// 扩展一个工厂函数 一般用factory来声明 一般用来创建实例对象
  factory CategoryItem.formJSON(Map<String, dynamic> json) {
    // 必须返回一个CategoryItem对象
    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(),
    ); // CategoryItem
  }
}
class GoodDetailItem extends GoodsItem {
  int payCount = 0;

  /// 商品详情项
  GoodDetailItem({
    required super.id,
    required super.name,
    required super.price,
    required super.picture,
    required super.orderNum,
    required this.payCount,
  }) : super(desc: "");
  // 转化方法
  factory GoodDetailItem.formJSON(Map<String, dynamic> json) {
    return GoodDetailItem(
      id: json["id"]?.toString() ?? "",
      name: json["name"]?.toString() ?? "",
      price: json["price"]?.toString() ?? "",
      picture: json["picture"]?.toString() ?? "",
      orderNum: int.tryParse(json["orderNum"]?.toString() ?? "0") ?? 0,
      payCount: int.tryParse(json["payCount"]?.toString() ?? "0") ?? 0,
    );
  }
}

三、封装API请求

在 lib/api/home.dart 中添加推荐列表的请求方法,支持传入参数:

Dart 复制代码
// 推荐列表
Future<List<GoodDetailItem>> getRecommendListAPI(
    Map<String, dynamic> params,
    ) async {
  // 返回请求
  return ((await dioRequest.get(HttpConstants.RECOMMEND_LIST, params: params))
  as List)
      .map((item) {
    return GoodDetailItem.formJSON(item as Map<String, dynamic>);
  })
      .toList();
}

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 {
  // 返回请求
  return (await dioRequest.get(HttpConstants.BANNER_LIST) as List).map((
    item,
  ) {
    return BannerItem.formJSON(item as Map<String, dynamic>);
  }).toList();
}

//分类列表接口
Future<List<CategoryItem>> getCategoryListAPI() async {
  // 返回请求
  return (await dioRequest.get(HttpConstants.CATEGORY_LIST) as List).map((
    item,
  ) {
    return CategoryItem.formJSON(item as Map<String, dynamic>);
  }).toList();
}

//特惠推荐地址
Future<SpecialOfferResult> getProductListAPI() async {
  // 返回请求
  return SpecialOfferResult.fromJSON(
      await dioRequest.get(HttpConstants.PRODUCT_LIST));
}

// 热榜推荐
Future<SpecialOfferResult> getInVogueListAPI() async {
  // 返回请求
  return SpecialOfferResult.fromJSON(
    await dioRequest.get(HttpConstants.IN_VOGUE_LIST),
  );
}

// 一站式推荐
Future<SpecialOfferResult> getOneStopListAPI() async {
  // 返回请求
  return SpecialOfferResult.fromJSON(
    await dioRequest.get(HttpConstants.ONE_STOP_LIST),
  );
}
// 推荐列表
Future<List<GoodDetailItem>> getRecommendListAPI(
    Map<String, dynamic> params,
    ) async {
  // 返回请求
  return ((await dioRequest.get(HttpConstants.RECOMMEND_LIST, params: params))
  as List)
      .map((item) {
    return GoodDetailItem.formJSON(item as Map<String, dynamic>);
  })
      .toList();
}

四、页面数据初始化

在首页视图lib/pages/Home/index.dart中添加状态变量

Dart 复制代码
HmMoreList(recommendList: _recommendList), // 无限滚动列表

添加获取数据方法

Dart 复制代码
  // 获取推荐列表
  void _getRecommendList() async {
    _recommendList = await getRecommendListAPI({"limit": 10});
    setState(() {});
  }

在initState中调用

Dart 复制代码
@override
void initState() {
  super.initState();
  _getBannederList();
  _getCategoryList();
  _getProductList();
  _getInVogueList();
  _getOneStopList();
  _getRecommendList();  // 新增
}

报错是因为还没实现对应的结构,接下来我们实现对应的结构。

五、实现无限滚动列表组件

修改lib/components/Home/HmMoreList.dart代码:

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmMoreList extends StatefulWidget {
  final List<GoodDetailItem> recommendList;

  const HmMoreList({super.key, required this.recommendList});

  @override
  State<HmMoreList> createState() => _HmMoreListState();
}

class _HmMoreListState extends State<HmMoreList> {
  @override
  Widget build(BuildContext context) {
    //必须是Sliver家族的组件
    return SliverGrid.builder(
      gridDelegate:
          //网格是两列
          SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        mainAxisSpacing: 10,
        crossAxisSpacing: 10,
        childAspectRatio: 0.75,
      ),
      itemCount: widget.recommendList.length,
      itemBuilder: (BuildContext context, int index) {
        final item = widget.recommendList[index];
        return Container(
          padding: EdgeInsets.all(8),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(8),
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(8),
                  child: Image.network(
                    item.picture,
                    width: double.infinity,
                    fit: BoxFit.cover,
                    errorBuilder: (context, error, stackTrace) {
                      return Image.asset(
                        "lib/assets/goods_avatar.png",
                        width: double.infinity,
                        fit: BoxFit.cover,
                      );
                    },
                  ),
                ),
              ),
              SizedBox(height: 8),
              Text(
                item.name,
                style: TextStyle(fontSize: 14),
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
              ),
              SizedBox(height: 4),
              Text(
                "¥${item.price}",
                style: TextStyle(
                  fontSize: 16,
                  color: Colors.red,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

六、运行效果

推荐列表以网格形式展示在首页底部,默认加载10条商品数据,包含:

  • 商品图片(支持加载失败占位图)

  • 商品名称(最多显示两行)

  • 商品价格(红色加粗显示)

运行效果如下:

目前,最多只能加载出来10条数据。因为我们写死了只能加载10条,所以我们在下一篇文章加入上拉加载,让他能继续加载更多数据。

最后别忘记了提交到AtomGit代码托管平台

项目地址:https://AtomGit.com/Deng666/shangcheng.git

相关推荐
嘴贱欠吻!2 小时前
Flutter鸿蒙开发指南(十三):推荐列表上拉加载
flutter
一叶星殇2 小时前
Windows 下用 Nginx 部署 Vue + .NET WebApi 全流程实战
vue.js·windows·nginx
键盘鼓手苏苏3 小时前
Flutter for OpenHarmony:debounce_throttle 防抖与节流的艺术(优化用户交互与网络请求) 深度解析与鸿蒙适配指南
网络·flutter·交互
天開神秀3 小时前
解决 n8n 在 Windows 上安装社区节点时 `spawn npm ENOENT/EINVAL` 错误
前端·windows·npm
iAkuya3 小时前
(leetcode)力扣100 71字符串解码(栈(两种)||递归)
windows·算法·leetcode
jimy13 小时前
从Windows terminal里面的输出内容中截取trim IP 地址,再更新到.ssh/config文件里面
windows·tcp/ip·ssh
阿林来了3 小时前
Flutter三方库适配OpenHarmony【flutter_speech】— 语音识别监听器实现
人工智能·flutter·语音识别·harmonyos
松叶似针3 小时前
Flutter三方库适配OpenHarmony【secure_application】— setWindowPrivacyMode 隐私模式实现
flutter·harmonyos
哈__4 小时前
基础入门 Flutter for OpenHarmony:image_cropper 图片裁剪详解
flutter