前言

在上一章节中,我们借助TraeAI快速开发了我们的项目。本章节我们继续使用TraeAI完成我们的项目开发吧!下面说一下本章节获取特惠推荐数据的步骤。

步骤:

(1)请求地址常量(AI)

(2)根据JSON转换对象模型(AI)

(3)封装API请求对象(AI)

(4)初始化获取数据(AI)

(5)传递数据到子组件(AI)

(6)拷贝图片资源/取三条子选项进行渲染(手动实现)

一、特惠数据模型

1.1 添加特惠推荐接口常量

特惠推荐的JSON数据

{
    "code": "1",
    "msg": "操作成功",
    "result": {
        "id": "897682543",
        "title": "特惠推荐",
        "subTypes": [
            {
                "id": "912000341",
                "title": "抢先尝鲜",
                "goodsItems": {
                    "counts": 459,
                    "pageSize": 10,
                    "pages": 46,
                    "page": 1,
                    "items": [
                        {
                            "id": "1750713979950333956",
                            "name": "Balva 日本制高级时尚太阳镜 方框",
                            "desc": "抵挡99%紫外线太阳镜",
                            "price": "1213.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/235220/p5.png",
                            "orderNum": 17
                        },
                        {
                            "id": "1750713979895808015",
                            "name": "COGIT UV休闲百搭牛仔帽 深蓝色",
                            "desc": "不压发型 清凉舒适",
                            "price": "116.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/250541/p3.png",
                            "orderNum": 18
                        },
                        {
                            "id": "1750713979900002316",
                            "name": "CA4LA AKA SIX CHECK TRAPPER HAT ORANGE ONESIZE 货号:MOR00097",
                            "desc": "暖色格调飞行帽",
                            "price": "1809.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/254345/p1.png",
                            "orderNum": 10
                        },
                        {
                            "id": "1750713979652538372",
                            "name": "ROOTOTE DELI 个性时尚大容量手提包 黑色豹纹",
                            "desc": "大容量实用手提包",
                            "price": "298.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/250011/p5.png",
                            "orderNum": 13
                        },
                        {
                            "id": "1750713979929362433",
                            "name": "手袋动物园 防寒保暖可爱动物手套 咖色小熊",
                            "desc": "两用保暖动物小手套",
                            "price": "198.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/238258/p1.png",
                            "orderNum": 13
                        },
                        {
                            "id": "1750713979203747840",
                            "name": "CU2 舒适减负背包 紫色",
                            "desc": "舒适轻松实现大容量出行",
                            "price": "2419.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/255004/p1.png",
                            "orderNum": 25
                        },
                        {
                            "id": "1750713979900002312",
                            "name": "kaorinomori 毛边针织条带帽Bebe黑色 56cm~58cm(97)",
                            "desc": "秋冬必备萌系帽",
                            "price": "537.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/256104/p9.png",
                            "orderNum": 9
                        },
                        {
                            "id": "1750713979900002317",
                            "name": "CA4LA CHARI3 CHARCOAL GRAY ONESIZE 货号:TAM02605",
                            "desc": "温暖实用,型格百搭",
                            "price": "787.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/254354/p1.png",
                            "orderNum": 8
                        },
                        {
                            "id": "1750713979925168129",
                            "name": "KNITTING INN 羊毛毛毡格纹围巾 赤褐色",
                            "desc": "厚实柔软有弹性",
                            "price": "1019.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/42590/p1.png",
                            "orderNum": 15
                        },
                        {
                            "id": "1379052170040578049",
                            "name": "极光限定 珠光蓝珐琅锅",
                            "desc": null,
                            "price": "199.00",
                            "picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-05/7f483771-6831-4a7a-abdb-2625acb755f3.png",
                            "orderNum": 929
                        }
                    ]
                }
            },
            {
                "id": "912000342",
                "title": "新品预告",
                "goodsItems": {
                    "counts": 459,
                    "pageSize": 10,
                    "pages": 46,
                    "page": 1,
                    "items": [
                        {
                            "id": "1750713979900002312",
                            "name": "kaorinomori 毛边针织条带帽Bebe黑色 56cm~58cm(97)",
                            "desc": "秋冬必备萌系帽",
                            "price": "537.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/256104/p9.png",
                            "orderNum": 9
                        },
                        {
                            "id": "1750713979929362433",
                            "name": "手袋动物园 防寒保暖可爱动物手套 咖色小熊",
                            "desc": "两用保暖动物小手套",
                            "price": "198.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/238258/p1.png",
                            "orderNum": 13
                        },
                        {
                            "id": "1750713979950333956",
                            "name": "Balva 日本制高级时尚太阳镜 方框",
                            "desc": "抵挡99%紫外线太阳镜",
                            "price": "1213.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/235220/p5.png",
                            "orderNum": 17
                        },
                        {
                            "id": "1750713979900002316",
                            "name": "CA4LA AKA SIX CHECK TRAPPER HAT ORANGE ONESIZE 货号:MOR00097",
                            "desc": "暖色格调飞行帽",
                            "price": "1809.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/254345/p1.png",
                            "orderNum": 10
                        },
                        {
                            "id": "1750713979925168129",
                            "name": "KNITTING INN 羊毛毛毡格纹围巾 赤褐色",
                            "desc": "厚实柔软有弹性",
                            "price": "1019.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/42590/p1.png",
                            "orderNum": 15
                        },
                        {
                            "id": "1379052170040578049",
                            "name": "极光限定 珠光蓝珐琅锅",
                            "desc": null,
                            "price": "199.00",
                            "picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-05/7f483771-6831-4a7a-abdb-2625acb755f3.png",
                            "orderNum": 929
                        },
                        {
                            "id": "1750713979895808015",
                            "name": "COGIT UV休闲百搭牛仔帽 深蓝色",
                            "desc": "不压发型 清凉舒适",
                            "price": "116.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/250541/p3.png",
                            "orderNum": 18
                        },
                        {
                            "id": "1750713979652538372",
                            "name": "ROOTOTE DELI 个性时尚大容量手提包 黑色豹纹",
                            "desc": "大容量实用手提包",
                            "price": "298.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/250011/p5.png",
                            "orderNum": 13
                        },
                        {
                            "id": "1750713979900002317",
                            "name": "CA4LA CHARI3 CHARCOAL GRAY ONESIZE 货号:TAM02605",
                            "desc": "温暖实用,型格百搭",
                            "price": "787.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/254354/p1.png",
                            "orderNum": 8
                        },
                        {
                            "id": "1750713979203747840",
                            "name": "CU2 舒适减负背包 紫色",
                            "desc": "舒适轻松实现大容量出行",
                            "price": "2419.00",
                            "picture": "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/255004/p1.png",
                            "orderNum": 25
                        }
                    ]
                }
            }
        ]
    }
}

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

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

1.2 使用Trae AI生成数据模型

将home.dart文件和特惠推荐.json拖入到输入框,给出指令:"根据特惠推荐.json中的数据在home.dart中生成对应的类型文件 包含工厂转化函数"

AI生成了以下代码:

// 商品项
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() ?? [],
    );
  }
}

// 特惠推荐响应
class SpecialOfferResponse {
  String code;
  String msg;
  SpecialOfferResult result;

  SpecialOfferResponse({
    required this.code,
    required this.msg,
    required this.result,
  });

  factory SpecialOfferResponse.fromJSON(Map<String, dynamic> json) {
    return SpecialOfferResponse(
      code: json["code"] ?? "",
      msg: json["msg"] ?? "",
      result: SpecialOfferResult.fromJSON(json["result"] ?? {}),
    );
  }
}

1.3 构建商品类与特惠推荐类

但是我们在轮播图的章节,是没有要code、msg、result这个结构的,我们是在dio请求工具里进一步解构,需要的是result对象

以下代码生成的code、msg、result我们是不需要的,我们用不上,直接删除下列代码。


// 特惠推荐响应
class SpecialOfferResponse {
  String code;
  String msg;
  SpecialOfferResult result;

  SpecialOfferResponse({
    required this.code,
    required this.msg,
    required this.result,
  });

