Menu
CoddyTech

Sổ tay công thức nấu ăn: Chưa có 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 33 trên 33.

Mở thẻ Yêu thích trước khi thêm mục nào vào danh sách yêu thích: màn hình đang trống, trông như bị lỗi. Danh sách trống cần cho biết vì sao nó trống.

isEmpty của một danh sách có giá trị true khi danh sách không có mục nào. Chọn trang trong build bằng if, trước return:

Widget page;
if (index == 0) {
  page = recipeList(recipes);
} else if (favorites.isEmpty) {
  page = const Center(child: Text('No favorites yet'));
} else {
  page = recipeList(favorites);
}

Biến có kiểu Widget có thể chứa bất kỳ widget nào, vì vậy page là một danh sách ở nhánh này và một thông báo ở nhánh khác. Sau đó, phần thân của Scaffold là page.

challenge icon

Thử thách

Dễ

Khi thẻ Favorites không có gì để hiển thị, hãy hiển thị No favorites yet ở giữa màn hình, với cỡ chữ 18, thay cho danh sách trống. Khi bạn đánh dấu yêu thích một công thức, thẻ này sẽ liệt kê lại công thức đó.

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']),
  ];
  int index = 0;

  Widget recipeList(List<Recipe> shown) {
    return ListView.builder(
      itemCount: shown.length,
      itemBuilder: (context, i) {
        final Recipe recipe = shown[i];
        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;
              });
            },
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    final List<Recipe> favorites = recipes.where((recipe) => recipe.favorite).toList();
    return Scaffold(
      appBar: AppBar(title: const Text('Recipe Book')),
      body: index == 0 ? recipeList(recipes) : recipeList(favorites),
      bottomNavigationBar: NavigationBar(
        selectedIndex: index,
        onDestinationSelected: (int i) {
          setState(() {
            index = i;
          });
        },
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.menu_book_outlined),
            selectedIcon: Icon(Icons.menu_book),
            label: 'Recipes',
          ),
          NavigationDestination(
            icon: Icon(Icons.star_border),
            selectedIcon: Icon(Icons.star),
            label: 'Favorites',
          ),
        ],
      ),
    );
  }
}

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