Toko: Tambahkan ke Keranjang
Termasuk bagian Proyek dalam perjalanan Flutter di Coddy. Pelajaran 22 dari 36.
Keranjang dibutuhkan di dua layar: halaman produk menambahkan produk ke keranjang, dan grid menampilkan jumlah isinya. Data yang dibutuhkan oleh lebih dari satu layar disimpan dalam model, yang dibagikan menggunakan Provider.
class CartModel extends ChangeNotifier {
final List<Product> _items = [];
List<Product> get items => _items;
int get count => _items.length;
void add(Product product) {
_items.add(product);
notifyListeners();
}
}Bungkus seluruh aplikasi dengan ChangeNotifierProvider di main, agar setiap layar dapat mengakses keranjang. Tombol hanya mengubah keranjang, jadi tombol menggunakan context.read<CartModel>().add(product). Grid menampilkan jumlahnya, jadi grid menggunakan context.watch, yang membangun ulang grid setiap kali notifyListeners dijalankan.
Jumlahnya ditampilkan pada ikon keranjang sebagai Badge, gelembung kecil yang digunakan aplikasi untuk pesan yang belum dibaca. isLabelVisible: count > 0 menyembunyikannya saat keranjang kosong.
Tantangan
SulitTambahkan ke keranjang bersama:
- impor
package:provider/provider.dart, tulisCartModeldari teori, lalu bungkusMyAppdenganChangeNotifierProvideryang membuatnya - di halaman produk, setelah jarak 32 piksel, tambahkan
FilledButton.icondenganIcons.add_shopping_cartdanAdd to cart: tombol ini menambahkan produk, menyembunyikan pesan yang sedang ditampilkan, lalu menampilkanSnackBaryang bertuliskanAdded Backpack to your cart(dengan nama produk) - di
actionspadaAppBargrid, tampilkanIcons.shopping_cartdalamBadgedengan jumlah item, 24 piksel dari sisi kanan
Coba sendiri
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: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductScreen(product: product),
),
);
},
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,
),
),
],
),
),
],
),
),
);
}
}
class ProductScreen extends StatelessWidget {
const ProductScreen({super.key, required this.product});
final Product product;
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: AppBar(title: Text(product.name)),
body: ListView(
padding: const EdgeInsets.all(24),
children: [
Container(
height: 240,
alignment: Alignment.center,
decoration: BoxDecoration(
color: product.color.shade50,
borderRadius: BorderRadius.circular(24),
),
child: Icon(product.icon, size: 120, color: product.color),
),
const SizedBox(height: 24),
Text(
money.format(product.price),
style: textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 12),
Text(product.description, style: textTheme.bodyLarge),
],
),
);
}
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Proyek
1Proyek: Daftar Tugas
Daftar Tugas: Daftar TugasDaftar Tugas: Tambahkan Tugas4Proyek: Pelacak Kebiasaan
Pelacak Kebiasaan: KebiasaanmuPelacak Kebiasaan: Pekan Ini3Proyek: Toko
Toko: Grid ProdukToko: HargaLatihan sendiri: Compiler Flutter online