Menu
CoddyTech

Cửa hàng: Lưới sản phẩm

Thuộc phần Dự án trong hành trình Flutter của Coddy. Bài học 19 trên 36.

Ứng dụng thứ ba là một cửa hàng nhỏ: có lưới sản phẩm, trang riêng cho từng sản phẩm, giỏ hàng và bước thanh toán. Đây là hình thức phổ biến của hầu hết ứng dụng mua sắm, đồng thời giúp bạn ôn lại Provider và intl.

Các sản phẩm đã có sẵn trong phần khởi đầu, mỗi sản phẩm gồm tên, giá, biểu tượng, màu sắc và mô tả. Cửa hàng hiển thị chúng theo dạng lưới, mỗi hàng có hai sản phẩm trên điện thoại:

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 là chiều rộng chia cho chiều cao: 0.8 khiến mỗi thẻ cao hơn chiều rộng một chút, đủ chỗ cho hình ảnh và hai dòng chữ.

Mỗi thẻ là một widget riêng, ProductCard, nhận một Product. Bên trong, ô Expanded lấp đầy khoảng trống phía trên tên sản phẩm. Màu của ô là sắc độ nhạt nhất của màu sản phẩm, product.color.shade50, với biểu tượng có màu đầy đủ đặt bên trên. Vì vậy, color có kiểu MaterialColor: kiểu này có nhiều sắc độ.

challenge icon

Thử thách

Trung bình

Hiển thị các sản phẩm:

  • viết một widget ProductCard nhận product là tham số bắt buộc
  • đây là một Card (với clipBehavior: Clip.antiAlias) chứa một Column: một Container Expanded rộng hết chiều ngang, có màu product.color.shade50 và biểu tượng sản phẩm ở giữa (kích thước 56, product.color), sau đó là tên sản phẩm trong titleMedium với phần đệm 12 pixel
  • thay Center bằng GridView.count trong phần lý thuyết

Tự làm thử

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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Dự án

Tự luyện tập: Trình biên dịch Flutter online