Learn how to build and integrate multiple screens in a Flutter Recipe App, including Home, Detail, Add, and Favorites screens, with detailed code examples and navigation strategies.
In this section, we will delve into the process of building the main screens for our Recipe App. This hands-on project will guide you through creating a Home Screen, Recipe Detail Screen, Add Recipe Screen, and Favorites Screen. Each screen will be designed to interact seamlessly with the others, providing a cohesive user experience. We’ll explore how to implement these screens using Flutter’s powerful widget system and navigation capabilities.
The HomeScreen serves as the central hub of our Recipe App, displaying a list of recipes with options to view details or add new recipes. This screen is crucial as it provides users with an overview of available recipes and acts as a gateway to other functionalities.
class HomeScreen extends StatelessWidget {
final List<Recipe> recipes = [
Recipe(
title: 'Spaghetti Bolognese',
description: 'A classic Italian dish...',
imageUrl: 'https://example.com/spaghetti.jpg',
),
Recipe(
title: 'Chicken Curry',
description: 'A flavorful Indian curry...',
imageUrl: 'https://example.com/chicken_curry.jpg',
),
// Add more recipes
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Recipes')),
body: ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
return ListTile(
leading: Image.network(recipes[index].imageUrl, width: 50, height: 50, fit: BoxFit.cover),
title: Text(recipes[index].title),
subtitle: Text(recipes[index].description),
onTap: () {
Navigator.pushNamed(
context,
'/detail',
arguments: recipes[index],
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.pushNamed(context, '/add');
},
child: Icon(Icons.add),
tooltip: 'Add Recipe',
),
);
}
}
class Recipe {
final String title;
final String description;
final String imageUrl;
Recipe({required this.title, required this.description, required this.imageUrl});
}
The RecipeDetailScreen provides users with detailed information about a selected recipe, including ingredients, steps, and images. This screen enhances the user experience by offering comprehensive details about each recipe.
class RecipeDetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final Recipe recipe = ModalRoute.of(context)!.settings.arguments as Recipe;
return Scaffold(
appBar: AppBar(title: Text(recipe.title)),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.network(recipe.imageUrl),
SizedBox(height: 10),
Text(
recipe.description,
style: TextStyle(fontSize: 18),
),
SizedBox(height: 20),
Text(
'Ingredients',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
// List of ingredients
SizedBox(height: 20),
Text(
'Steps',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
// List of steps
],
),
),
),
);
}
}
The AddRecipeScreen allows users to input details for a new recipe, including the title, description, and image URL. This screen is essential for expanding the app’s content and engaging users by letting them contribute their own recipes.
class AddRecipeScreen extends StatefulWidget {
@override
_AddRecipeScreenState createState() => _AddRecipeScreenState();
}
class _AddRecipeScreenState extends State<AddRecipeScreen> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _titleController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
final TextEditingController _imageUrlController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Add New Recipe')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
TextFormField(
controller: _titleController,
decoration: InputDecoration(labelText: 'Recipe Title'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a title';
}
return null;
},
),
SizedBox(height: 10),
TextFormField(
controller: _descriptionController,
decoration: InputDecoration(labelText: 'Description'),
maxLines: 3,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a description';
}
return null;
},
),
SizedBox(height: 10),
TextFormField(
controller: _imageUrlController,
decoration: InputDecoration(labelText: 'Image URL'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an image URL';
}
if (!Uri.parse(value).isAbsolute) {
return 'Please enter a valid URL';
}
return null;
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
Recipe newRecipe = Recipe(
title: _titleController.text,
description: _descriptionController.text,
imageUrl: _imageUrlController.text,
);
// Add recipe to the list or database
Navigator.pop(context);
}
},
child: Text('Add Recipe'),
),
],
),
),
),
),
);
}
@override
void dispose() {
_titleController.dispose();
_descriptionController.dispose();
_imageUrlController.dispose();
super.dispose();
}
}
The FavoritesScreen displays a list of user-marked favorite recipes, allowing easy access and management. This screen enhances user engagement by providing a personalized experience.
class FavoritesScreen extends StatelessWidget {
final List<Recipe> favoriteRecipes = [
// Pre-populated favorite recipes
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Favorites')),
body: ListView.builder(
itemCount: favoriteRecipes.length,
itemBuilder: (context, index) {
return ListTile(
leading: Image.network(favoriteRecipes[index].imageUrl, width: 50, height: 50, fit: BoxFit.cover),
title: Text(favoriteRecipes[index].title),
onTap: () {
Navigator.pushNamed(
context,
'/detail',
arguments: favoriteRecipes[index],
);
},
);
},
),
);
}
}
To better understand the flow and relationship between different screens in our app, let’s use a Mermaid.js diagram:
graph LR A[Building Screens] --> B[Home Screen] A --> C[Recipe Detail Screen] A --> D[Add Recipe Screen] A --> E[Favorites Screen] A --> F[Settings Screen] B --> B1[Recipe List] B --> B2[Add Recipe Button] B1 --> B3[Navigate to Detail] B2 --> B4[Navigate to Add Recipe] D --> D1[Form Fields] D --> D2[Add Button] C --> C1[Display Recipe Details] E --> E2[Favorite Recipe List] F --> F1[App Settings]
Each screen in our Recipe App is designed to interact with the navigation system, allowing users to move seamlessly between different parts of the app. Here’s how you can integrate these screens using Flutter’s navigation capabilities:
As you become more comfortable with building and navigating between screens, consider customizing each screen further. Here are a few ideas to explore:
By experimenting with these features, you’ll gain a deeper understanding of Flutter’s capabilities and how to create a dynamic, user-friendly app.
Building screens in Flutter involves understanding the widget hierarchy and how to effectively use navigation to create a seamless user experience. By following the examples and instructions provided, you can create a robust Recipe App that showcases your skills in Flutter development. Remember to explore and customize the app further, as this is the best way to learn and grow as a developer.