Menu
CoddyTech

Giỏ hàng: Tổng tiền

Thuộc phần Quản lý trạng thái trong hành trình Flutter của Coddy. Bài học 25 trên 27.

Màn hình giỏ hàng cần hiển thị tổng tiền. Tổng tiền không phải dữ liệu mới, mà được tính từ những gì có trong giỏ hàng. Vì vậy, tổng tiền không được lưu ở đâu cả. Đây là một provider theo dõi giỏ hàng và cộng giá bằng fold:

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

Vì provider theo dõi giỏ hàng, tổng tiền sẽ được tính lại mỗi khi giỏ hàng thay đổi, và mọi widget theo dõi tổng tiền đều hiển thị số tiền mới.

Hiển thị tổng tiền bên dưới danh sách. Vị trí bottomNavigationBar của Scaffold nhận mọi widget và giữ widget ở cuối màn hình, bên dưới danh sách, ngay cả khi danh sách cuộn.

challenge icon

Thử thách

Trung bình

Hiển thị tổng tiền trên màn hình giỏ hàng:

  • Thêm một totalProvider theo dõi cartProvider và cộng các mức giá.
  • Trên màn hình giỏ hàng, hiển thị Total: $2.75 (hai chữ số thập phân) trong bottomNavigationBar của Scaffold, kèm theo một chút khoảng đệm.

Tự làm thử

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];
  }
}

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

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);
    return Scaffold(
      appBar: AppBar(title: const Text('Your cart')),
      body: cart.isEmpty
          ? const Center(child: Text('Your cart is empty'))
          : ListView(
              children: [
                for (final product in cart)
                  ListTile(
                    title: Text(product.name),
                    trailing: Text('\$${product.price.toStringAsFixed(2)}'),
                  ),
              ],
            ),
    );
  }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Quản lý trạng thái

Tự luyện tập: Trình biên dịch Flutter online