前言

本文将详细介绍如何从零开发Flutter电商应用的商品详情页。我们将参考开源鸿蒙的水果详情页设计,并针对电商场景进行优化实现。

一、项目背景

本文基于一个采用Flutter 3.x框架开发的跨平台电商应用,主要技术架构如下:
核心框架:Flutter 3.x版本
应用结构:采用底部导航栏设计,包含四个核心页面:

首页
商品分类页
购物车页面
个人中心页

已实现功能模块:

轮播图展示
商品分类列表
特惠商品推荐板块

二、开发流程

需求梳理 → 数据建模 → 接口开发 → 页面实现 → 联调测试 → 问题优化
2.1、需求设计
根据CSDN文章中的水果详情页面设计,我们需要实现:
模块功能表格

模块 功能说明
顶部导航栏 返回按钮、标题、分享按钮
商品基本信息 图片、名称、英文名、分类、价格
商品卖点 营养价值/功效描述
商品特性 规格特性列表(卡片展示)
商品参数 网格形式展示参数数据
底部操作栏 客服、收藏、加入购物车、立即购买

2.2 数据架构规划
参考水果详情的数据结构,设计商品详情数据模型:

// 商品参数项
class ProductParam {
  final String name;  // 参数名称
  final String value; // 参数值
}

// 商品规格特性
class ProductFeature {
  final String short; // 简短描述
  final String long;  // 详细描述
}

// 商品详情
class ProductDetail {
  final String id;                  // 商品ID
  final String name;                // 商品名称
  final String englishName;         // 英文名称
  final List<ProductParam> params;  // 商品参数列表
  final String category;            // 分类
  final String picture;             // 商品图片
  final String benefits;            // 商品功效/卖点
  final List<String> detailImages;  // 详情图片列表
  final String detailDescription;   // 详细描述
  final String mainColor;           // 主题色
  final String mainBg;              // 背景色
  final List<ProductFeature> features; // 规格特性列表
  final String price;               // 价格
  final String originalPrice;       // 原价
}

三、代码实现方案

3.1 创建数据模型文件
文件路径:lib/viewmodels/product_detail.dart

/**
 * 商品详情数据模型
 *
 * 参考CSDN文章: https://blog.csdn.net/qq_33247427/article/details/157554060
 * 来源: 【开源鸿蒙跨平台开发先锋训练营】DAY15~DAY19为开源鸿蒙跨平台应用全面集成添加核心场景-水果详情页面
 */

// 商品参数项
class ProductParam {
  final String name;  // 参数名称
  final String value; // 参数值

  ProductParam({required this.name, required this.value});

  factory ProductParam.fromJSON(List<dynamic> json) {
    return ProductParam(
      name: json[0] ?? '',
      value: json[1] ?? '',
    );
  }
}

// 商品规格特性
class ProductFeature {
  final String short; // 简短描述
  final String long;  // 详细描述

  ProductFeature({required this.short, required this.long});

  factory ProductFeature.fromJSON(Map<String, dynamic> json) {
    return ProductFeature(
      short: json['short'] ?? '',
      long: json['long'] ?? '',
    );
  }
}

// 商品详情
class ProductDetail {
  final String id;                  // 商品ID
  final String name;                // 商品名称
  final String englishName;         // 英文名称
  final List<ProductParam> params;  // 商品参数列表
  final String category;            // 分类
  final String picture;             // 商品图片
  final String benefits;            // 商品功效/卖点
  final List<String> detailImages;  // 详情图片列表
  final String detailDescription;   // 详细描述
  final String mainColor;           // 主题色
  final String mainBg;              // 背景色
  final List<ProductFeature> features; // 规格特性列表
  final String price;               // 价格
  final String originalPrice;       // 原价

  ProductDetail({
    required this.id,
    required this.name,
    required this.englishName,
    required this.params,
    required this.category,
    required this.picture,
    required this.benefits,
    required this.detailImages,
    required this.detailDescription,
    required this.mainColor,
    required this.mainBg,
    required this.features,
    required this.price,
    required this.originalPrice,
  });

