Menu
CoddyTech

Sổ tay công thức nấu ăn: Tab mục yêu thích

Thuộc phần Danh sách và màn hình trong hành trình Flutter của Coddy. Bài học 32 trên 33.

Trái tim chỉ hữu ích khi bạn có thể tìm lại những món yêu thích. Tab thứ hai, Favorites, chỉ hiển thị các công thức nấu ăn có trái tim màu đỏ.

Cả hai tab đều hiển thị danh sách công thức nấu ăn giống nhau, vì vậy hãy viết danh sách một lần dưới dạng phương thức nhận các công thức cần hiển thị:

Widget recipeList(List<Recipe> shown) {
  return ListView.builder(...);
}

Các món yêu thích là những công thức nấu ăn where favorite có giá trị true. Hãy xác định chúng ở đầu build để tab được cập nhật sau mỗi setState. Sau đó, phần nội dung sẽ hiển thị recipeList(recipes) hoặc recipeList(favorites), tùy theo tab.

challenge icon

Thử thách

Khó

Thêm các thẻ vào sách công thức:

  • chuyển ListView.builder vào phương thức Widget recipeList(List<Recipe> shown) để liệt kê shown
  • thêm NavigationBar với hai thẻ: Recipes (Icons.menu_book_outlined, Icons.menu_book khi được chọn) và Favorites (Icons.star_border, Icons.star khi được chọn), đồng thời giữ index của thẻ trong trạng thái
  • thẻ Recipes liệt kê mọi công thức, thẻ Favorites chỉ liệt kê các công thức yêu thích

Tự làm thử

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

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

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final List<Recipe> recipes = [
    Recipe(name: 'Pancakes', minutes: 20, ingredients: ['Flour', 'Eggs', 'Milk', 'Butter']),
    Recipe(name: 'Tomato Soup', minutes: 35, ingredients: ['Tomatoes', 'Onion', 'Garlic', 'Olive oil']),
    Recipe(name: 'Guacamole', minutes: 10, ingredients: ['Avocados', 'Lime', 'Onion', 'Salt']),
    Recipe(name: 'Fried Rice', minutes: 25, ingredients: ['Rice', 'Eggs', 'Peas', 'Soy sauce']),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Recipe Book')),
      body: ListView.builder(
        itemCount: recipes.length,
        itemBuilder: (context, index) {
          final Recipe recipe = recipes[index];
          return ListTile(
            leading: const Icon(Icons.restaurant_menu),
            title: Text(recipe.name),
            subtitle: Text('${recipe.minutes} min'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => RecipeScreen(recipe: recipe)),
              );
            },
            trailing: IconButton(
              icon: Icon(
                recipe.favorite ? Icons.favorite : Icons.favorite_border,
                color: recipe.favorite ? Colors.red : null,
              ),
              onPressed: () {
                setState(() {
                  recipe.favorite = !recipe.favorite;
                });
              },
            ),
          );
        },
      ),
    );
  }
}

class RecipeScreen extends StatelessWidget {
  const RecipeScreen({super.key, required this.recipe});

  final Recipe recipe;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(recipe.name)),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          Text('Ready in ${recipe.minutes} minutes', style: const TextStyle(fontSize: 18)),
          const SizedBox(height: 24),
          const Text('Ingredients', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
          for (String ingredient in recipe.ingredients)
            ListTile(
              leading: const Icon(Icons.check_circle_outline),
              title: Text(ingredient),
            ),
        ],
      ),
    );
  }
}

class Recipe {
  final String name;
  final int minutes;
  final List<String> ingredients;
  bool favorite = false;

  Recipe({required this.name, required this.minutes, required this.ingredients});
}
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 Danh sách và màn hình

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