diff --git a/lib/data/dummy_data.dart b/lib/data/dummy_data.dart index 2917e7f..b6351ea 100644 --- a/lib/data/dummy_data.dart +++ b/lib/data/dummy_data.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_navigation/models/category.dart'; +import 'package:flutter_navigation/models/meal.dart'; +// Constants in Dart should be written in lowerCamelcase. const availableCategories = [ Category( id: 'c1', @@ -53,4 +55,355 @@ const availableCategories = [ title: 'Summer', color: Colors.teal, ), +]; + +const dummyMeals = [ + Meal( + id: 'm1', + categories: [ + 'c1', + 'c2', + ], + title: 'Spaghetti with Tomato Sauce', + affordability: Affordability.affordable, + complexity: Complexity.simple, + imageUrl: + 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/20/Spaghetti_Bolognese_mit_Parmesan_oder_Grana_Padano.jpg/800px-Spaghetti_Bolognese_mit_Parmesan_oder_Grana_Padano.jpg', + duration: 20, + ingredients: [ + '4 Tomatoes', + '1 Tablespoon of Olive Oil', + '1 Onion', + '250g Spaghetti', + 'Spices', + 'Cheese (optional)' + ], + steps: [ + 'Cut the tomatoes and the onion into small pieces.', + 'Boil some water - add salt to it once it boils.', + 'Put the spaghetti into the boiling water - they should be done in about 10 to 12 minutes.', + 'In the meantime, heaten up some olive oil and add the cut onion.', + 'After 2 minutes, add the tomato pieces, salt, pepper and your other spices.', + 'The sauce will be done once the spaghetti are.', + 'Feel free to add some cheese on top of the finished dish.' + ], + isGlutenFree: false, + isVegan: true, + isVegetarian: true, + isLactoseFree: true, + ), + Meal( + id: 'm2', + categories: [ + 'c2', + ], + title: 'Toast Hawaii', + affordability: Affordability.affordable, + complexity: Complexity.simple, + imageUrl: + 'https://cdn.pixabay.com/photo/2018/07/11/21/51/toast-3532016_1280.jpg', + duration: 10, + ingredients: [ + '1 Slice White Bread', + '1 Slice Ham', + '1 Slice Pineapple', + '1-2 Slices of Cheese', + 'Butter' + ], + steps: [ + 'Butter one side of the white bread', + 'Layer ham, the pineapple and cheese on the white bread', + 'Bake the toast for round about 10 minutes in the oven at 200°C' + ], + isGlutenFree: false, + isVegan: false, + isVegetarian: false, + isLactoseFree: false, + ), + Meal( + id: 'm3', + categories: [ + 'c2', + 'c3', + ], + title: 'Classic Hamburger', + affordability: Affordability.pricey, + complexity: Complexity.simple, + imageUrl: + 'https://cdn.pixabay.com/photo/2014/10/23/18/05/burger-500054_1280.jpg', + duration: 45, + ingredients: [ + '300g Cattle Hack', + '1 Tomato', + '1 Cucumber', + '1 Onion', + 'Ketchup', + '2 Burger Buns' + ], + steps: [ + 'Form 2 patties', + 'Fry the patties for c. 4 minutes on each side', + 'Quickly fry the buns for c. 1 minute on each side', + 'Bruch buns with ketchup', + 'Serve burger with tomato, cucumber and onion' + ], + isGlutenFree: false, + isVegan: false, + isVegetarian: false, + isLactoseFree: true, + ), + Meal( + id: 'm4', + categories: [ + 'c4', + ], + title: 'Wiener Schnitzel', + affordability: Affordability.luxurious, + complexity: Complexity.challenging, + imageUrl: + 'https://cdn.pixabay.com/photo/2018/03/31/19/29/schnitzel-3279045_1280.jpg', + duration: 60, + ingredients: [ + '8 Veal Cutlets', + '4 Eggs', + '200g Bread Crumbs', + '100g Flour', + '300ml Butter', + '100g Vegetable Oil', + 'Salt', + 'Lemon Slices' + ], + steps: [ + 'Tenderize the veal to about 2–4mm, and salt on both sides.', + 'On a flat plate, stir the eggs briefly with a fork.', + 'Lightly coat the cutlets in flour then dip into the egg, and finally, coat in breadcrumbs.', + 'Heat the butter and oil in a large pan (allow the fat to get very hot) and fry the schnitzels until golden brown on both sides.', + 'Make sure to toss the pan regularly so that the schnitzels are surrounded by oil and the crumbing becomes ‘fluffy’.', + 'Remove, and drain on kitchen paper. Fry the parsley in the remaining oil and drain.', + 'Place the schnitzels on awarmed plate and serve garnishedwith parsley and slices of lemon.' + ], + isGlutenFree: false, + isVegan: false, + isVegetarian: false, + isLactoseFree: false, + ), + Meal( + id: 'm5', + categories: [ + 'c2' + 'c5', + 'c10', + ], + title: 'Salad with Smoked Salmon', + affordability: Affordability.luxurious, + complexity: Complexity.simple, + imageUrl: + 'https://cdn.pixabay.com/photo/2016/10/25/13/29/smoked-salmon-salad-1768890_1280.jpg', + duration: 15, + ingredients: [ + 'Arugula', + 'Lamb\'s Lettuce', + 'Parsley', + 'Fennel', + '200g Smoked Salmon', + 'Mustard', + 'Balsamic Vinegar', + 'Olive Oil', + 'Salt and Pepper' + ], + steps: [ + 'Wash and cut salad and herbs', + 'Dice the salmon', + 'Process mustard, vinegar and olive oil into a dessing', + 'Prepare the salad', + 'Add salmon cubes and dressing' + ], + isGlutenFree: true, + isVegan: false, + isVegetarian: true, + isLactoseFree: true, + ), + Meal( + id: 'm6', + categories: [ + 'c6', + 'c10', + ], + title: 'Delicious Orange Mousse', + affordability: Affordability.affordable, + complexity: Complexity.hard, + imageUrl: + 'https://cdn.pixabay.com/photo/2017/05/01/05/18/pastry-2274750_1280.jpg', + duration: 240, + ingredients: [ + '4 Sheets of Gelatine', + '150ml Orange Juice', + '80g Sugar', + '300g Yoghurt', + '200g Cream', + 'Orange Peel', + ], + steps: [ + 'Dissolve gelatine in pot', + 'Add orange juice and sugar', + 'Take pot off the stove', + 'Add 2 tablespoons of yoghurt', + 'Stir gelatin under remaining yoghurt', + 'Cool everything down in the refrigerator', + 'Whip the cream and lift it under die orange mass', + 'Cool down again for at least 4 hours', + 'Serve with orange peel', + ], + isGlutenFree: true, + isVegan: false, + isVegetarian: true, + isLactoseFree: false, + ), + Meal( + id: 'm7', + categories: [ + 'c7', + ], + title: 'Pancakes', + affordability: Affordability.affordable, + complexity: Complexity.simple, + imageUrl: + 'https://cdn.pixabay.com/photo/2018/07/10/21/23/pancake-3529653_1280.jpg', + duration: 20, + ingredients: [ + '1 1/2 Cups all-purpose Flour', + '3 1/2 Teaspoons Baking Powder', + '1 Teaspoon Salt', + '1 Tablespoon White Sugar', + '1 1/4 cups Milk', + '1 Egg', + '3 Tablespoons Butter, melted', + ], + steps: [ + 'In a large bowl, sift together the flour, baking powder, salt and sugar.', + 'Make a well in the center and pour in the milk, egg and melted butter; mix until smooth.', + 'Heat a lightly oiled griddle or frying pan over medium high heat.', + 'Pour or scoop the batter onto the griddle, using approximately 1/4 cup for each pancake. Brown on both sides and serve hot.' + ], + isGlutenFree: true, + isVegan: false, + isVegetarian: true, + isLactoseFree: false, + ), + Meal( + id: 'm8', + categories: [ + 'c8', + ], + title: 'Creamy Indian Chicken Curry', + affordability: Affordability.pricey, + complexity: Complexity.challenging, + imageUrl: + 'https://cdn.pixabay.com/photo/2018/06/18/16/05/indian-food-3482749_1280.jpg', + duration: 35, + ingredients: [ + '4 Chicken Breasts', + '1 Onion', + '2 Cloves of Garlic', + '1 Piece of Ginger', + '4 Tablespoons Almonds', + '1 Teaspoon Cayenne Pepper', + '500ml Coconut Milk', + ], + steps: [ + 'Slice and fry the chicken breast', + 'Process onion, garlic and ginger into paste and sauté everything', + 'Add spices and stir fry', + 'Add chicken breast + 250ml of water and cook everything for 10 minutes', + 'Add coconut milk', + 'Serve with rice' + ], + isGlutenFree: true, + isVegan: false, + isVegetarian: false, + isLactoseFree: true, + ), + Meal( + id: 'm9', + categories: [ + 'c9', + ], + title: 'Chocolate Souffle', + affordability: Affordability.affordable, + complexity: Complexity.hard, + imageUrl: + 'https://cdn.pixabay.com/photo/2014/08/07/21/07/souffle-412785_1280.jpg', + duration: 45, + ingredients: [ + '1 Teaspoon melted Butter', + '2 Tablespoons white Sugar', + '2 Ounces 70% dark Chocolate, broken into pieces', + '1 Tablespoon Butter', + '1 Tablespoon all-purpose Flour', + '4 1/3 tablespoons cold Milk', + '1 Pinch Salt', + '1 Pinch Cayenne Pepper', + '1 Large Egg Yolk', + '2 Large Egg Whites', + '1 Pinch Cream of Tartar', + '1 Tablespoon white Sugar', + ], + steps: [ + 'Preheat oven to 190°C. Line a rimmed baking sheet with parchment paper.', + 'Brush bottom and sides of 2 ramekins lightly with 1 teaspoon melted butter; cover bottom and sides right up to the rim.', + 'Add 1 tablespoon white sugar to ramekins. Rotate ramekins until sugar coats all surfaces.', + 'Place chocolate pieces in a metal mixing bowl.', + 'Place bowl over a pan of about 3 cups hot water over low heat.', + 'Melt 1 tablespoon butter in a skillet over medium heat. Sprinkle in flour. Whisk until flour is incorporated into butter and mixture thickens.', + 'Whisk in cold milk until mixture becomes smooth and thickens. Transfer mixture to bowl with melted chocolate.', + 'Add salt and cayenne pepper. Mix together thoroughly. Add egg yolk and mix to combine.', + 'Leave bowl above the hot (not simmering) water to keep chocolate warm while you whip the egg whites.', + 'Place 2 egg whites in a mixing bowl; add cream of tartar. Whisk until mixture begins to thicken and a drizzle from the whisk stays on the surface about 1 second before disappearing into the mix.', + 'Add 1/3 of sugar and whisk in. Whisk in a bit more sugar about 15 seconds.', + 'whisk in the rest of the sugar. Continue whisking until mixture is about as thick as shaving cream and holds soft peaks, 3 to 5 minutes.', + 'Transfer a little less than half of egg whites to chocolate.', + 'Mix until egg whites are thoroughly incorporated into the chocolate.', + 'Add the rest of the egg whites; gently fold into the chocolate with a spatula, lifting from the bottom and folding over.', + 'Stop mixing after the egg white disappears. Divide mixture between 2 prepared ramekins. Place ramekins on prepared baking sheet.', + 'Bake in preheated oven until scuffles are puffed and have risen above the top of the rims, 12 to 15 minutes.', + ], + isGlutenFree: true, + isVegan: false, + isVegetarian: true, + isLactoseFree: false, + ), + Meal( + id: 'm10', + categories: [ + 'c2', + 'c5', + 'c10', + ], + title: 'Asparagus Salad with Cherry Tomatoes', + affordability: Affordability.luxurious, + complexity: Complexity.simple, + imageUrl: + 'https://cdn.pixabay.com/photo/2018/04/09/18/26/asparagus-3304997_1280.jpg', + duration: 30, + ingredients: [ + 'White and Green Asparagus', + '30g Pine Nuts', + '300g Cherry Tomatoes', + 'Salad', + 'Salt, Pepper and Olive Oil' + ], + steps: [ + 'Wash, peel and cut the asparagus', + 'Cook in salted water', + 'Salt and pepper the asparagus', + 'Roast the pine nuts', + 'Halve the tomatoes', + 'Mix with asparagus, salad and dressing', + 'Serve with Baguette' + ], + isGlutenFree: true, + isVegan: true, + isVegetarian: true, + isLactoseFree: true, + ), ]; \ No newline at end of file diff --git a/lib/models/meal.dart b/lib/models/meal.dart new file mode 100644 index 0000000..6f4ceb0 --- /dev/null +++ b/lib/models/meal.dart @@ -0,0 +1,43 @@ +enum Complexity { + simple, + challenging, + hard, +} + +enum Affordability { + affordable, + pricey, + luxurious, +} + +class Meal { + const Meal({ + required this.id, + required this.categories, + required this.title, + required this.imageUrl, + required this.ingredients, + required this.steps, + required this.duration, + required this.complexity, + required this.affordability, + required this.isGlutenFree, + required this.isLactoseFree, + required this.isVegan, + required this.isVegetarian, + }); + + final String id; + final List categories; + final String title; + final String imageUrl; + final List ingredients; + final List steps; + final int duration; + final Complexity complexity; + final Affordability affordability; + final bool isGlutenFree; + final bool isLactoseFree; + final bool isVegan; + final bool isVegetarian; +} \ No newline at end of file diff --git a/lib/screens/categories.dart b/lib/screens/categories.dart index 97063fe..8ebbc00 100644 --- a/lib/screens/categories.dart +++ b/lib/screens/categories.dart @@ -1,11 +1,26 @@ import 'package:flutter/material.dart'; import 'package:flutter_navigation/data/dummy_data.dart'; +import 'package:flutter_navigation/screens/meals.dart'; +import '../models/category.dart'; import '../widgets/category_grid_item.dart'; class CategoriesScreen extends StatelessWidget { const CategoriesScreen({super.key}); + void _selectCategory(BuildContext context, Category category) { + final filteredMeals = dummyMeals.where((meal) => meal.categories.contains(category.id)).toList(); + + Navigator.of(context).push( + MaterialPageRoute( + builder: (ctx) => MealsScreen( + title: category.title, + meals: filteredMeals, + ), + ), + ); //Navigator.push(context, route); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -22,7 +37,12 @@ class CategoriesScreen extends StatelessWidget { ), children: [ for (final category in availableCategories) - CategoryGridItem(category: category) + CategoryGridItem( + category: category, + onSelectCategory: () { + _selectCategory(context, category); + }, + ) ], ), ); diff --git a/lib/screens/meals.dart b/lib/screens/meals.dart new file mode 100644 index 0000000..f9a147b --- /dev/null +++ b/lib/screens/meals.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_navigation/widgets/meal_item.dart'; + +import '../models/meal.dart'; + +class MealsScreen extends StatelessWidget { + const MealsScreen({super.key, required this.title, required this.meals}); + + final String title; + final List meals; + + @override + Widget build(BuildContext context) { + Widget content = Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Uh oh ... nothing here!', + style: Theme.of(context).textTheme.headlineLarge!.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + ), + const SizedBox(height: 16), + Text( + 'Try selecting a different category!', + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + ), + ], + ), + ); + + if (meals.isNotEmpty) { + content = ListView.builder( + itemCount: meals.length, + itemBuilder: (ctx, index) => MealItem(meal: meals[index]), + ); + } + return Scaffold( + appBar: AppBar( + title: Text(title), + ), + body: content, + ); + } +} diff --git a/lib/widgets/category_grid_item.dart b/lib/widgets/category_grid_item.dart index c55d463..dcac7e1 100644 --- a/lib/widgets/category_grid_item.dart +++ b/lib/widgets/category_grid_item.dart @@ -3,14 +3,19 @@ import 'package:flutter/material.dart'; import '../models/category.dart'; class CategoryGridItem extends StatelessWidget { - const CategoryGridItem({super.key, required this.category}); + const CategoryGridItem({ + super.key, + required this.category, + required this.onSelectCategory, + }); final Category category; + final void Function() onSelectCategory; @override Widget build(BuildContext context) { return InkWell( - onTap: () {}, + onTap: onSelectCategory, splashColor: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(16), child: Container( diff --git a/lib/widgets/meal_item.dart b/lib/widgets/meal_item.dart new file mode 100644 index 0000000..275ee62 --- /dev/null +++ b/lib/widgets/meal_item.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_navigation/models/meal.dart'; +import 'package:transparent_image/transparent_image.dart'; + +class MealItem extends StatelessWidget { + const MealItem({ + super.key, + required this.meal, + }); + + final Meal meal; + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.all(8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + clipBehavior: Clip.hardEdge, + elevation: 2, + child: InkWell( + onTap: () {}, + child: Stack( + children: [ + FadeInImage( + placeholder: MemoryImage(kTransparentImage), + image: NetworkImage(meal.imageUrl), + fit: BoxFit.cover, + height: 200, + width: double.infinity, + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + color: Colors.black54, + padding: const EdgeInsets.symmetric( + vertical: 6, + horizontal: 44, + ), + child: Column( + children: [ + Text( + meal.title, + maxLines: 2, + textAlign: TextAlign.center, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 12), + Row( + children: [], + ), + ], + ), + ), + ) + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 952eddc..5363d81 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -272,6 +272,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + transparent_image: + dependency: "direct main" + description: + name: transparent_image + sha256: e8991d955a2094e197ca24c645efec2faf4285772a4746126ca12875e54ca02f + url: "https://pub.dev" + source: hosted + version: "2.0.1" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6ac4d5e..ef6ecb6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 google_fonts: ^5.1.0 + transparent_image: ^2.0.1 dev_dependencies: flutter_test: