Cửa hàng: Chi tiết sản phẩm
Thuộc phần Dự án trong hành trình Flutter của Coddy. Bài học 21 trên 36.
Chạm vào một sản phẩm trong cửa hàng, bạn sẽ mở trang của sản phẩm đó: một hình ảnh lớn, giá và vài dòng giới thiệu. Đó là màn hình thứ hai, ProductScreen, nhận sản phẩm thông qua hàm khởi tạo.
Để có thể chạm vào toàn bộ thẻ, hãy đặt InkWell bên trong Card, bao quanh nội dung. Thành phần này chạy onTap và tạo hiệu ứng gợn sóng bạn thấy trong các ứng dụng Material; Clip.antiAlias của thẻ giữ hiệu ứng gợn sóng bên trong các góc bo tròn.
Card(
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => ProductScreen(product: product),
));
},
child: Column(...),
),
)Trang sản phẩm dùng ListView, vì vậy bạn có thể cuộn phần mô tả dài. AppBar hiển thị tên sản phẩm và mũi tên quay lại, mà Flutter tự thêm cho bạn khi mở một màn hình mới.
Để hiển thị giá thật lớn, hãy bắt đầu từ headlineSmall trong giao diện rồi chỉ thay đổi những gì cần thiết bằng copyWith: textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold). Có ?. vì kiểu giao diện có thể là null.
Thử thách
Trung bìnhTạo một trang cho mỗi sản phẩm:
- viết một
ProductScreennhậnproductcórequired, với tên sản phẩm trongAppBarcủa trang - phần thân là một
ListViewcó khoảng đệm 24 pixel: một ô cao 240 pixel, có màuproduct.color.shade50, các góc bo tròn 24 pixel và biểu tượng có kích thước 120, sau đó là khoảng cách 24 pixel, giá (dùngheadlineSmallin đậm với màu chính), khoảng cách 12 pixel và phần mô tả trongbodyLarge - bọc
Columncủa mỗi thẻ trongInkWellđể mở trang của sản phẩm đó
Tự làm thử
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const MyApp());
}
final money = NumberFormat.currency(locale: 'en_US', symbol: '\$');
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: 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),
],
),
);
}
}
class ProductCard extends StatelessWidget {
const ProductCard({super.key, required this.product});
final Product product;
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
width: double.infinity,
alignment: Alignment.center,
color: product.color.shade50,
child: Icon(product.icon, size: 56, color: product.color),
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
Text(
money.format(product.price),
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
],
),
),
],
),
);
}
}
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
1Dự án: Danh sách việc cần làm
Danh sách việc cần làm: Danh sách công việcDanh sách việc cần làm: Thêm công việc4Dự án: Theo dõi thói quen
Theo dõi thói quen: Thói quen của bạnTheo dõi thói quen: Tuần3Dự án: Cửa hàng
Cửa hàng: Lưới sản phẩmCửa hàng: Giá cảTự luyện tập: Trình biên dịch Flutter online