  factory SpecialOfferResponse.fromJSON(Map<String, dynamic> json) {
    return SpecialOfferResponse(
      code: json["code"] ?? "",
      msg: json["msg"] ?? "",
      result: SpecialOfferResult.fromJSON(json["result"] ?? {}),
    );
  }
}

然后编写lib/api/home.dart代码,在最下方注释特惠推荐地址,就会自动输出以下代码

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

在_getCategoryList()下回车,AI会自动补全代码。然后再到HmSuggetsion中的

  const HmSuggestion({super.key, required this.specialOfferResult});

此处上方,AI会给出代码。

二、API接口与数据获取

2.1 首页组件数据初始化

lib/pages/Home/index.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<CategoryItem> _categoryList = [];

  //轮播图列表
  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(categoryList: _categoryList)), //分类组件
      SliverToBoxAdapter(child: SizedBox(height: 10)),
      SliverToBoxAdapter(child: HmSuggestion(specialOfferResult: _specialOfferResult)), //推荐组件
      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(), //无限滚动列表
    ];
  }

  //特惠推荐
  SpecialOfferResult _specialOfferResult = SpecialOfferResult(
    id: "",
    title: "",
    subTypes: [],
  );

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

  //获取特惠推荐
  void _getProductList() async {
    _specialOfferResult = await getProductListAPI();
    setState(() {});
  }

  //获取分类列表
  void _getCategoryList() async {
    _categoryList = await getCategoryListAPI();
    setState(() {});
  }

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

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

lib/components/Home/HmSuggestion.dart代码

import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmSuggestion extends StatefulWidget {
  //父传子
  final SpecialOfferResult specialOfferResult;
  
  const HmSuggestion({super.key, required this.specialOfferResult});

  @override
  State<HmSuggestion> createState() => _HmSuggestionState();
}

class _HmSuggestionState extends State<HmSuggestion> {
  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: EdgeInsets.symmetric(horizontal: 10),
        child: Container(
            color: Colors.blue,
            alignment: Alignment.center,
            height: 300,
            child: Text(
              "推荐",
              style: TextStyle(color: Colors.white),
            )));
  }
}

2.2 特惠推荐数据获取与状态管理

复制以下图片到assets目录,可以在我的代码仓库找:https://gitcode.com/Deng666/shangcheng.git

编写lib/components/Home/HmSuggestion.dart代码

import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmSuggestion extends StatefulWidget {
  //父传子
  final SpecialOfferResult specialOfferResult;

  const HmSuggestion({super.key, required this.specialOfferResult});

  @override
  State<HmSuggestion> createState() => _HmSuggestionState();
}

class _HmSuggestionState extends State<HmSuggestion> {
  //完成渲染

  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: EdgeInsets.symmetric(horizontal: 10),
        child: Container(
            alignment: Alignment.center,
            height: 300,
            decoration: BoxDecoration(
                color: Colors.blue,
                borderRadius: BorderRadius.circular(12),
                image: DecorationImage(
                  image: AssetImage("lib/assets/home_cmd_sm.png"),
                  fit: BoxFit.cover,
                )),
            child: Text(
              "推荐",
              style: TextStyle(color: Colors.white),
            )));
  }
}

2.3 完善特惠推荐相关UI

现在我们需要完成特惠推荐这里的相关UI

HmSuggestion.dart代码

import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmSuggestion extends StatefulWidget {
  //父传子
  final SpecialOfferResult specialOfferResult;

  const HmSuggestion({super.key, required this.specialOfferResult});

  @override
  State<HmSuggestion> createState() => _HmSuggestionState();
}

class _HmSuggestionState extends State<HmSuggestion> {
  Widget _buildHeader() {
    return Row(
      children: [
        Text("特惠推荐",
            style: TextStyle(
                color: const Color.fromARGB(255, 86, 24, 20),
                fontSize: 18,
                fontWeight: FontWeight.w700)),
        SizedBox(width: 10),
        Text(
          "精选省攻略",
          style: TextStyle(
            fontSize: 12,
            color: const Color.fromARGB(255, 124, 63, 58),
          ),
        ),
      ],
    );
  }