  factory ProductDetail.fromJSON(Map<String, dynamic> json) {
    // 解析参数列表
    List<ProductParam> paramList = [];
    if (json['params'] != null) {
      paramList = (json['params'] as List)
          .map((e) => ProductParam.fromJSON(e as List<dynamic>))
          .toList();
    }

    // 解析特性列表
    List<ProductFeature> featureList = [];
    if (json['features'] != null) {
      featureList = (json['features'] as List)
          .map((e) => ProductFeature.fromJSON(e as Map<String, dynamic>))
          .toList();
    }

    // 解析详情图片
    List<String> images = [];
    if (json['detailImages'] != null) {
      images = List<String>.from(json['detailImages']);
    }

    return ProductDetail(
      id: json['id'] ?? '',
      name: json['name'] ?? '',
      englishName: json['englishName'] ?? '',
      params: paramList,
      category: json['category'] ?? '默认分类',
      picture: json['picture'] ?? '',
      benefits: json['benefits'] ?? '',
      detailImages: images,
      detailDescription: json['detailDescription'] ?? '',
      mainColor: json['mainColor'] ?? '#FF6B00',
      mainBg: json['mainBg'] ?? '#FFF3E0',
      features: featureList,
      price: json['price'] ?? '0.00',
      originalPrice: json['originalPrice'] ?? '0.00',
    );
  }

  // 创建模拟数据(用于演示)
  static ProductDetail createMock() {
    return ProductDetail(
      id: '1',
      name: '精选有机红富士苹果',
      englishName: 'Organic Fuji Apple',
      params: [
        ProductParam(name: '产地', value: '陕西烟台'),
        ProductParam(name: '规格', value: '500g/个'),
        ProductParam(name: '保质期', value: '30天'),
        ProductParam(name: '储存方式', value: '冷藏保存'),
        ProductParam(name: '净含量', value: '2.5kg'),
        ProductParam(name: '品牌', value: '果园直供'),
      ],
      category: '新鲜水果',
      picture: 'https://images.unsplash.com/photo-1560806887-1e4cd0b6cbd6?w=400',
      benefits: '富含维生素C、膳食纤维和多种矿物质,口感清脆香甜,是日常健康饮食的理想选择。',
      detailImages: [
        'https://images.unsplash.com/photo-1560806887-1e4cd0b6cbd6?w=400',
        'https://images.unsplash.com/photo-1567306226416-28f0efdc88ce?w=400',
      ],
      detailDescription: '我们的红富士苹果来自优质果园,采用有机种植方式,不使用任何化学农药和化肥。每一颗苹果都经过精心挑选,确保最佳口感和品质。',
      mainColor: '#FF6B00',
      mainBg: '#FFF3E0',
      features: [
        ProductFeature(
          short: '新鲜直达',
          long: '果园直采,从枝头到舌尖不超过48小时,确保新鲜度。',
        ),
        ProductFeature(
          short: '有机认证',
          long: '通过国家有机食品认证,无农药残留,吃得放心。',
        ),
        ProductFeature(
          short: '营养丰富',
          long: '富含维生素C、果胶、膳食纤维等营养元素,有益健康。',
        ),
      ],
      price: '39.90',
      originalPrice: '59.90',
    );
  }
}

3.2 设计API接口文档
文件路径:lib/api/product_detail.dart

/**
 * 商品详情API接口
 */

import 'package:harmonyos_day_four/utils/DioRequest.dart';
import 'package:harmonyos_day_four/viewmodels/product_detail.dart';

/// 获取商品详情数据
/// 商品详情接口(实际项目中需要替换为真实的接口地址)
Future<ProductDetail> getProductDetailAPI(String productId) async {
  // 模拟API请求 - 实际项目中应该请求真实接口
  // final result = await dioRequest.get('/product/detail/$productId');
  // return ProductDetail.fromJSON(result);

  // 当前返回模拟数据用于演示
  return ProductDetail.createMock();
}

3.3 优化商品详情页
文件路径:lib/pages/product/detail.dart

Scaffold
├── AppBar (顶部导航栏)
├── SingleChildScrollView (可滚动内容)
│   ├── 商品基本信息卡片
│   ├── 商品卖点卡片
│   ├── 商品特性卡片
│   ├── 商品参数网格
│   └── 商品详情描述卡片
└── BottomNavigationBar (底部操作栏)

关键代码:

// 商品基本信息卡片
Widget _buildProductHeader() {
  return Container(
    width: double.infinity,
    padding: const EdgeInsets.all(16),
    decoration: const BoxDecoration(color: Colors.white),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // 商品图片
        ClipRRect(
          borderRadius: BorderRadius.circular(12),
          child: Image.network(_productDetail!.picture, ...),
        ),
        // 商品名称和价格
        Text(_productDetail!.name, ...),
        // 价格信息(含原价删除线)
        Row(
          children: [
            Text(${_productDetail!.price}', style: 主题色),
            Text(${_productDetail!.originalPrice}',
                  style: 删除线样式),
          ],
        ),
      ],
    ),
  );
}

// 商品参数网格(2列布局)
GridView.builder(
  shrinkWrap: true,
  physics: const NeverScrollableScrollPhysics(),
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    mainAxisSpacing: 10,
    crossAxisSpacing: 10,
    childAspectRatio: 2.0,
  ),
  itemBuilder: (context, index) {
    return _buildParamCard(_productDetail!.params[index]);
  },
)

