Menu
CoddyTech

商店:商品网格

本课属于 Coddy Flutter 学习旅程的“项目”单元。第 19 课,共 36 课。

第三个应用是一家小商店:商品网格、每件商品各自的页面、购物车和结账页面。大多数购物应用都采用这种结构,而且这里会再次用到 Provider 和 intl。

商品已包含在起始项目中,每件商品都有名称、价格、图标、颜色和描述。商店会以网格形式展示商品,在手机上每行显示两件:

GridView.count(
  crossAxisCount: 2,
  padding: const EdgeInsets.all(16),
  mainAxisSpacing: 12,
  crossAxisSpacing: 12,
  childAspectRatio: 0.8,
  children: [for (Product product in products) ProductCard(product: product)],
)

childAspectRatio 是宽度除以高度:0.8 会让每张卡片略高于宽,足以放下一张图片和两行文字。

每张卡片都是一个独立的组件 ProductCard,它接收一个 Product。其中,Expanded 瓷砖会填满名称上方的空间。它的颜色是商品颜色中最浅的色调,即 product.color.shade50,上面放着一个完整颜色的图标。这就是 color 是 MaterialColor 的原因:它包含多个色调。

challenge icon

挑战

中等

显示商品:

  • 编写一个 ProductCard 组件,它接收一个 required 的 product
  • 它是一个 Card(带有 clipBehavior: Clip.antiAlias),其中包含一个 Column:一个全宽的 Expanded Container,背景色为 product.color.shade50,中间显示商品图标(大小为 56,颜色为 product.color),然后以 titleMedium 样式显示名称,并设置 12 像素的内边距
  • 将 Center 替换为理论部分介绍的 GridView.count

亲手试试

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
      ),
      home: const ShopScreen(),
    );
  }
}

class Product {
  const Product({
    required this.name,
    required this.price,
    required this.icon,
    required this.color,
    required this.description,
  });

  final String name;
  final double price;
  final IconData icon;
  final MaterialColor color;
  final String description;
}

const List<Product> products = [
  Product(
    name: 'Headphones',
    price: 59,
    icon: Icons.headphones,
    color: Colors.indigo,
    description: 'Wireless headphones with 30 hours of battery.',
  ),
  Product(
    name: 'Watch',
    price: 129,
    icon: Icons.watch,
    color: Colors.teal,
    description: 'A smart watch that counts your steps.',
  ),
  Product(
    name: 'Backpack',
    price: 45.5,
    icon: Icons.backpack,
    color: Colors.orange,
    description: 'Room for a laptop, a lunch and a jacket.',
  ),
  Product(
    name: 'Mug',
    price: 12,
    icon: Icons.coffee,
    color: Colors.brown,
    description: 'Holds a big coffee and keeps it warm.',
  ),
  Product(
    name: 'Camera',
    price: 1299,
    icon: Icons.photo_camera,
    color: Colors.blueGrey,
    description: 'A mirrorless camera for sharp photos.',
  ),
  Product(
    name: 'Sneakers',
    price: 89.99,
    icon: Icons.directions_run,
    color: Colors.red,
    description: 'Light running shoes for every day.',
  ),
];

class ShopScreen extends StatelessWidget {
  const ShopScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Coddy Shop')),
      body: const Center(
        child: Text('Products go here'),
      ),
    );
  }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

项目中的所有课

自己练习: Flutter 在线编译器