  //左侧结构
  Widget _buildLeft() {
    return Container(
      width: 100,
      height: 140,
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10),
          image: DecorationImage(
              image: AssetImage("lib/assets/home_cmd_inner.png"), fit: BoxFit.cover)),
    );
  }

  //完成渲染
  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: EdgeInsets.symmetric(horizontal: 10),
        child: Container(
            alignment: Alignment.center,
            height: 300,
            padding: EdgeInsets.all(12),
            decoration: BoxDecoration(
                color: Colors.blue,
                borderRadius: BorderRadius.circular(12),
                image: DecorationImage(
                  image: AssetImage("lib/assets/home_cmd_sm.png"),
                  fit: BoxFit.cover,
                )),
            child: Column(
              children: [
                //顶部内容
                _buildHeader(),
                Row(
                  children: [_buildLeft()],
                )
              ],
            )));
  }
}

继续修改HmSuggestion.dart代码

import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmSuggestion extends StatefulWidget {
  //父传子
  final SpecialOfferResult specialOfferResult;

  const HmSuggestion({super.key, required this.specialOfferResult});

  @override
  State<HmSuggestion> createState() => _HmSuggestionState();
}

class _HmSuggestionState extends State<HmSuggestion> {

//取前三条数据
List<GoodsItem> _getDisplayItems(){
  return widget.specialOfferResult.subTypes.first.goodsItems.items.take(3).toList();
}


  Widget _buildHeader() {
    return Row(
      children: [
        Text("特惠推荐",
            style: TextStyle(
                color: const Color.fromARGB(255, 86, 24, 20),
                fontSize: 18,
                fontWeight: FontWeight.w700)),
        SizedBox(width: 10),
        Text(
          "精选省攻略",
          style: TextStyle(
            fontSize: 12,
            color: const Color.fromARGB(255, 124, 63, 58),
          ),
        ),
      ],
    );
  }

  //左侧结构
  Widget _buildLeft() {
    return Container(
      width: 100,
      height: 140,
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10),
          image: DecorationImage(
              image: AssetImage("lib/assets/home_cmd_inner.png"), fit: BoxFit.cover)),
    );
  }

  List<Widget> _getChildrenList(){
    List<GoodsItem> list = _getDisplayItems();//取到前3条数据
    return List.generate(list.length, (int index){
      return Column(
        children: [
          //裁剪圆角 ClipRect 可以包裹子元素 裁剪图片设置圆角
          ClipRRect(
            borderRadius: BorderRadius.circular(8),
            child: Image.network(list[index].picture,width: 100,height: 140,fit: BoxFit.cover),
            ),
            SizedBox(height: 10),
            Container(
               padding: EdgeInsets.symmetric(horizontal: 10,vertical: 4),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(12),
                color: Color.fromARGB(255, 240, 96, 12),
              ),
              child: Text("¥${list[index].price}",style: TextStyle(color: Colors.white),),
            )
        ],
      
      );
    });
  }

  //完成渲染
  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: EdgeInsets.symmetric(horizontal: 10),
        child: Container(
            alignment: Alignment.center,
            padding: EdgeInsets.all(12),
            decoration: BoxDecoration(
                color: Colors.blue,
                borderRadius: BorderRadius.circular(12),
                image: DecorationImage(
                  image: AssetImage("lib/assets/home_cmd_sm.png"),
                  fit: BoxFit.cover,
                )),
            child: Column(
              children: [
                //顶部内容
                _buildHeader(),
                SizedBox(height: 10),
                Row(
                  children: [_buildLeft(),Expanded(child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: _getChildrenList()))],
                )
              ],
            )));
  }
}

运行,然后模拟器会有报错

2.4 图片加载与错误处理优化

这个时候让AI解决,但是解决过程有点漫长,不过还在最终解决了,这里我就不给详细的过程了。大概就是复述自己的问题,让AI解决,尽量保持原有的UI结构。

HmSuggestion.dart代码

