在这里插入图片描述

前言

在现代电商应用开发中,商品卡片是最基础也是最重要的UI组件之一。一个设计精良的商品卡片不仅能够清晰地展示商品信息,还能有效提升用户的购买欲望。本文将详细介绍如何在Flutter和OpenHarmony平台上开发一个功能完善、视觉美观的商品卡片组件,帮助开发者快速构建跨平台的商城应用。

商品卡片通常需要展示商品图片、名称、价格、折扣信息、销量等关键数据。在设计时,我们需要考虑不同屏幕尺寸的适配问题,以及用户交互体验的优化。Flutter的声明式UI框架和OpenHarmony的ArkUI都提供了强大的布局能力,让我们能够轻松实现响应式的商品卡片设计。

Flutter商品卡片基础结构

首先,我们来看Flutter中商品卡片的基础数据模型定义:

class Product {
  final String id;
  final String name;
  final String imageUrl;
  final double price;
  final double originalPrice;
  final int salesCount;
  final double rating;

  Product({
    required this.id,
    required this.name,
    required this.imageUrl,
    required this.price,
    required this.originalPrice,
    required this.salesCount,
    required this.rating,
  });
}

在Flutter开发中,定义清晰的数据模型是构建UI组件的第一步。Product类包含了商品的核心属性,包括唯一标识符id用于数据追踪和列表渲染优化,name存储商品名称,imageUrl保存商品图片的网络地址,price和originalPrice分别表示当前售价和原价用于计算折扣,salesCount记录销量数据增强用户信任感,rating则是商品评分用于展示商品质量。这种数据模型的设计遵循了单一职责原则,每个字段都有明确的用途,便于后续的维护和扩展。

接下来实现商品卡片的容器组件:

class ProductCard extends StatelessWidget {
  final Product product;
  final VoidCallback? onTap;

  const ProductCard({
    Key? key,
    required this.product,
    this.onTap,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(12),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(0.08),
              blurRadius: 8,
              offset: const Offset(0, 2),
            ),
          ],
        ),
        child: _buildCardContent(),
      ),
    );
  }
}

ProductCard组件采用StatelessWidget实现,因为商品卡片本身不需要管理内部状态,所有数据都通过外部传入。GestureDetector包装器为整个卡片添加了点击事件处理能力,用户点击卡片时可以跳转到商品详情页。Container的decoration属性定义了卡片的视觉样式,包括白色背景、12像素的圆角以及柔和的阴影效果。阴影使用了8%透明度的黑色,配合8像素的模糊半径和向下2像素的偏移,营造出卡片悬浮的立体感,这种设计符合Material Design的设计规范。

OpenHarmony商品卡片实现

在OpenHarmony平台上,我们使用ArkTS语言和ArkUI框架来实现相同的商品卡片功能:

@Component
struct ProductCard {
  @Prop product: ProductInfo
  private onCardClick: () => void = () => {}

  build() {
    Column() {
      this.ProductImage()
      this.ProductInfo()
    }
    .width('100%')
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({
      radius: 8,
      color: '#14000000',
      offsetX: 0,
      offsetY: 2
    })
    .onClick(() => this.onCardClick())
  }
}

OpenHarmony的ArkUI框架采用了声明式UI的设计理念,与Flutter有着相似的开发体验。@Component装饰器标记这是一个可复用的UI组件,@Prop装饰器声明product属性从父组件接收数据。build方法是组件的核心,返回组件的UI结构。Column容器将子元素垂直排列,通过链式调用设置宽度、背景色、圆角和阴影等样式属性。shadow方法的参数与Flutter的BoxShadow类似,radius控制模糊程度,color使用带透明度的十六进制颜色值,offsetX和offsetY控制阴影偏移方向。

定义商品数据接口:

interface ProductInfo {
  id: string
  name: string
  imageUrl: string
  price: number
  originalPrice: number
  salesCount: number
  rating: number
}

