Menu
CoddyTech

Sklep: Szczegóły produktu

Część sekcji Projekty ścieżki Flutter w Coddy. Lekcja 21 z 36.

Gdy stukniesz produkt w sklepie, otworzy się jego strona: duże zdjęcie, cena i kilka słów na jego temat. To drugi ekran, ProductScreen, który otrzymuje produkt za pośrednictwem konstruktora.

Aby można było stuknąć całą kartę, umieść InkWell wewnątrz Card, wokół jej zawartości. Uruchamia on onTap i rysuje falę, którą widzisz w aplikacjach Material; Clip.antiAlias karty utrzymuje falę wewnątrz zaokrąglonych rogów.

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

Strona produktu to ListView, więc długi opis można przewijać. Jej AppBar wyświetla nazwę produktu i strzałkę wstecz, którą Flutter dodaje automatycznie na ekranie otwartym przez nawigację.

W przypadku dużej ceny zacznij od stylu headlineSmall z motywu i zmień tylko to, czego potrzebujesz, za pomocą copyWith: textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold). ?. jest tam dlatego, że styl motywu może mieć wartość null.

challenge icon

Wyzwanie

Średni

Utwórz stronę dla każdego produktu:

  • napisz ProductScreen, który przyjmuje wymagany product, a jego nazwę wyświetla w AppBar
  • jego treść to ListView z marginesem wewnętrznym 24 pikseli: kafelek o wysokości 240 pikseli w kolorze product.color.shade50, z zaokrąglonymi narożnikami o promieniu 24 pikseli i ikoną w rozmiarze 120, następnie 24 piksele odstępu, cena (pogrubione headlineSmall w kolorze głównym), 12 pikseli odstępu i opis w stylu bodyLarge
  • owijaj Column każdej karty w InkWell, który otwiera stronę produktu

Spróbuj swoich sił

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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Projekty

Poćwicz samodzielnie: Kompilator Flutter online