Shop: In den Warenkorb
Teil des Abschnitts Projekte der Flutter-Journey von Coddy. Lektion 22 von 36.
Der Warenkorb wird auf zwei Bildschirmen benötigt: Die Produktseite fügt Produkte hinzu, und das Raster zeigt an, wie viele Produkte sich darin befinden. Daten, die mehrere Bildschirme benötigen, gehören in ein Modell, das mit Provider geteilt wird.
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();
}
}Umschließe die gesamte App in main mit einem ChangeNotifierProvider, damit jeder Bildschirm auf den Warenkorb zugreifen kann. Die Schaltfläche ändert nur den Warenkorb, daher verwendet sie context.read<CartModel>().add(product). Das Raster zeigt die Anzahl an, daher verwendet es context.watch, wodurch das Raster jedes Mal neu aufgebaut wird, wenn notifyListeners ausgeführt wird.
Die Anzahl wird als Badge auf dem Warenkorbsymbol angezeigt – das ist die kleine Blase, die Apps für ungelesene Nachrichten verwenden. isLabelVisible: count > 0 blendet sie aus, solange der Warenkorb leer ist.
Aufgabe
SchwerEinen gemeinsamen Warenkorb hinzufügen:
- importiere
package:provider/provider.dart, schreibe dasCartModelaus der Theorie und umschließeMyAppmit einemChangeNotifierProvider, der es erstellt - füge auf der Produktseite nach einem Abstand von 32 Pixeln einen
FilledButton.iconmitIcons.add_shopping_cartundAdd to carthinzu: Er fügt das Produkt hinzu, blendet bereits angezeigte Meldungen aus und zeigt eineSnackBarmit dem TextAdded Backpack to your cartan (mit dem Produktnamen) - zeige in den
actionsderAppBardes RastersIcons.shopping_cartin einemBadgemit der Anzahl an, 24 Pixel vom rechten Rand entfernt
Probier es selbst
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),
],
),
);
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Projekte
1Projekt: To-do-Liste
To-do-Liste: Die AufgabenlisteTo-do-Liste: Eine Aufgabe hinzufügen4Projekt: Gewohnheitstracker
Gewohnheitstracker: Deine GewohnheitenGewohnheitstracker: Die WocheÜbe selbstständig: Online-Flutter-Compiler