Menu
CoddyTech

עגלת קניות: מעבר לתשלום

חלק מהיחידה ניהול מצב במסלול ה-Flutter של Coddy. שיעור 27 מתוך 27.

החלק האחרון הוא התשלום. לחיצה אחת עושה שלושה דברים: העגלה מתרוקנת, הודעה מאשרת את ההזמנה, והקונה חוזר לחנות, שבה התג נעלם.

ריקון העגלה הוא שיטה נוספת במחלקת ההתראות, והיא מגדירה רשימה חדשה וריקה:

void clear() {
  state = [];
}

תשלום כשאין שום דבר בעגלה לא הגיוני, אז השבת את הכפתור באמצעות צביעה באפור. אי אפשר ללחוץ על כפתור שהערך של onPressed שלו הוא null:

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

היזכר במה שבנית: שני מסכים, תג וסכום כולל, כולם קוראים את אותה עגלה, בלי להעביר ערך ממסך למסך. זהו ניהול מצב.

challenge icon

אתגר

בינוני

מתחת לסכום הכולל במסך העגלה, הוסף כפתור Check out:

  • מושבת כשהעגלה ריקה.
  • בלחיצה עליו, הוא מרוקן את העגלה באמצעות המתודה החדשה clear(), מציג SnackBar עם ההודעה Order placed! וחוזר לחנות.

נסו בעצמכם

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 iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה ניהול מצב

תרגלו בעצמכם: קומפיילר Flutter אונליין