Post Detail

Post #104

Flutter Jour Six Part 1

Flutter Jour Six (Part 1)

DeliMeals

  • 07:45 - 08:30 : Favorites
  • 08:30 - 08:45 : Explication du projet Coiffeur

Code

  1import 'package:delimeals/dummy_data.dart';
  2import 'package:delimeals/models/meal.dart';
  3import 'package:delimeals/screens/category_meals_screen.dart';
  4import 'package:delimeals/screens/meal_detail_screen.dart';
  5import 'package:delimeals/screens/tabs_screen.dart';
  6import 'package:delimeals/widgets/filters_screen.dart';
  7import 'package:flutter/material.dart';
  8
  9void main() {
 10  runApp(MyApp());
 11}
 12
 13class MyApp extends StatefulWidget {
 14  @override
 15  _MyAppState createState() => _MyAppState();
 16}
 17
 18class _MyAppState extends State<MyApp> {
 19  Map<String, bool> _filters = {
 20    'gluten': false,
 21    'lactose': false,
 22    'vegan': false,
 23    'vegetarian': false,
 24  };
 25
 26  List<Meal> _favoriteMeals = [];
 27
 28  List<Meal> _availableMeals = DUMMY_MEALS;
 29
 30  void _setFilters(Map<String, bool> filterData) {
 31    setState(() {
 32      _filters = filterData;
 33
 34      _availableMeals = DUMMY_MEALS.where((meal) {
 35        if (_filters['gluten'] && !meal.isGlutenFree) {
 36          return false;
 37        }
 38        if (_filters['lactose'] && !meal.isLactoseFree) {
 39          return false;
 40        }
 41        if (_filters['vegan'] && !meal.isVegan) {
 42          return false;
 43        }
 44        if (_filters['vegetarian'] && !meal.isVegetarian) {
 45          return false;
 46        }
 47        return true;
 48      }).toList();
 49    });
 50  }
 51
 52  void _toggleFavorite(String mealId) {
 53    final existingIndex =
 54        _favoriteMeals.indexWhere((meal) => meal.id == mealId);
 55
 56    if (existingIndex >= 0) {
 57      setState(() {
 58        _favoriteMeals.removeAt(existingIndex);
 59      });
 60    } else {
 61      setState(() {
 62        _favoriteMeals.add(DUMMY_MEALS.firstWhere((meal) => meal.id == mealId));
 63      });
 64    }
 65  }
 66
 67  bool _isMealFavorite(String id) {
 68    return _favoriteMeals.any((meal) => meal.id == id);
 69  }
 70
 71  @override
 72  Widget build(BuildContext context) {
 73    return MaterialApp(
 74      title: 'DeliMeals',
 75      theme: ThemeData(
 76        primarySwatch: Colors.pink,
 77        accentColor: Colors.amber,
 78        canvasColor: Color.fromRGBO(255, 254, 229, 1),
 79        fontFamily: 'Raleway',
 80        textTheme: TextTheme(
 81          headline6: TextStyle(
 82            fontWeight: FontWeight.bold,
 83          ),
 84        ),
 85      ),
 86      routes: {
 87        '/': (context) => TabsScreen(_favoriteMeals),
 88        CategoryMealsScreen.routeName: (context) =>
 89            CategoryMealsScreen(_availableMeals),
 90        MealDetailScreen.routeName: (context) =>
 91            MealDetailScreen(_toggleFavorite, _isMealFavorite),
 92        FiltersScreen.routeName: (context) =>
 93            FiltersScreen(_filters, _setFilters),
 94      },
 95    );
 96  }
 97}
 98
 99class MyHomePage extends StatefulWidget {
100  @override
101  _MyHomePageState createState() => _MyHomePageState();
102}
103
104class _MyHomePageState extends State<MyHomePage> {
105  @override
106  Widget build(BuildContext context) {
107    return Scaffold(
108      appBar: AppBar(
109        title: Text('DeliMeals'),
110      ),
111      body: Center(
112        child: Text('Navigation Time!'),
113      ),
114    );
115  }
116}
  1import 'package:delimeals/models/meal.dart';
  2import 'package:delimeals/screens/meal_detail_screen.dart';
  3import 'package:flutter/material.dart';
  4
  5class MealItem extends StatelessWidget {
  6  final String id;
  7  final String title;
  8  final String imageUrl;
  9  final int duration;
 10  final Complexity complexity;
 11  final Affordability affordability;
 12
 13  MealItem({
 14    @required this.id,
 15    @required this.title,
 16    @required this.imageUrl,
 17    @required this.duration,
 18    @required this.complexity,
 19    @required this.affordability,
 20  });
 21
 22  String get complexityText {
 23    switch (complexity) {
 24      case Complexity.Simple:
 25        return 'Simple';
 26        break;
 27      case Complexity.Challenging:
 28        return 'Challenging';
 29        break;
 30      case Complexity.Hard:
 31        return 'Hard';
 32        break;
 33      default:
 34        return 'Unknown';
 35    }
 36  }
 37
 38  String get affordabilityText {
 39    switch (affordability) {
 40      case Affordability.Affordable:
 41        return 'Affordable';
 42        break;
 43      case Affordability.Pricey:
 44        return 'Pricey';
 45        break;
 46      case Affordability.Luxurious:
 47        return 'Luxurious';
 48        break;
 49      default:
 50        return 'Unknown';
 51    }
 52  }
 53
 54  void selectMeal(BuildContext context) {
 55    Navigator.of(context).pushNamed(
 56      MealDetailScreen.routeName,
 57      arguments: id,
 58    );
 59  }
 60
 61  @override
 62  Widget build(BuildContext context) {
 63    return InkWell(
 64      onTap: () => selectMeal(context),
 65      child: Card(
 66        shape: RoundedRectangleBorder(
 67          borderRadius: BorderRadius.circular(15),
 68        ),
 69        elevation: 4,
 70        margin: EdgeInsets.all(10),
 71        child: Column(
 72          children: <Widget>[
 73            Stack(
 74              children: <Widget>[
 75                ClipRRect(
 76                  borderRadius: BorderRadius.only(
 77                    topLeft: Radius.circular(15),
 78                    topRight: Radius.circular(15),
 79                  ),
 80                  child: Image.network(
 81                    imageUrl,
 82                    height: 250,
 83                    width: double.infinity,
 84                    fit: BoxFit.cover,
 85                  ),
 86                ),
 87                Positioned(
 88                  bottom: 20,
 89                  right: 10,
 90                  child: Container(
 91                    width: 300,
 92                    color: Colors.black54,
 93                    padding: EdgeInsets.symmetric(
 94                      vertical: 5,
 95                      horizontal: 20,
 96                    ),
 97                    child: Text(
 98                      title,
 99                      style: TextStyle(
100                        fontSize: 26,
101                        color: Colors.white,
102                        fontFamily: 'Raleway',
103                      ),
104                      softWrap: true,
105                      overflow: TextOverflow.fade,
106                    ),
107                  ),
108                ),
109              ],
110            ),
111            Padding(
112              padding: EdgeInsets.all(20),
113              child: Row(
114                mainAxisAlignment: MainAxisAlignment.spaceAround,
115                children: <Widget>[
116                  Row(
117                    children: <Widget>[
118                      Icon(Icons.schedule),
119                      SizedBox(width: 6),
120                      Text('$duration min'),
121                    ],
122                  ),
123                  Row(
124                    children: <Widget>[
125                      Icon(Icons.work),
126                      SizedBox(width: 6),
127                      Text(complexityText),
128                    ],
129                  ),
130                  Row(
131                    children: <Widget>[
132                      Icon(Icons.attach_money),
133                      SizedBox(width: 6),
134                      Text(affordabilityText)
135                    ],
136                  )
137                ],
138              ),
139            ),
140          ],
141        ),
142      ),
143    );
144  }
145}
 1import 'package:delimeals/models/meal.dart';
 2import 'package:delimeals/widgets/main_drawer.dart';
 3import 'package:flutter/material.dart';
 4
 5import 'categories_screen.dart';
 6import 'favorites_screen.dart';
 7
 8class TabsScreen extends StatefulWidget {
 9  List<Meal> favoriteMeals;
10
11  TabsScreen(this.favoriteMeals);
12
13  @override
14  _TabsScreenState createState() => _TabsScreenState();
15}
16
17class _TabsScreenState extends State<TabsScreen> {
18  List<Map<String, Object>> _pages;
19
20  @override
21  initState() {
22    _pages = [
23      {'page': CategoriesScreen(), 'title': 'Categories'},
24      {
25        'page': FavoritesScreen(widget.favoriteMeals),
26        'title': 'Your Favorites'
27      },
28    ];
29    super.initState();
30  }
31
32  int _selectedPageIndex = 0;
33
34  void _selectPage(int index) {
35    setState(() {
36      _selectedPageIndex = index;
37    });
38  }
39
40  @override
41  Widget build(BuildContext context) {
42    return Scaffold(
43      drawer: MainDrawer(),
44      appBar: AppBar(
45        title: Text(_pages[_selectedPageIndex]['title']),
46      ),
47      body: _pages[_selectedPageIndex]['page'],
48      bottomNavigationBar: BottomNavigationBar(
49        onTap: _selectPage,
50        backgroundColor: Theme.of(context).primaryColor,
51        unselectedItemColor: Colors.white,
52        selectedItemColor: Theme.of(context).accentColor,
53        currentIndex: _selectedPageIndex,
54        items: [
55          BottomNavigationBarItem(
56            icon: Icon(Icons.category),
57            title: Text('Categories'),
58          ),
59          BottomNavigationBarItem(
60            icon: Icon(Icons.star),
61            title: Text('Favorites'),
62          ),
63        ],
64      ),
65    );
66  }
67}
  1import 'package:delimeals/dummy_data.dart';
  2import 'package:flutter/material.dart';
  3
  4class MealDetailScreen extends StatelessWidget {
  5  static const routeName = '/meal-detail';
  6
  7  final Function toggleFavorite;
  8  final Function isMealFavorite;
  9
 10  MealDetailScreen(this.toggleFavorite, this.isMealFavorite);
 11
 12  Widget buildSectionTitle(BuildContext context, String text) {
 13    return Container(
 14      margin: EdgeInsets.symmetric(vertical: 10),
 15      child: Text(
 16        text,
 17        style: Theme.of(context).textTheme.headline6,
 18      ),
 19    );
 20  }
 21
 22  Widget buildContainer({Widget child}) {
 23    return Container(
 24      decoration: BoxDecoration(
 25        color: Colors.white,
 26        border: Border.all(color: Colors.grey),
 27        borderRadius: BorderRadius.circular(10),
 28      ),
 29      padding: EdgeInsets.all(10),
 30      margin: EdgeInsets.all(10),
 31      height: 150,
 32      width: 300,
 33      child: child,
 34    );
 35  }
 36
 37  @override
 38  Widget build(BuildContext context) {
 39    final mealId = ModalRoute.of(context).settings.arguments as String;
 40    final selectedMeal = DUMMY_MEALS.firstWhere((meal) => meal.id == mealId);
 41
 42    return Scaffold(
 43      floatingActionButton: FloatingActionButton(
 44        child: Icon(
 45          isMealFavorite(mealId) ? Icons.star : Icons.star_border,
 46        ),
 47        onPressed: () => toggleFavorite(mealId),
 48      ),
 49      appBar: AppBar(
 50        title: Text('${selectedMeal.title}'),
 51      ),
 52      body: SingleChildScrollView(
 53        child: Column(
 54          children: <Widget>[
 55            Container(
 56              height: 300,
 57              width: double.infinity,
 58              child: Image.network(
 59                selectedMeal.imageUrl,
 60                fit: BoxFit.cover,
 61              ),
 62            ),
 63            buildSectionTitle(context, 'Ingredients'),
 64            buildContainer(
 65              child: ListView.builder(
 66                itemBuilder: (context, index) => Card(
 67                  color: Theme.of(context).accentColor,
 68                  child: Padding(
 69                    padding: const EdgeInsets.symmetric(
 70                      vertical: 5,
 71                      horizontal: 10,
 72                    ),
 73                    child: Text(selectedMeal.ingredients[index]),
 74                  ),
 75                ),
 76                itemCount: selectedMeal.ingredients.length,
 77              ),
 78            ),
 79            buildSectionTitle(context, 'Steps'),
 80            buildContainer(
 81              child: ListView.builder(
 82                itemBuilder: (context, index) => Column(
 83                  children: <Widget>[
 84                    ListTile(
 85                      leading: CircleAvatar(
 86                        child: Text('# ${(index + 1)}'),
 87                      ),
 88                      title: Text(
 89                        selectedMeal.steps[index],
 90                      ),
 91                    ),
 92                    Divider(),
 93                  ],
 94                ),
 95                itemCount: selectedMeal.steps.length,
 96              ),
 97            ),
 98          ],
 99        ),
100      ),
101    );
102  }
103}
 1import 'package:delimeals/models/meal.dart';
 2import 'package:delimeals/widgets/meal_item.dart';
 3import 'package:flutter/material.dart';
 4
 5class FavoritesScreen extends StatelessWidget {
 6  final List<Meal> favoriteMeals;
 7
 8  FavoritesScreen(this.favoriteMeals);
 9
10  @override
11  Widget build(BuildContext context) {
12    if (favoriteMeals.isEmpty) {
13      return Center(
14        child: Text('You have no Favorites yet - start adding some !'),
15      );
16    } else {
17      return ListView.builder(
18        itemBuilder: (context, index) {
19          return MealItem(
20            id: favoriteMeals[index].id,
21            title: favoriteMeals[index].title,
22            imageUrl: favoriteMeals[index].imageUrl,
23            duration: favoriteMeals[index].duration,
24            complexity: favoriteMeals[index].complexity,
25            affordability: favoriteMeals[index].affordability,
26          );
27        },
28        itemCount: favoriteMeals.length,
29      );
30    }
31  }
32}
 1import 'package:delimeals/models/meal.dart';
 2import 'package:delimeals/widgets/meal_item.dart';
 3import 'package:flutter/material.dart';
 4
 5class CategoryMealsScreen extends StatefulWidget {
 6  static const routeName = '/category-meals';
 7
 8  final List<Meal> availableMeals;
 9  CategoryMealsScreen(this.availableMeals);
10
11  @override
12  _CategoryMealsScreenState createState() => _CategoryMealsScreenState();
13}
14
15class _CategoryMealsScreenState extends State<CategoryMealsScreen> {
16  String categoryTitle;
17  List<Meal> displayedMeals;
18  var _loadedInitData = false;
19
20  @override
21  void initState() {
22    super.initState();
23  }
24
25  @override
26  void didChangeDependencies() {
27    if (!_loadedInitData) {
28      final routeArgs =
29          ModalRoute.of(context).settings.arguments as Map<String, String>;
30      final categoryId = routeArgs['id'];
31
32      categoryTitle = routeArgs['title'];
33      displayedMeals = widget.availableMeals.where((meal) {
34        return meal.categories.contains(categoryId);
35      }).toList();
36
37      _loadedInitData = true;
38    }
39
40    super.didChangeDependencies();
41  }
42
43  @override
44  Widget build(BuildContext context) {
45    return Scaffold(
46      appBar: AppBar(
47        title: Text(
48          categoryTitle,
49        ),
50      ),
51      body: ListView.builder(
52        itemBuilder: (context, index) {
53          return MealItem(
54            id: displayedMeals[index].id,
55            title: displayedMeals[index].title,
56            imageUrl: displayedMeals[index].imageUrl,
57            duration: displayedMeals[index].duration,
58            complexity: displayedMeals[index].complexity,
59            affordability: displayedMeals[index].affordability,
60          );
61        },
62        itemCount: displayedMeals.length,
63      ),
64    );
65  }
66}