Menu
CoddyTech

Shop: Produktdetails

Teil des Abschnitts Projekte der Flutter-Journey von Coddy. Lektion 21 von 36.

Tippe in einem Geschäft auf ein Produkt, und seine Seite wird angezeigt: ein großes Bild, der Preis und ein paar Worte dazu. Das ist ein zweiter Bildschirm, ProductScreen, der das Produkt über seinen Konstruktor erhält.

Damit die ganze Karte antippbar ist, platziere ein InkWell innerhalb der Card um ihren Inhalt herum. Es führt onTap aus und zeichnet den Welleneffekt, den du in Material-Apps siehst; das Clip.antiAlias der Karte hält den Welleneffekt innerhalb der abgerundeten Ecken.

Card(
  clipBehavior: Clip.antiAlias,
  child: InkWell(
    onTap: () {
      Navigator.push(context, MaterialPageRoute(
        builder: (context) => ProductScreen(product: product),
      ));
    },
    child: Column(...),
  ),
)

Die Produktseite ist eine ListView, sodass eine lange Beschreibung scrollbar ist. Ihre AppBar zeigt den Namen des Produkts und den Zurück-Pfeil, den Flutter automatisch für dich auf einem weitergeleiteten Bildschirm hinzufügt.

Für den großen Preis nimm den Stil headlineSmall des Designs und ändere mit copyWith nur, was du brauchst: textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold). Das ?. steht dort, weil ein Designstil null sein kann.

challenge icon

Aufgabe

Mittel

Gib jedem Produkt eine eigene Seite:

  • Schreibe einen ProductScreen, der ein required product entgegennimmt, wobei der Name in der AppBar angezeigt wird
  • Der Inhalt besteht aus einer ListView mit 24 Pixeln Innenabstand: einer 240 Pixel hohen Kachel in product.color.shade50 mit Eckenradius 24 und dem Icon in Größe 120, dann 24 Pixel Abstand, dem Preis (headlineSmall in Fettschrift und in der Primärfarbe), 12 Pixel Abstand und der Beschreibung in bodyLarge
  • Umschließe die Column jeder Karte mit einem InkWell, der zur Produktseite navigiert

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: 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,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Projekte

Übe selbstständig: Online-Flutter-Compiler