Menu
CoddyTech

Keranjang Belanja: Selesaikan Pembelian

Termasuk bagian Pengelolaan State dalam perjalanan Flutter di Coddy. Pelajaran 27 dari 27.

Bagian terakhir adalah checkout. Satu ketukan melakukan tiga hal: isi cart dikosongkan, sebuah pesan mengonfirmasi pesanan, dan pembeli kembali ke toko, tempat badge sudah hilang.

Mengosongkan cart adalah satu method lagi pada notifier, dan method ini menetapkan list kosong yang baru:

void clear() {
  state = [];
}

Checkout saat cart kosong tidak masuk akal, jadi buat tombolnya berwarna abu-abu. Tombol dengan onPressed bernilai null tidak bisa ditekan:

onPressed: cart.isEmpty ? null : () { ... },

Lihat kembali yang sudah kamu buat: dua layar, sebuah badge dan total, semuanya membaca satu cart, tanpa nilai yang diteruskan dari satu layar ke layar lain. Itulah manajemen state.

challenge icon

Tantangan

Sedang

Di bawah total pada layar keranjang, tambahkan tombol Check out:

  • Dinonaktifkan saat keranjang kosong.
  • Saat ditekan, kosongkan keranjang dengan method baru clear(), tampilkan SnackBar bertuliskan Order placed!, lalu kembali ke toko.

Coba sendiri

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class Product {
  const Product(this.name, this.price);

  final String name;
  final double price;
}

const products = [
  Product('Apple', 0.5),
  Product('Bread', 2.25),
  Product('Cheese', 4),
  Product('Milk', 1.2),
];

class Cart extends Notifier<List<Product>> {
  @override
  List<Product> build() => [];

  void add(Product product) {
    state = [...state, product];
  }

  void removeAt(int index) {
    final copy = [...state];
    copy.removeAt(index);
    state = copy;
  }
}

final cartProvider = NotifierProvider<Cart, List<Product>>(Cart.new);

final totalProvider = Provider((ref) {
  final cart = ref.watch(cartProvider);
  return cart.fold(0.0, (sum, product) => sum + product.price);
});

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: ShopPage(),
    );
  }
}

class ShopPage extends ConsumerWidget {
  const ShopPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final cart = ref.watch(cartProvider);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Shop'),
        actions: [
          IconButton(
            icon: Badge(
              label: Text('${cart.length}'),
              isLabelVisible: cart.isNotEmpty,
              child: const Icon(Icons.shopping_cart),
            ),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const CartScreen()),
              );
            },
          ),
        ],
      ),
      body: ListView(
        children: [
          for (final product in products)
            ListTile(
              title: Text(product.name),
              subtitle: Text('\$${product.price.toStringAsFixed(2)}'),
              trailing: IconButton(
                icon: const Icon(Icons.add_shopping_cart),
                onPressed: () {
                  ref.read(cartProvider.notifier).add(product);
                },
              ),
            ),
        ],
      ),
    );
  }
}

class CartScreen extends ConsumerWidget {
  const CartScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final cart = ref.watch(cartProvider);
    final total = ref.watch(totalProvider);
    return Scaffold(
      appBar: AppBar(title: const Text('Your cart')),
      bottomNavigationBar: Padding(
        padding: const EdgeInsets.all(24),
        child: Text(
          'Total: \$${total.toStringAsFixed(2)}',
          style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
      ),
      body: cart.isEmpty
          ? const Center(child: Text('Your cart is empty'))
          : ListView(
              children: [
                for (var i = 0; i < cart.length; i++)
                  ListTile(
                    title: Text(cart[i].name),
                    subtitle: Text('\$${cart[i].price.toStringAsFixed(2)}'),
                    trailing: IconButton(
                      icon: const Icon(Icons.delete),
                      onPressed: () {
                        ref.read(cartProvider.notifier).removeAt(i);
                      },
                    ),
                  ),
              ],
            ),
    );
  }
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pengelolaan State

Latihan sendiri: Compiler Flutter online