feat(): toggle favorite meals

feat/state-management
Alysson H. 2 years ago
parent 083a617c95
commit 7e09c1f81f

@ -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(),
);
}
}

@ -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,
);
})
]);
}
}

@ -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<Meal> 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,
);
}
}

@ -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),
),
),
],
),
));
}
}

@ -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<TabsScreen> createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
int _selectedPageIndex = 0;
final List<Meal> _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'),
],
),
);
}
}

@ -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(

Loading…
Cancel
Save