在TypeScript中,interface用于定义对象的类型结构,这与Dart中的class定义数据模型有所不同。interface只定义类型约束,不包含具体实现,这使得代码更加灵活。ProductInfo接口定义了与Flutter中Product类相同的属性,确保两个平台的数据结构保持一致,便于后端API的统一设计和前端代码的逻辑复用。

商品图片组件实现

Flutter中的商品图片组件:

Widget _buildProductImage() {
  return ClipRRect(
    borderRadius: const BorderRadius.vertical(
      top: Radius.circular(12),
    ),
    child: AspectRatio(
      aspectRatio: 1,
      child: Image.network(
        product.imageUrl,
        fit: BoxFit.cover,
        errorBuilder: (context, error, stackTrace) {
          return Container(
            color: Colors.grey[200],
            child: const Icon(
              Icons.image_not_supported,
              size: 48,
              color: Colors.grey,
            ),
          );
        },
      ),
    ),
  );
}

商品图片是吸引用户注意力的关键元素,因此需要特别注意其展示效果。ClipRRect组件用于裁剪子组件的边角,这里只对顶部两个角进行圆角处理,与卡片容器的圆角保持一致。AspectRatio组件确保图片保持1:1的正方形比例,这是电商应用中最常见的商品图片展示比例。Image.network用于加载网络图片,fit属性设置为BoxFit.cover确保图片完全覆盖容器区域同时保持原始比例。errorBuilder回调处理图片加载失败的情况,显示一个灰色背景和图片损坏图标,提供良好的错误反馈体验。

OpenHarmony中的图片组件实现:

@Builder
ProductImage() {
  Stack() {
    Image(this.product.imageUrl)
      .width('100%')
      .aspectRatio(1)
      .objectFit(ImageFit.Cover)
      .borderRadius({
        topLeft: 12,
        topRight: 12
      })
      .alt($r('app.media.placeholder'))
    
    if (this.getDiscount() > 0) {
      this.DiscountBadge()
    }
  }
}

@Builder装饰器定义了一个可复用的UI构建方法,类似于Flutter中的私有Widget构建方法。Stack容器允许子元素层叠显示,这里用于在图片上叠加折扣标签。Image组件的objectFit属性对应Flutter的fit属性,ImageFit.Cover实现相同的覆盖效果。borderRadius支持分别设置四个角的圆角值,这里只设置顶部两个角。alt属性指定图片加载失败时的占位图,$r函数用于引用应用资源文件。条件渲染通过if语句实现,当存在折扣时显示折扣标签组件。

价格信息展示

Flutter价格组件的实现:

Widget _buildPriceSection() {
  final discount = ((product.originalPrice - product.price) / 
                    product.originalPrice * 100).round();
  
  return Padding(
    padding: const EdgeInsets.symmetric(horizontal: 12),
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.end,
      children: [
        Text(
          ${product.price.toStringAsFixed(2)}',
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
            color: Color(0xFFE53935),
          ),
        ),
        const SizedBox(width: 6),
        Text(
          ${product.originalPrice.toStringAsFixed(2)}',
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey[500],
            decoration: TextDecoration.lineThrough,
          ),
        ),
      ],
    ),
  );
}

