Compare commits

..

No commits in common. '490e0c10cb6ec2ba446e00dfc998b1c9d0cfbb56' and 'bbac96686e93ae8d097fa7f529878bed53f23d3d' have entirely different histories.

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:meals/screens/CategoriesScreen.dart'; import 'package:meals/screens/CategoriesScreen.dart';
import 'package:meals/screens/tabs.dart'; import 'package:meals/screens/tabs.dart';
@ -14,7 +13,7 @@ final theme = ThemeData(
); );
void main() { void main() {
runApp(const ProviderScope(child: App())); runApp(const App());
} }
class App extends StatelessWidget { class App extends StatelessWidget {

@ -1,20 +0,0 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:meals/models/Meal.dart';
class FavoriteMealsNotifier extends StateNotifier<List<Meal>> {
FavoriteMealsNotifier(List<Meal>? initialList) : super(initialList ?? []);
bool toggleMealFavoriteStatus(Meal meal) {
if (state.contains(meal)) {
state = state.where((Meal m) => m.id != meal.id).toList();
return false;
}
state = [...state, meal];
return true;
}
}
final favoriteMealsProvider =
StateNotifierProvider<FavoriteMealsNotifier, List<Meal>>(
(ref) => FavoriteMealsNotifier([]));

@ -1,55 +0,0 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:meals/providers/meals_provider.dart';
import 'package:meals/screens/filters.dart';
enum Filter {
glutenFree,
lactoseFree,
veggie,
vegan,
}
class FiltersNotifier extends StateNotifier<Map<Filter, bool>> {
FiltersNotifier()
: super({
Filter.glutenFree: false,
Filter.lactoseFree: false,
Filter.veggie: false,
Filter.vegan: false,
});
void setFilter(Filter filter, bool isActive) {
state = {
...state,
filter: isActive,
};
}
void setFilters(Map<Filter, bool> newFilters) {
state = newFilters;
}
}
final filtersProvider =
StateNotifierProvider<FiltersNotifier, Map<Filter, bool>>(
(ref) => FiltersNotifier());
final filteredMealsProvider = Provider((ref) {
final meals = ref.watch(mealsProvider);
final activeFilters = ref.watch(filtersProvider);
return meals.where((meal) {
if (activeFilters[Filter.glutenFree]! && !meal.isGlutenFree) {
return false;
}
if (activeFilters[Filter.lactoseFree]! && !meal.isLactoseFree) {
return false;
}
if (activeFilters[Filter.veggie]! && !meal.isVegetarian) {
return false;
}
if (activeFilters[Filter.vegan]! && !meal.isVegan) {
return false;
}
return true;
}).toList();
});

@ -1,4 +0,0 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:meals/data/dummy_categories.dart';
final mealsProvider = Provider((ref) => dummyMeals);

@ -7,11 +7,11 @@ import 'package:meals/widgets/CategoryGridItem.dart';
class CategoriesScreen extends StatelessWidget { class CategoriesScreen extends StatelessWidget {
final List<Meal> availableMeals; final List<Meal> availableMeals;
final void Function(Meal meal) onToggleFavorite;
const CategoriesScreen({ const CategoriesScreen({super.key,
super.key,
required this.availableMeals, required this.availableMeals,
}); required this.onToggleFavorite});
void _selectCategory(BuildContext context, Category category) { void _selectCategory(BuildContext context, Category category) {
final meals = availableMeals final meals = availableMeals
@ -21,9 +21,11 @@ class CategoriesScreen extends StatelessWidget {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (ctx) => MealsScreen( builder: (ctx) =>
MealsScreen(
title: category.title, title: category.title,
meals: meals, meals: meals,
onToggleFavorite: onToggleFavorite,
))); )));
} }

@ -6,12 +6,13 @@ import 'package:meals/widgets/MealItem.dart';
class MealsScreen extends StatelessWidget { class MealsScreen extends StatelessWidget {
final String? title; final String? title;
final List<Meal> meals; final List<Meal> meals;
final void Function(Meal meal) onToggleFavorite;
const MealsScreen({ const MealsScreen(
super.key, {super.key,
this.title, this.title,
required this.meals, required this.meals,
}); required this.onToggleFavorite});
void _selectMeal(BuildContext buildContext, Meal meal) { void _selectMeal(BuildContext buildContext, Meal meal) {
Navigator.push( Navigator.push(
@ -19,6 +20,7 @@ class MealsScreen extends StatelessWidget {
MaterialPageRoute( MaterialPageRoute(
builder: (ctx) => MealDetailScreen( builder: (ctx) => MealDetailScreen(
meal: meal, meal: meal,
onToggleFavorite: onToggleFavorite,
), ),
)); ));
} }

@ -1,26 +1,65 @@
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:meals/screens/tabs.dart';
import 'package:meals/widgets/main_drawer.dart';
enum Filter {
glutenFree,
lactoseFree,
veggie,
vegan,
}
class FiltersScreen extends StatefulWidget {
final Map<Filter, bool> currentFilters;
const FiltersScreen({Key? key, required this.currentFilters})
: super(key: key);
@override
State<FiltersScreen> createState() => _FiltersScreenState();
}
import '../providers/filters_provider.dart'; class _FiltersScreenState extends State<FiltersScreen> {
bool _glutenFreeFilterSet = false;
bool _lactoseFreeFilterSet = false;
bool _veggieFilterSet = false;
bool _veganFilterSet = false;
class FiltersScreen extends ConsumerWidget { @override
const FiltersScreen({Key? key}) : super(key: key); void initState() {
super.initState();
setState(() {
_glutenFreeFilterSet = widget.currentFilters[Filter.glutenFree]!;
_lactoseFreeFilterSet = widget.currentFilters[Filter.lactoseFree]!;
_veggieFilterSet = widget.currentFilters[Filter.veggie]!;
_veganFilterSet = widget.currentFilters[Filter.vegan]!;
});
}
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Your filters'), title: const Text('Your filters'),
), ),
body: Column( body: WillPopScope(
onWillPop: () async {
Navigator.of(context).pop({
Filter.glutenFree: _glutenFreeFilterSet,
Filter.lactoseFree: _lactoseFreeFilterSet,
Filter.veggie: _veggieFilterSet,
Filter.vegan: _veganFilterSet,
});
return false;
},
child: Column(
children: [ children: [
SwitchListTile( SwitchListTile(
value: ref.watch(filtersProvider)[Filter.glutenFree]!, value: _glutenFreeFilterSet,
onChanged: (checked) => ref onChanged: (checked) => setState(() {
.read(filtersProvider.notifier) _glutenFreeFilterSet = checked;
.setFilter(Filter.glutenFree, checked), }),
title: Text( title: Text(
'Gluten free', 'Gluten free',
style: Theme.of(context).textTheme.titleLarge!.copyWith( style: Theme.of(context).textTheme.titleLarge!.copyWith(
@ -35,10 +74,10 @@ class FiltersScreen extends ConsumerWidget {
contentPadding: const EdgeInsets.only(left: 34, right: 22), contentPadding: const EdgeInsets.only(left: 34, right: 22),
), ),
SwitchListTile( SwitchListTile(
value: ref.watch(filtersProvider)[Filter.lactoseFree]!, value: _lactoseFreeFilterSet,
onChanged: (checked) => ref onChanged: (checked) => setState(() {
.read(filtersProvider.notifier) _lactoseFreeFilterSet = checked;
.setFilter(Filter.lactoseFree, checked), }),
title: Text( title: Text(
'Lactose free', 'Lactose free',
style: Theme.of(context).textTheme.titleLarge!.copyWith( style: Theme.of(context).textTheme.titleLarge!.copyWith(
@ -53,10 +92,10 @@ class FiltersScreen extends ConsumerWidget {
contentPadding: const EdgeInsets.only(left: 34, right: 22), contentPadding: const EdgeInsets.only(left: 34, right: 22),
), ),
SwitchListTile( SwitchListTile(
value: ref.watch(filtersProvider)[Filter.veggie]!, value: _veggieFilterSet,
onChanged: (checked) => ref onChanged: (checked) => setState(() {
.read(filtersProvider.notifier) _veggieFilterSet = checked;
.setFilter(Filter.veggie, checked), }),
title: Text( title: Text(
'Veggie', 'Veggie',
style: Theme.of(context).textTheme.titleLarge!.copyWith( style: Theme.of(context).textTheme.titleLarge!.copyWith(
@ -71,10 +110,10 @@ class FiltersScreen extends ConsumerWidget {
contentPadding: const EdgeInsets.only(left: 34, right: 22), contentPadding: const EdgeInsets.only(left: 34, right: 22),
), ),
SwitchListTile( SwitchListTile(
value: ref.watch(filtersProvider)[Filter.vegan]!, value: _veganFilterSet,
onChanged: (checked) => ref onChanged: (checked) => setState(() {
.read(filtersProvider.notifier) _veganFilterSet = checked;
.setFilter(Filter.vegan, checked), }),
title: Text( title: Text(
'Vegan', 'Vegan',
style: Theme.of(context).textTheme.titleLarge!.copyWith( style: Theme.of(context).textTheme.titleLarge!.copyWith(
@ -89,6 +128,7 @@ class FiltersScreen extends ConsumerWidget {
contentPadding: const EdgeInsets.only(left: 34, right: 22), contentPadding: const EdgeInsets.only(left: 34, right: 22),
) )
], ],
),
)); ));
} }
} }

@ -1,36 +1,24 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:meals/providers/favorites_provider.dart';
import '../models/Meal.dart'; import '../models/Meal.dart';
class MealDetailScreen extends ConsumerWidget { class MealDetailScreen extends StatelessWidget {
final Meal meal; final Meal meal;
final void Function(Meal meal) onToggleFavorite;
const MealDetailScreen({Key? key, required this.meal}) : super(key: key); const MealDetailScreen(
{Key? key, required this.meal, required this.onToggleFavorite})
: super(key: key);
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context) {
final favoriteMeals = ref.watch(favoriteMealsProvider);
final isIntoFavorite = favoriteMeals.contains(meal);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(meal.title), title: Text(meal.title),
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () => onToggleFavorite(meal),
final wasAdded = ref icon: const Icon(Icons.star))
.read(favoriteMealsProvider.notifier)
.toggleMealFavoriteStatus(meal);
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(wasAdded
? 'Meal has been added to your favorite.'
: 'Meal is no longer as a favorite.')));
},
icon: Icon(isIntoFavorite ? Icons.star : Icons.star_border))
], ],
), ),
body: SingleChildScrollView( body: SingleChildScrollView(

@ -1,22 +1,36 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:meals/data/dummy_categories.dart';
import 'package:meals/providers/favorites_provider.dart';
import 'package:meals/screens/CategoriesScreen.dart'; import 'package:meals/screens/CategoriesScreen.dart';
import 'package:meals/screens/MealsScreen.dart'; import 'package:meals/screens/MealsScreen.dart';
import 'package:meals/screens/filters.dart'; import 'package:meals/screens/filters.dart';
import 'package:meals/widgets/main_drawer.dart'; import 'package:meals/widgets/main_drawer.dart';
import '../providers/filters_provider.dart'; import '../models/Meal.dart';
class TabsScreen extends ConsumerStatefulWidget { const kDefaultFilters = {
Filter.glutenFree: false,
Filter.lactoseFree: false,
Filter.veggie: false,
Filter.vegan: false,
};
class TabsScreen extends StatefulWidget {
const TabsScreen({Key? key}) : super(key: key); const TabsScreen({Key? key}) : super(key: key);
@override @override
ConsumerState<TabsScreen> createState() => _TabsScreenState(); State<TabsScreen> createState() => _TabsScreenState();
} }
class _TabsScreenState extends ConsumerState<TabsScreen> { class _TabsScreenState extends State<TabsScreen> {
int _selectedPageIndex = 0; int _selectedPageIndex = 0;
final List<Meal> _favoriteMeals = [];
Map<Filter, bool> _selectedFilters = kDefaultFilters;
void _showInfoMessage(String message) {
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
}
void _selectPage(int index) { void _selectPage(int index) {
setState(() { setState(() {
@ -24,13 +38,33 @@ class _TabsScreenState extends ConsumerState<TabsScreen> {
}); });
} }
void _toggleFavoriteMeal(Meal meal) {
if (_favoriteMeals.contains(meal)) {
setState(() {
_favoriteMeals.remove(meal);
});
_showInfoMessage('Meal is no longer a favorite');
} else {
setState(() {
_favoriteMeals.add(meal);
_showInfoMessage('Meal has been added to your favorites');
});
}
}
void _setScreen(String identifier) async { void _setScreen(String identifier) async {
Navigator.of(context).pop(); Navigator.of(context).pop();
switch (identifier) { switch (identifier) {
case 'filters': case 'filters':
await Navigator.of(context).push<Map<Filter, bool>>( final results = await Navigator.of(context)
MaterialPageRoute(builder: (ctx) => const FiltersScreen())); .push<Map<Filter, bool>>(MaterialPageRoute(
builder: (ctx) => FiltersScreen(
currentFilters: _selectedFilters,
)));
setState(() {
_selectedFilters = results ?? kDefaultFilters;
});
break; break;
} }
} }
@ -39,19 +73,35 @@ class _TabsScreenState extends ConsumerState<TabsScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget activePage; Widget activePage;
String? activePageTitle; String? activePageTitle;
final availableMeals = ref.watch(filteredMealsProvider); final availableMeals = dummyMeals.where((meal) {
if (_selectedFilters[Filter.glutenFree]! && !meal.isGlutenFree) {
return false;
}
if (_selectedFilters[Filter.lactoseFree]! && !meal.isLactoseFree) {
return false;
}
if (_selectedFilters[Filter.veggie]! && !meal.isVegetarian) {
return false;
}
if (_selectedFilters[Filter.vegan]! && !meal.isVegan) {
return false;
}
return true;
}).toList();
switch (_selectedPageIndex) { switch (_selectedPageIndex) {
case 1: case 1:
activePageTitle = 'Favorites'; activePageTitle = 'Favorites';
activePage = MealsScreen( activePage = MealsScreen(
meals: ref.watch(favoriteMealsProvider), meals: _favoriteMeals,
onToggleFavorite: _toggleFavoriteMeal,
); );
break; break;
default: default:
activePageTitle = 'Pick your category'; activePageTitle = 'Pick your category';
activePage = CategoriesScreen( activePage = CategoriesScreen(
availableMeals: availableMeals, availableMeals: availableMeals,
onToggleFavorite: _toggleFavoriteMeal,
); );
} }

@ -94,14 +94,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
flutter_riverpod:
dependency: "direct main"
description:
name: flutter_riverpod
sha256: b83ac5827baadefd331ea1d85110f34645827ea234ccabf53a655f41901a9bf4
url: "https://pub.dev"
source: hosted
version: "2.3.6"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -251,14 +243,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.4" version: "4.2.4"
riverpod:
dependency: transitive
description:
name: riverpod
sha256: "80e48bebc83010d5e67a11c9514af6b44bbac1ec77b4333c8ea65dbc79e2d8ef"
url: "https://pub.dev"
source: hosted
version: "2.3.6"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -280,14 +264,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.0"
state_notifier:
dependency: transitive
description:
name: state_notifier
sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289"
url: "https://pub.dev"
source: hosted
version: "0.7.2+1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:

@ -37,7 +37,6 @@ dependencies:
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
google_fonts: ^4.0.3 google_fonts: ^4.0.3
transparent_image: ^2.0.1 transparent_image: ^2.0.1
flutter_riverpod: ^2.3.6
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

Loading…
Cancel
Save