From 7e09c1f81fcb07215584ef94d8d6d904f3734472 Mon Sep 17 00:00:00 2001 From: Alysson Date: Tue, 2 May 2023 15:23:49 +0200 Subject: [PATCH] feat(): toggle favorite meals --- lib/main.dart | 3 +- lib/screens/CategoriesScreen.dart | 51 ++++++++++---------- lib/screens/MealsScreen.dart | 42 ++++++++++++---- lib/screens/meal_details.dart | 80 +++++++++++++++++++++++++++++++ lib/screens/tabs.dart | 70 +++++++++++++++++++++++++++ lib/widgets/MealItem.dart | 7 ++- 6 files changed, 215 insertions(+), 38 deletions(-) create mode 100644 lib/screens/meal_details.dart create mode 100644 lib/screens/tabs.dart diff --git a/lib/main.dart b/lib/main.dart index ca310a0..630fa44 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:meals/screens/CategoriesScreen.dart'; +import 'package:meals/screens/tabs.dart'; final theme = ThemeData( useMaterial3: true, @@ -22,7 +23,7 @@ class App extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( theme: theme, - home: const CategoriesScreen(), + home: const TabsScreen(), ); } } diff --git a/lib/screens/CategoriesScreen.dart b/lib/screens/CategoriesScreen.dart index 433af26..8340841 100644 --- a/lib/screens/CategoriesScreen.dart +++ b/lib/screens/CategoriesScreen.dart @@ -6,7 +6,9 @@ import 'package:meals/screens/MealsScreen.dart'; import 'package:meals/widgets/CategoryGridItem.dart'; class CategoriesScreen extends StatelessWidget { - const CategoriesScreen({super.key}); + final void Function(Meal meal) onToggleFavorite; + + const CategoriesScreen({super.key, required this.onToggleFavorite}); void _selectCategory(BuildContext context, Category category) { final meals = dummyMeals @@ -17,34 +19,31 @@ class CategoriesScreen extends StatelessWidget { context, MaterialPageRoute( builder: (ctx) => - MealsScreen(title: category.title, meals: meals))); + MealsScreen(title: category.title, + meals: meals, + onToggleFavorite: onToggleFavorite,))); } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Pick your category'), - ), - body: GridView( - padding: const EdgeInsets.all(24), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - childAspectRatio: 1.5, - crossAxisSpacing: 20, - mainAxisSpacing: 20, - ), - children: [ - for (final category in availableCategories) - CategoryGridItem( - category: category, - onClickCallback: () { - _selectCategory( - context, - category, - ); - }) - ]), - ); + return GridView( + padding: const EdgeInsets.all(24), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 1.5, + crossAxisSpacing: 20, + mainAxisSpacing: 20, + ), + children: [ + for (final category in availableCategories) + CategoryGridItem( + category: category, + onClickCallback: () { + _selectCategory( + context, + category, + ); + }) + ]); } } diff --git a/lib/screens/MealsScreen.dart b/lib/screens/MealsScreen.dart index d20c10f..4cbf0c0 100644 --- a/lib/screens/MealsScreen.dart +++ b/lib/screens/MealsScreen.dart @@ -1,12 +1,29 @@ import 'package:flutter/material.dart'; import 'package:meals/models/Meal.dart'; +import 'package:meals/screens/meal_details.dart'; import 'package:meals/widgets/MealItem.dart'; class MealsScreen extends StatelessWidget { - final String title; + final String? title; final List meals; + final void Function(Meal meal) onToggleFavorite; - const MealsScreen({super.key, required this.title, required this.meals}); + const MealsScreen( + {super.key, + this.title, + required this.meals, + required this.onToggleFavorite}); + + void _selectMeal(BuildContext buildContext, Meal meal) { + Navigator.push( + buildContext, + MaterialPageRoute( + builder: (ctx) => MealDetailScreen( + meal: meal, + onToggleFavorite: onToggleFavorite, + ), + )); + } @override Widget build(BuildContext context) { @@ -34,16 +51,23 @@ class MealsScreen extends StatelessWidget { ) : ListView.builder( itemBuilder: (BuildContext context, int index) { - return MealItem(meal: meals[index]); + return MealItem( + meal: meals[index], + callbackClickOnMeal: (meal) { + _selectMeal(context, meal); + }, + ); }, itemCount: meals.length, ); - return Scaffold( - appBar: AppBar( - title: Text(title), - ), - body: content, - ); + return title == null + ? content + : Scaffold( + appBar: AppBar( + title: Text(title!), + ), + body: content, + ); } } diff --git a/lib/screens/meal_details.dart b/lib/screens/meal_details.dart new file mode 100644 index 0000000..419902f --- /dev/null +++ b/lib/screens/meal_details.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; + +import '../models/Meal.dart'; + +class MealDetailScreen extends StatelessWidget { + final Meal meal; + final void Function(Meal meal) onToggleFavorite; + + const MealDetailScreen( + {Key? key, required this.meal, required this.onToggleFavorite}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(meal.title), + actions: [ + IconButton( + onPressed: () => onToggleFavorite(meal), + icon: const Icon(Icons.star)) + ], + ), + body: SingleChildScrollView( + child: Column( + children: [ + Image.network( + meal.imageUrl, + width: double.infinity, + height: 300, + fit: BoxFit.cover, + ), + const SizedBox( + height: 14, + ), + Text( + 'Ingredients', + style: Theme.of(context).textTheme.titleLarge!.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox( + height: 14, + ), + for (final ingredient in meal.ingredients) + Text( + ingredient, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onBackground), + ), + const SizedBox( + height: 14, + ), + Text( + 'Steps', + style: Theme.of(context).textTheme.titleLarge!.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox( + height: 14, + ), + for (final step in meal.steps) + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Text( + step, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onBackground), + ), + ), + ], + ), + )); + } +} diff --git a/lib/screens/tabs.dart b/lib/screens/tabs.dart new file mode 100644 index 0000000..49a99c9 --- /dev/null +++ b/lib/screens/tabs.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:meals/screens/CategoriesScreen.dart'; +import 'package:meals/screens/MealsScreen.dart'; + +import '../models/Meal.dart'; + +class TabsScreen extends StatefulWidget { + const TabsScreen({Key? key}) : super(key: key); + + @override + State createState() => _TabsScreenState(); +} + +class _TabsScreenState extends State { + int _selectedPageIndex = 0; + final List _favoriteMeals = []; + + void _selectPage(int index) { + setState(() { + _selectedPageIndex = index; + }); + } + + void _toggleFavoriteMeal(Meal meal) { + if (_favoriteMeals.contains(meal)) { + setState(() { + _favoriteMeals.remove(meal); + }); + } else { + setState(() { + _favoriteMeals.add(meal); + }); + } + } + + @override + Widget build(BuildContext context) { + Widget activePage; + String? activePageTitle; + + switch (_selectedPageIndex) { + case 1: + activePageTitle = 'Favorites'; + activePage = MealsScreen( + meals: _favoriteMeals, + onToggleFavorite: _toggleFavoriteMeal, + ); + break; + default: + activePageTitle = 'Pick your category'; + activePage = CategoriesScreen( + onToggleFavorite: _toggleFavoriteMeal, + ); + } + + return Scaffold( + appBar: AppBar(title: Text(activePageTitle)), + body: activePage, + bottomNavigationBar: BottomNavigationBar( + onTap: _selectPage, + currentIndex: _selectedPageIndex, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.set_meal), label: 'Categories'), + BottomNavigationBarItem(icon: Icon(Icons.star), label: 'Favorites'), + ], + ), + ); + } +} diff --git a/lib/widgets/MealItem.dart b/lib/widgets/MealItem.dart index df66c36..2e3c54c 100644 --- a/lib/widgets/MealItem.dart +++ b/lib/widgets/MealItem.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; import 'package:meals/models/Meal.dart'; +import 'package:meals/screens/meal_details.dart'; import 'package:meals/widgets/meal_item%20treat.dart'; import 'package:transparent_image/transparent_image.dart'; class MealItem extends StatelessWidget { final Meal meal; - const MealItem({Key? key, required this.meal}) : super(key: key); + final void Function(Meal meal) callbackClickOnMeal; + + const MealItem({Key? key, required this.meal, required this.callbackClickOnMeal}) : super(key: key); @override Widget build(BuildContext context) { @@ -15,7 +18,7 @@ class MealItem extends StatelessWidget { clipBehavior: Clip.hardEdge, elevation: 2, child: InkWell( - onTap: () {}, + onTap: () => callbackClickOnMeal(meal), child: Stack( children: [ FadeInImage(