**3.4 增强轮播图组件交互体验,实现点击跳转功能
**
文件路径:lib/components/Home/HmSlider.dart

// 添加导入
import 'package:harmonyos_day_four/pages/product/detail.dart';

// 修改PageView.builder的itemBuilder
itemBuilder: (context, index) {
  return GestureDetector(
    onTap: () {
      // 点击轮播图跳转到商品详情页
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => ProductDetailPage(
            productId: widget.bannerList[index].id,
          ),
        ),
      );
    },
    child: Image.network(
      widget.bannerList[index].imgUrl,
      fit: BoxFit.cover,
      width: screenWidth,
      // ... loadingBuilder 和 errorBuilder
    ),
  );
},

四、遇到的问题及解决方法问题

问题描述
轮播图点击无响应,无法跳转至商品详情页。

原因分析
Stack布局中,搜索框组件通过Padding(padding: const EdgeInsets.all(10))覆盖了整个轮播图区域,导致点击事件被拦截。

Widget _getSearch() {
  return Positioned(
    top: 10,
    left: 0,
    right: 0,
    child: Padding(
      padding: const EdgeInsets.all(10),  // 导致覆盖问题
      child: Container(...),
    ),
  );
}
 

修复方案
移除Padding改用直接边距控制:

Widget _getSearch() {
  return Positioned(
    top: 10,
    left: 10,   // 直接控制边距
    right: 10,
    child: Container(...),  // 移除Padding
  );
}
 

修复效果

搜索框仅占据顶部70px区域
轮播图其他区域可正常响应点击事件
图片加载失败处理方案
问题描述
网络图片加载失败时无友好提示。

解决方案
使用errorBuilder构建错误状态UI:

Image.network(
  _productDetail!.picture,
  errorBuilder: (context, error, stackTrace) {
    return Container(
      decoration: BoxDecoration(
        color: bgColor,
        borderRadius: BorderRadius.circular(12),
      ),
      child: Center(
        child: Icon(Icons.image_not_supported,
                    size: 50, color: mainColor.withOpacity(0.5)),
      ),
    );
  },
)
 

主题色动态解析方案

问题描述
需将服务端返回的十六进制颜色值(如#FF6B00)转换为Color对象。

解决方案

Color _parseColor(String hex) {
  return Color(int.parse(hex.substring(1, 7), radix: 16) + 0xFF000000);
}
 

使用示例

Color primaryColor = _parseColor('#FF6B00');

五、页面配色方案

参考水果详情页面的配色,电商详情页采用橙色系:
以下是整理后的表格形式:

用途 颜色值 说明
主题色 #FF6B00 橙色,用于标题、价格、按钮
浅橙背景 #FFF3E0 半透明橙色,用于卡片背景
页面背景 #F5F5F5 浅灰色
主文本 #1F2937 深灰色
次要文本 #9CA3AF 中灰色
标签文本 #6B7280 灰色

六、项目结构

lib/
├── api/
│   └── product_detail.dart          # 商品详情API
├── components/
│   └── Home/
│       └── HmSlider.dart            # 轮播图组件(已修改)
├── pages/
│   └── product/
│       └── detail.dart              # 商品详情页面
└── viewmodels/
    └── product_detail.dart          # 商品详情数据模型

七、总结

数据模型设计
参考开源鸿蒙的水果详情页面数据结构,设计电商商品详情的数据模型,确保数据字段覆盖商品基本属性、规格参数和展示信息。

页面UI开发
采用卡片式布局风格实现商品详情页,完整展示商品主图、名称、价格、促销信息、规格参数和详情描述等内容区域。通过视觉层级划分提升信息可读性。

交互功能实现
完成从首页轮播图到商品详情页的点击跳转功能联调,确保路由跳转参数传递正确。针对Stack布局中存在的点击事件拦截问题,通过调整组件层级和事件冒泡机制进行修复。

关键技术应用
GridView.builder组件实现商品参数网格化展示,支持动态数据加载和自适应列数。Positioned组件在Stack布局中精确定位悬浮按钮等子元素。通过合理设置Padding避免组件重叠导致的界面异常。

异常处理机制
为网络图片组件配置errorBuilder回调,处理加载失败情况并显示占位图。对可能出现的空数据状态进行防御性编程,保证页面容错能力。

该实现方案采用Flutter框架构建跨平台电商商品详情页,在保持原生性能的同时提供丰富的交互体验。通过组件化开发思路,各功能模块可独立维护和复用。

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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