价格展示是商品卡片中最重要的信息之一,需要通过视觉设计突出显示。当前售价使用较大的18像素字号和粗体样式,配合醒目的红色(#E53935)吸引用户注意。原价使用较小的12像素字号和灰色,并添加删除线效果表示已失效。Row组件将两个价格水平排列,crossAxisAlignment设置为end使它们底部对齐,视觉上更加协调。Padding组件为价格区域添加水平内边距,与卡片边缘保持适当距离。toStringAsFixed(2)方法确保价格始终显示两位小数,保持格式统一。

OpenHarmony价格组件:

@Builder
PriceSection() {
  Row() {
    Text('¥' + this.product.price.toFixed(2))
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
      .fontColor('#E53935')
    
    Text('¥' + this.product.originalPrice.toFixed(2))
      .fontSize(12)
      .fontColor('#9E9E9E')
      .decoration({
        type: TextDecorationType.LineThrough,
        color: '#9E9E9E'
      })
      .margin({ left: 6 })
  }
  .width('100%')
  .padding({ left: 12, right: 12 })
  .alignItems(VerticalAlign.Bottom)
}

ArkUI的Text组件通过链式调用设置样式属性,这种API设计使代码更加简洁易读。fontSize、fontWeight、fontColor分别设置字号、字重和颜色。decoration方法用于添加文本装饰效果,TextDecorationType.LineThrough创建删除线。margin方法设置外边距,这里只设置左边距实现两个价格之间的间隔。Row的alignItems属性设置为VerticalAlign.Bottom实现底部对齐,与Flutter的crossAxisAlignment效果相同。

销量和评分展示

Widget _buildSalesInfo() {
  return Padding(
    padding: const EdgeInsets.fromLTRB(12, 8, 12, 12),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Row(
          children: [
            const Icon(
              Icons.star,
              size: 14,
              color: Color(0xFFFFB300),
            ),
            const SizedBox(width: 2),
            Text(
              product.rating.toStringAsFixed(1),
              style: const TextStyle(
                fontSize: 12,
                color: Color(0xFFFFB300),
              ),
            ),
          ],
        ),
        Text(
          '已售${_formatSalesCount(product.salesCount)}',
          style: TextStyle(
            fontSize: 11,
            color: Colors.grey[500],
          ),
        ),
      ],
    ),
  );
}

销量和评分信息能够帮助用户快速判断商品的受欢迎程度和质量水平。评分使用星星图标配合数字显示,采用金黄色(#FFB300)突出显示。销量信息放置在右侧,使用较小的字号和灰色,作为辅助信息展示。mainAxisAlignment设置为spaceBetween使两组信息分别靠左和靠右对齐,充分利用水平空间。_formatSalesCount方法用于格式化销量数字,当销量超过一万时显示为"x.x万"的形式,提高可读性。

销量格式化方法:

String _formatSalesCount(int count) {
  if (count >= 10000) {
    return '${(count / 10000).toStringAsFixed(1)}万';
  }
  return count.toString();
}

这个辅助方法实现了销量数字的智能格式化。当销量达到10000及以上时,将其转换为以"万"为单位的形式,保留一位小数。例如,15000会显示为"1.5万",这种格式更加简洁直观,符合中文用户的阅读习惯。对于小于10000的销量,直接显示原始数字。这种处理方式在电商应用中非常常见,能够有效提升信息的可读性。

OpenHarmony销量评分组件:

@Builder
SalesInfo() {
  Row() {
    Row() {
      Image($r('app.media.star'))
        .width(14)
        .height(14)
      Text(this.product.rating.toFixed(1))
        .fontSize(12)
        .fontColor('#FFB300')
        .margin({ left: 2 })
    }
    
    Text('已售' + this.formatSalesCount(this.product.salesCount))
      .fontSize(11)
      .fontColor('#9E9E9E')
  }
  .width('100%')
  .justifyContent(FlexAlign.SpaceBetween)
  .padding({
    left: 12,
    right: 12,
    top: 8,
    bottom: 12
  })
}

ArkUI中使用Image组件加载本地资源图标,$r函数引用应用资源目录下的图片文件。嵌套的Row组件将星星图标和评分数字组合在一起。外层Row的justifyContent属性设置为FlexAlign.SpaceBetween,实现与Flutter相同的两端对齐效果。padding方法支持分别设置四个方向的内边距值,提供了更精细的布局控制能力。

总结

本文详细介绍了在Flutter和OpenHarmony平台上开发商品卡片组件的完整过程。通过对比两个平台的实现方式,我们可以发现它们在声明式UI的设计理念上有很多相似之处,这使得开发者能够快速在两个平台之间切换。商品卡片作为商城应用的核心组件,其设计质量直接影响用户体验和转化率。在实际开发中,还需要考虑图片懒加载、骨架屏占位、无障碍访问等高级特性,以打造更加完善的用户体验。

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