import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmSuggestion extends StatefulWidget {
  //父传子
  final SpecialOfferResult specialOfferResult;

  const HmSuggestion({super.key, required this.specialOfferResult});

  @override
  State<HmSuggestion> createState() => _HmSuggestionState();
}

class _HmSuggestionState extends State<HmSuggestion> {
//取前三条数据
  List<GoodsItem> _getDisplayItems() {
    //在初始化还没获取到数据直接返回空列表
    if (widget.specialOfferResult.subTypes.isEmpty) {
      return [];
    }
   
    return widget.specialOfferResult.subTypes.first.goodsItems.items
        .take(3)
        .toList();
  }

  Widget _buildHeader() {
    return Row(
      children: [
        Text("特惠推荐",
            style: TextStyle(
                color: const Color.fromARGB(255, 86, 24, 20),
                fontSize: 18,
                fontWeight: FontWeight.w700)),
        SizedBox(width: 10),
        Text(
          "精选省攻略",
          style: TextStyle(
            fontSize: 12,
            color: const Color.fromARGB(255, 124, 63, 58),
          ),
        ),
      ],
    );
  }

  //左侧结构
  Widget _buildLeft() {
    return Container(
      width: 75,
      height: 140,
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10),
          image: DecorationImage(
              image: AssetImage("lib/assets/home_cmd_inner.png"),
              fit: BoxFit.cover)),
    );
  }

  List<Widget> _getChildrenList() {
    List<GoodsItem> list = _getDisplayItems(); //取到前3条数据
    return List.generate(list.length, (int index) {
      return Expanded(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 4.0),
          child: Column(
            children: [
              //裁剪圆角 ClipRect 可以包裹子元素 裁剪图片设置圆角
              ClipRRect(
                borderRadius: BorderRadius.circular(8),
                child: Image.network(
                  list[index].picture,
                  width: double.infinity,
                  height: 140,
                  fit: BoxFit.cover,
                  errorBuilder: (context, error, stackTrace) {
                    // 返回一个新的部件体替换原有图片
                    return Image.asset(
                      "lib/assets/home_cmd_inner.png",
                      width: double.infinity,
                      height: 140,
                      fit: BoxFit.cover,
                    );
                  },
                ),
              ),

              SizedBox(height: 10),
              Container(
                padding: EdgeInsets.symmetric(horizontal: 4, vertical: 4),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(12),
                  color: Color.fromARGB(255, 240, 96, 12),
                ),
                child: Text(
                  "¥${list[index].price}",
                  style: TextStyle(color: Colors.white, fontSize: 11),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              )
            ],
          ),
        ),
      );
    });
  }

  //完成渲染
  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: EdgeInsets.symmetric(horizontal: 10),
        child: Container(
            alignment: Alignment.center,
            padding: EdgeInsets.all(12),
            decoration: BoxDecoration(
                color: Colors.blue,
                borderRadius: BorderRadius.circular(12),
                image: DecorationImage(
                  image: AssetImage("lib/assets/home_cmd_sm.png"),
                  fit: BoxFit.cover,
                )),
            child: Column(
              children: [
                //顶部内容
                _buildHeader(),
                SizedBox(height: 10),
                Row(
                  children: [
                    _buildLeft(),
                    SizedBox(width: 4),
                    Expanded(
                        child: Row(
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                            children: _getChildrenList()))
                  ],
                )
              ],
            )));
  }
}

viewmodels/home.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
  }
}

运行鸿蒙模拟器,效果如图

别忘了提交代码哦~,使用Android Studio进行Git可视化操作

如果在文章中遇到问题,可拉下来源码进行查阅:https://AtomGit.com/Deng666/shangcheng.git

三、总结

本章通过Trae AI辅助编程,完整实现了特惠推荐功能的开发。主要完成了接口常量添加、数据模型构建、API接口封装、数据获取与UI展示的全流程。使用工厂模式转换JSON数据,实现了商品列表的动态渲染,并优化了图片加载错误处理。至此,首页特惠推荐模块开发完成,应用功能进一步完善。

最后,感谢大家的观看,如果本文章对你有帮助,请点个赞吧!~如果有疑问,可以在评论区留言,看到会回复。

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

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