Post Detail
Post #102
Flutter Jour Cinq Part 2
Flutter Jour Cinq (Part 2) Part 1
DeliMeals
- 11:15 - 12:15 : Ajout des tabs
Code jusque là (1)
1import 'package:delimeals/screens/category_meals_screen.dart';
2import 'package:delimeals/screens/meal_detail_screen.dart';
3import 'package:delimeals/screens/tabs_screen.dart';
4import 'package:flutter/material.dart';
5
6void main() {
7 runApp(MyApp());
8}
9
10class MyApp extends StatelessWidget {
11 @override
12 Widget build(BuildContext context) {
13 return MaterialApp(
14 title: 'DeliMeals',
15 theme: ThemeData(
16 primarySwatch: Colors.pink,
17 accentColor: Colors.amber,
18 canvasColor: Color.fromRGBO(255, 254, 229, 1),
19 fontFamily: 'Raleway',
20 textTheme: TextTheme(
21 headline6: TextStyle(
22 fontWeight: FontWeight.bold,
23 ),
24 ),
25 ),
26 routes: {
27 '/': (context) => TabsScreen(),
28 CategoryMealsScreen.routeName: (context) => CategoryMealsScreen(),
29 MealDetailScreen.routeName: (context) => MealDetailScreen(),
30 },
31 );
32 }
33}
34
35class MyHomePage extends StatefulWidget {
36 @override
37 _MyHomePageState createState() => _MyHomePageState();
38}
39
40class _MyHomePageState extends State<MyHomePage> {
41 @override
42 Widget build(BuildContext context) {
43 return Scaffold(
44 appBar: AppBar(
45 title: Text('DeliMeals'),
46 ),
47 body: Center(
48 child: Text('Navigation Time!'),
49 ),
50 );
51 }
52}
1import 'package:delimeals/dummy_data.dart';
2import 'package:delimeals/widgets/category_item.dart';
3import 'package:flutter/material.dart';
4import 'package:flutter/widgets.dart';
5
6class CategoriesScreen extends StatelessWidget {
7 @override
8 Widget build(BuildContext context) {
9 return GridView(
10 padding: const EdgeInsets.all(25),
11 children: DUMMY_CATEGORIES
12 .map(
13 (catData) => CategoryItem(
14 catData.id,
15 catData.title,
16 catData.color,
17 ),
18 )
19 .toList(),
20 gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
21 maxCrossAxisExtent: 200,
22 childAspectRatio: 3 / 2,
23 crossAxisSpacing: 20,
24 mainAxisSpacing: 20,
25 ),
26 );
27 }
28}
1import 'package:flutter/material.dart';
2
3import 'categories_screen.dart';
4import 'favorites_screen.dart';
5
6class TabsScreen extends StatefulWidget {
7 @override
8 _TabsScreenState createState() => _TabsScreenState();
9}
10
11class _TabsScreenState extends State<TabsScreen> {
12 final List<Map<String, Object>> _pages = [
13 {'page': CategoriesScreen(), 'title': 'Categories'},
14 {'page': FavoritesScreen(), 'title': 'Your Favorites'},
15 ];
16
17 int _selectedPageIndex = 0;
18
19 void _selectPage(int index) {
20 setState(() {
21 _selectedPageIndex = index;
22 });
23 }
24
25 @override
26 Widget build(BuildContext context) {
27 return Scaffold(
28 appBar: AppBar(
29 title: Text(_pages[_selectedPageIndex]['title']),
30 ),
31 body: _pages[_selectedPageIndex]['page'],
32 bottomNavigationBar: BottomNavigationBar(
33 onTap: _selectPage,
34 backgroundColor: Theme.of(context).primaryColor,
35 unselectedItemColor: Colors.white,
36 selectedItemColor: Theme.of(context).accentColor,
37 currentIndex: _selectedPageIndex,
38 items: [
39 BottomNavigationBarItem(
40 icon: Icon(Icons.category),
41 title: Text('Categories'),
42 ),
43 BottomNavigationBarItem(
44 icon: Icon(Icons.star),
45 title: Text('Favorites'),
46 ),
47 ],
48 ),
49 );
50 }
51}
1import 'package:flutter/material.dart';
2
3class FavoritesScreen extends StatelessWidget {
4 @override
5 Widget build(BuildContext context) {
6 return Center(
7 child: Text('Favorites'),
8 );
9 }
10}
- 13:00 - 15:00 : Drawers
Code jusqu’ici (2)
1import 'package:delimeals/screens/category_meals_screen.dart';
2import 'package:delimeals/screens/meal_detail_screen.dart';
3import 'package:delimeals/screens/tabs_screen.dart';
4import 'package:delimeals/widgets/filters_screen.dart';
5import 'package:flutter/material.dart';
6
7void main() {
8 runApp(MyApp());
9}
10
11class MyApp extends StatelessWidget {
12 @override
13 Widget build(BuildContext context) {
14 return MaterialApp(
15 title: 'DeliMeals',
16 theme: ThemeData(
17 primarySwatch: Colors.pink,
18 accentColor: Colors.amber,
19 canvasColor: Color.fromRGBO(255, 254, 229, 1),
20 fontFamily: 'Raleway',
21 textTheme: TextTheme(
22 headline6: TextStyle(
23 fontWeight: FontWeight.bold,
24 ),
25 ),
26 ),
27 routes: {
28 '/': (context) => TabsScreen(),
29 CategoryMealsScreen.routeName: (context) => CategoryMealsScreen(),
30 MealDetailScreen.routeName: (context) => MealDetailScreen(),
31 FiltersScreen.routeName: (context) => FiltersScreen(),
32 },
33 );
34 }
35}
36
37class MyHomePage extends StatefulWidget {
38 @override
39 _MyHomePageState createState() => _MyHomePageState();
40}
41
42class _MyHomePageState extends State<MyHomePage> {
43 @override
44 Widget build(BuildContext context) {
45 return Scaffold(
46 appBar: AppBar(
47 title: Text('DeliMeals'),
48 ),
49 body: Center(
50 child: Text('Navigation Time!'),
51 ),
52 );
53 }
54}
1import 'package:delimeals/dummy_data.dart';
2import 'package:flutter/material.dart';
3
4class MealDetailScreen extends StatelessWidget {
5 static const routeName = '/meal-detail';
6
7 Widget buildSectionTitle(BuildContext context, String text) {
8 return Container(
9 margin: EdgeInsets.symmetric(vertical: 10),
10 child: Text(
11 text,
12 style: Theme.of(context).textTheme.headline6,
13 ),
14 );
15 }
16
17 Widget buildContainer({Widget child}) {
18 return Container(
19 decoration: BoxDecoration(
20 color: Colors.white,
21 border: Border.all(color: Colors.grey),
22 borderRadius: BorderRadius.circular(10),
23 ),
24 padding: EdgeInsets.all(10),
25 margin: EdgeInsets.all(10),
26 height: 150,
27 width: 300,
28 child: child,
29 );
30 }
31
32 @override
33 Widget build(BuildContext context) {
34 final mealId = ModalRoute.of(context).settings.arguments as String;
35 final selectedMeal = DUMMY_MEALS.firstWhere((meal) => meal.id == mealId);
36
37 return Scaffold(
38 floatingActionButton: FloatingActionButton(
39 child: Icon(
40 Icons.delete,
41 ),
42 onPressed: () {
43 Navigator.of(context).pop(mealId);
44 },
45 ),
46 appBar: AppBar(
47 title: Text('${selectedMeal.title}'),
48 ),
49 body: SingleChildScrollView(
50 child: Column(
51 children: <Widget>[
52 Container(
53 height: 300,
54 width: double.infinity,
55 child: Image.network(
56 selectedMeal.imageUrl,
57 fit: BoxFit.cover,
58 ),
59 ),
60 buildSectionTitle(context, 'Ingredients'),
61 buildContainer(
62 child: ListView.builder(
63 itemBuilder: (context, index) => Card(
64 color: Theme.of(context).accentColor,
65 child: Padding(
66 padding: const EdgeInsets.symmetric(
67 vertical: 5,
68 horizontal: 10,
69 ),
70 child: Text(selectedMeal.ingredients[index]),
71 ),
72 ),
73 itemCount: selectedMeal.ingredients.length,
74 ),
75 ),
76 buildSectionTitle(context, 'Steps'),
77 buildContainer(
78 child: ListView.builder(
79 itemBuilder: (context, index) => Column(
80 children: <Widget>[
81 ListTile(
82 leading: CircleAvatar(
83 child: Text('# ${(index + 1)}'),
84 ),
85 title: Text(
86 selectedMeal.steps[index],
87 ),
88 ),
89 Divider(),
90 ],
91 ),
92 itemCount: selectedMeal.steps.length,
93 ),
94 ),
95 ],
96 ),
97 ),
98 );
99 }
100}
1import 'package:delimeals/dummy_data.dart';
2import 'package:delimeals/models/meal.dart';
3import 'package:delimeals/widgets/meal_item.dart';
4import 'package:flutter/material.dart';
5
6class CategoryMealsScreen extends StatefulWidget {
7 static const routeName = '/category-meals';
8
9 @override
10 _CategoryMealsScreenState createState() => _CategoryMealsScreenState();
11}
12
13class _CategoryMealsScreenState extends State<CategoryMealsScreen> {
14 String categoryTitle;
15 List<Meal> displayedMeals;
16 var _loadedInitData = false;
17
18 @override
19 void initState() {
20 super.initState();
21 }
22
23 @override
24 void didChangeDependencies() {
25 if (!_loadedInitData) {
26 final routeArgs =
27 ModalRoute.of(context).settings.arguments as Map<String, String>;
28 final categoryId = routeArgs['id'];
29
30 categoryTitle = routeArgs['title'];
31 displayedMeals = DUMMY_MEALS.where((meal) {
32 return meal.categories.contains(categoryId);
33 }).toList();
34
35 _loadedInitData = true;
36 }
37
38 super.didChangeDependencies();
39 }
40
41 void _removeMeal(String mealId) {
42 setState(() {
43 displayedMeals.removeWhere((meal) => meal.id == mealId);
44 });
45 }
46
47 @override
48 Widget build(BuildContext context) {
49 return Scaffold(
50 appBar: AppBar(
51 title: Text(
52 categoryTitle,
53 ),
54 ),
55 body: ListView.builder(
56 itemBuilder: (context, index) {
57 return MealItem(
58 id: displayedMeals[index].id,
59 title: displayedMeals[index].title,
60 imageUrl: displayedMeals[index].imageUrl,
61 duration: displayedMeals[index].duration,
62 complexity: displayedMeals[index].complexity,
63 affordability: displayedMeals[index].affordability,
64 removeItem: _removeMeal,
65 );
66 },
67 itemCount: displayedMeals.length,
68 ),
69 );
70 }
71}
1import 'package:delimeals/widgets/main_drawer.dart';
2import 'package:flutter/material.dart';
3
4class FiltersScreen extends StatelessWidget {
5 static const routeName = '/filters';
6
7 @override
8 Widget build(BuildContext context) {
9 return Scaffold(
10 drawer: MainDrawer(),
11 appBar: AppBar(
12 title: Text('Your Filters'),
13 ),
14 body: Center(
15 child: Text('Filters'),
16 ),
17 );
18 }
19}
1import 'package:delimeals/widgets/filters_screen.dart';
2import 'package:flutter/material.dart';
3
4class MainDrawer extends StatelessWidget {
5 Widget buildListTile(String title, IconData icon, Function tapHandler) {
6 return ListTile(
7 leading: Icon(
8 icon,
9 size: 26,
10 ),
11 title: Text(
12 title,
13 style: TextStyle(
14 fontFamily: 'RobotoCondensed',
15 fontSize: 24,
16 fontWeight: FontWeight.bold,
17 ),
18 ),
19 onTap: tapHandler,
20 );
21 }
22
23 @override
24 Widget build(BuildContext context) {
25 return Drawer(
26 child: Column(
27 children: [
28 Container(
29 height: 120,
30 width: double.infinity,
31 padding: EdgeInsets.all(20),
32 alignment: Alignment.centerLeft,
33 color: Theme.of(context).accentColor,
34 child: Text(
35 'Cooking Up !',
36 style: TextStyle(
37 fontWeight: FontWeight.w900,
38 fontSize: 30,
39 color: Theme.of(context).primaryColor,
40 ),
41 ),
42 ),
43 SizedBox(
44 height: 20,
45 ),
46 buildListTile('Meals', Icons.restaurant, () {
47 Navigator.of(context).pushReplacementNamed('/');
48 }),
49 buildListTile('Filters', Icons.settings, () {
50 Navigator.of(context).pushReplacementNamed(FiltersScreen.routeName);
51 }),
52 ],
53 ),
54 );
55 }
56}
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 final Function removeItem;
13
14 MealItem({
15 @required this.id,
16 @required this.title,
17 @required this.imageUrl,
18 @required this.duration,
19 @required this.complexity,
20 @required this.affordability,
21 @required this.removeItem,
22 });
23
24 String get complexityText {
25 switch (complexity) {
26 case Complexity.Simple:
27 return 'Simple';
28 break;
29 case Complexity.Challenging:
30 return 'Challenging';
31 break;
32 case Complexity.Hard:
33 return 'Hard';
34 break;
35 default:
36 return 'Unknown';
37 }
38 }
39
40 String get affordabilityText {
41 switch (affordability) {
42 case Affordability.Affordable:
43 return 'Affordable';
44 break;
45 case Affordability.Pricey:
46 return 'Pricey';
47 break;
48 case Affordability.Luxurious:
49 return 'Luxurious';
50 break;
51 default:
52 return 'Unknown';
53 }
54 }
55
56 void selectMeal(BuildContext context) {
57 Navigator.of(context)
58 .pushNamed(
59 MealDetailScreen.routeName,
60 arguments: id,
61 )
62 .then((result) {
63 if (result != null) {
64 removeItem(result);
65 }
66 });
67 }
68
69 @override
70 Widget build(BuildContext context) {
71 return InkWell(
72 onTap: () => selectMeal(context),
73 child: Card(
74 shape: RoundedRectangleBorder(
75 borderRadius: BorderRadius.circular(15),
76 ),
77 elevation: 4,
78 margin: EdgeInsets.all(10),
79 child: Column(
80 children: <Widget>[
81 Stack(
82 children: <Widget>[
83 ClipRRect(
84 borderRadius: BorderRadius.only(
85 topLeft: Radius.circular(15),
86 topRight: Radius.circular(15),
87 ),
88 child: Image.network(
89 imageUrl,
90 height: 250,
91 width: double.infinity,
92 fit: BoxFit.cover,
93 ),
94 ),
95 Positioned(
96 bottom: 20,
97 right: 10,
98 child: Container(
99 width: 300,
100 color: Colors.black54,
101 padding: EdgeInsets.symmetric(
102 vertical: 5,
103 horizontal: 20,
104 ),
105 child: Text(
106 title,
107 style: TextStyle(
108 fontSize: 26,
109 color: Colors.white,
110 fontFamily: 'Raleway',
111 ),
112 softWrap: true,
113 overflow: TextOverflow.fade,
114 ),
115 ),
116 ),
117 ],
118 ),
119 Padding(
120 padding: EdgeInsets.all(20),
121 child: Row(
122 mainAxisAlignment: MainAxisAlignment.spaceAround,
123 children: <Widget>[
124 Row(
125 children: <Widget>[
126 Icon(Icons.schedule),
127 SizedBox(width: 6),
128 Text('$duration min'),
129 ],
130 ),
131 Row(
132 children: <Widget>[
133 Icon(Icons.work),
134 SizedBox(width: 6),
135 Text(complexityText),
136 ],
137 ),
138 Row(
139 children: <Widget>[
140 Icon(Icons.attach_money),
141 SizedBox(width: 6),
142 Text(affordabilityText)
143 ],
144 )
145 ],
146 ),
147 ),
148 ],
149 ),
150 ),
151 );
152 }
153}
No comments here yet.