Recipe Book: Favorites
Part of the Lists and Screens section of Coddy's Flutter journey. Lesson 31 of 33.
Some recipes you cook every week. A heart at the end of each row marks a favorite: an empty heart when it is not, a red filled one when it is.
Whether a recipe is a favorite is part of the recipe, so it becomes a field. Unlike the others it changes, so it is not final, and it starts as false:
bool favorite = false;Data that changes needs setState, so HomeScreen becomes a StatefulWidget. The recipes move out of build into the State class: a list made inside build is made again on every setState, and every heart would empty out.
The heart is an IconButton. Its icon picks the filled or the empty heart with ? :, and pressing it flips the field: recipe.favorite = !recipe.favorite.
Challenge
HardAdd a favorite heart to every recipe:
- give
Recipea fieldbool favorite = false; - turn
HomeScreeninto aStatefulWidgetand move the recipes list into itsStateclass - give each tile a
trailing:IconButtonshowingIcons.favorite_border, or a redIcons.favoritewhen the recipe is a favorite. Pressing it flipsfavorite
Try it yourself
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 StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
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']),
];
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)),
);
},
);
},
),
);
}
}
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;
Recipe({required this.name, required this.minutes, required this.ingredients});
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Lists and Screens
1Scrolling Lists
A List That ScrollsList TilesLeading and TrailingTap a List TileDividers Between Rows4Your Own Classes
A Class for Your DataNamed and Required ParametersFields That Can Be EmptyWidgets From Data With mapFilter a List With whereLook Up in a Map7Project: Recipe Book
Recipe Book: The Recipe ListRecipe Book: The Recipe Screen6Tabs, Drawers and Swipes
Tabs With a Navigation BarFilled Icons for the Open TabA Side DrawerSwipe to DeleteUndo a SwipePractice on your own: Online Flutter compiler