projet debut
| @@ -1,12 +1,31 @@ | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
|  |  | ||||||
| void main() { | class CardWithImage extends StatelessWidget { | ||||||
|   runApp( |   const CardWithImage({super.key}); | ||||||
|     Container( |   @override | ||||||
|       color: Colors.red, |   Widget build(BuildContext context) { | ||||||
|       alignment: Alignment.center, |     return Scaffold( | ||||||
|       margin: const EdgeInsets.all(100), |       appBar: AppBar(title: const Text('Card + Image de fond')), | ||||||
|       child: const Text("Hello world", textDirection: TextDirection.ltr), |       body: Padding( | ||||||
|     ), |         padding: const EdgeInsets.all(10), | ||||||
|   ); |         child: Card( | ||||||
|  |           elevation: 5, | ||||||
|  |           clipBehavior: Clip.antiAlias, | ||||||
|  |           child: SizedBox( | ||||||
|  |             height: 150, | ||||||
|  |             child: Stack( | ||||||
|  |               fit: StackFit.expand, // l'enfant occupe toute la carte | ||||||
|  |               children: [ | ||||||
|  |                 // Image locale déclarée dans pubspec.yaml | ||||||
|  |                 Image.asset( | ||||||
|  |                   'assets/images/paris.jpg', | ||||||
|  |                   fit: BoxFit.cover, // couvre toute la surface, quitte à rogner | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
| } | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								my_travel/assets/images/activities/chaumont.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 7.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								my_travel/assets/images/activities/dame.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										
											BIN
										
									
								
								my_travel/assets/images/activities/defense.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										
											BIN
										
									
								
								my_travel/assets/images/activities/louvre.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								my_travel/assets/images/lyon.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 8.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								my_travel/assets/images/nice.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 90 KiB | 
							
								
								
									
										
											BIN
										
									
								
								my_travel/assets/images/paris.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 44 KiB | 
							
								
								
									
										29
									
								
								my_travel/lib/data/data.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,29 @@ | |||||||
|  | // lib/data/data.dart | ||||||
|  | import '../models/activity.model.dart'; | ||||||
|  |  | ||||||
|  | List<Activity> activities = [ | ||||||
|  |   Activity( | ||||||
|  |     image: 'assets/images/activities/louvre.jpg', | ||||||
|  |     name: 'Le Louvre', | ||||||
|  |     id: 'a1', | ||||||
|  |     city: 'Paris', | ||||||
|  |   ), | ||||||
|  |   Activity( | ||||||
|  |     image: 'assets/images/activities/chaumont.jpg', | ||||||
|  |     name: 'Les buttes Chaumont', | ||||||
|  |     id: 'a2', | ||||||
|  |     city: 'Paris', | ||||||
|  |   ), | ||||||
|  |   Activity( | ||||||
|  |     image: 'assets/images/activities/dame.jpg', | ||||||
|  |     name: 'Notre Dame', | ||||||
|  |     id: 'a3', | ||||||
|  |     city: 'Paris', | ||||||
|  |   ), | ||||||
|  |   Activity( | ||||||
|  |     image: 'assets/images/activities/defense.jpg', | ||||||
|  |     name: 'La Défense', | ||||||
|  |     id: 'a4', | ||||||
|  |     city: 'Paris', | ||||||
|  |   ), | ||||||
|  | ]; | ||||||
| @@ -1,22 +1,15 @@ | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
|  | import 'views/city/city.dart'; | ||||||
|  |  | ||||||
| void main() => runApp(const DrEvaristenTrip()); | void main() => runApp(const DrEvaristenTrip()); | ||||||
|  |  | ||||||
| class DrEvaristenTrip extends StatelessWidget { | class DrEvaristenTrip extends StatelessWidget { | ||||||
|  |  | ||||||
|   const DrEvaristenTrip({super.key}); |   const DrEvaristenTrip({super.key}); | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return MaterialApp( |     return MaterialApp( | ||||||
|       debugShowCheckedModeBanner: false, |       debugShowCheckedModeBanner: false, | ||||||
|       home: Scaffold( |       home: City(), // affichage direct pour développement | ||||||
|         appBar: AppBar( |  | ||||||
|           leading: const Icon(Icons.home), |  | ||||||
|           title: const Text('DrEvaristen Trip'), |  | ||||||
|           actions: const <Widget>[Icon(Icons.more_vert)], |  | ||||||
|         ), |  | ||||||
|       body: const Text('DrEvaristen'), |  | ||||||
|       ), |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								my_travel/lib/models/activity.model.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | |||||||
|  | // lib/models/activity.model.dart | ||||||
|  | class Activity { | ||||||
|  |   String name; | ||||||
|  |   String image; | ||||||
|  |   String? id; | ||||||
|  |   String city; | ||||||
|  |   Activity({ | ||||||
|  |     required this.name, | ||||||
|  |     required this.city, | ||||||
|  |     this.id, | ||||||
|  |     required this.image, | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								my_travel/lib/views/city/city.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import '../../data/data.dart' as data; | ||||||
|  | import '../../models/activity.model.dart'; | ||||||
|  | import '../widgets/activity_card.dart'; | ||||||
|  |  | ||||||
|  | class City extends StatefulWidget { | ||||||
|  |   final List<Activity> activities = data.activities; | ||||||
|  |   City({super.key}); | ||||||
|  |   @override | ||||||
|  |   State<City> createState() => _CityState(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _CityState extends State<City> { | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Scaffold( | ||||||
|  |       appBar: AppBar( | ||||||
|  |         leading: const Icon(Icons.chevron_left), | ||||||
|  |         title: const Text('Paris'), | ||||||
|  |         actions: const [Icon(Icons.more_vert)], | ||||||
|  |       ), | ||||||
|  |  | ||||||
|  |       // ==================== GridView.extent (taille max par item) - VERSION ACTIVE ✅ ==================== | ||||||
|  |       body: Container( | ||||||
|  |         padding: const EdgeInsets.all(10), | ||||||
|  |         child: GridView.extent( | ||||||
|  |           maxCrossAxisExtent: 150, // largeur max d'un item | ||||||
|  |           mainAxisSpacing: 2, | ||||||
|  |           crossAxisSpacing: 5, | ||||||
|  |           children: widget.activities | ||||||
|  |               .map((a) => ActivityCard(activity: a)) | ||||||
|  |               .toList(), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								my_travel/lib/views/home/home.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,53 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import '../widgets/city_card.dart'; | ||||||
|  |  | ||||||
|  | class Home extends StatefulWidget { | ||||||
|  |   const Home({super.key}); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   State<Home> createState() => _HomeState(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _HomeState extends State<Home> { | ||||||
|  |   // Liste "source de vérité" affichée dans l'UI | ||||||
|  |   final List<Map<String, dynamic>> cities = [ | ||||||
|  |     {'name': 'Paris', 'image': 'assets/images/paris.jpg', 'checked': false}, | ||||||
|  |     {'name': 'Lyon', 'image': 'assets/images/lyon.jpg', 'checked': false}, | ||||||
|  |     {'name': 'Nice', 'image': 'assets/images/nice.jpg', 'checked': false}, | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|  |   // Fonction pour basculer l'état favori d'une ville | ||||||
|  |   void switchChecked(Map<String, dynamic> city) { | ||||||
|  |     setState(() { | ||||||
|  |       city['checked'] = !(city['checked'] ?? false); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Scaffold( | ||||||
|  |       appBar: AppBar( | ||||||
|  |         leading: const Icon(Icons.home), | ||||||
|  |         title: const Text('DrEvaristen Trip'), | ||||||
|  |         actions: const [Icon(Icons.more_vert)], | ||||||
|  |       ), | ||||||
|  |       body: Container( | ||||||
|  |         padding: const EdgeInsets.all(10), | ||||||
|  |         child: Column( | ||||||
|  |           crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|  |           // On transforme chaque map en CityCard | ||||||
|  |           children: [ | ||||||
|  |             for (final city in cities) | ||||||
|  |               CityCard( | ||||||
|  |                 name: city['name'], | ||||||
|  |                 image: city['image'], | ||||||
|  |                 checked: city['checked'] ?? false, | ||||||
|  |                 updateChecked: () => | ||||||
|  |                     switchChecked(city), // capture la bonne ville | ||||||
|  |               ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								my_travel/lib/views/widgets/activity_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import '../../models/activity.model.dart'; | ||||||
|  |  | ||||||
|  | class ActivityCard extends StatelessWidget { | ||||||
|  |   final Activity activity; | ||||||
|  |   const ActivityCard({super.key, required this.activity}); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return ListTile( | ||||||
|  |       leading: CircleAvatar(backgroundImage: AssetImage(activity.image)), | ||||||
|  |       title: Text(activity.name), | ||||||
|  |       subtitle: Text(activity.city), | ||||||
|  |       trailing: Checkbox( | ||||||
|  |         value: true, | ||||||
|  |         tristate: true, | ||||||
|  |         onChanged: (e) {}, // nécessite un état pour évoluer | ||||||
|  |         activeColor: Colors.black, | ||||||
|  |         checkColor: Colors.red, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										67
									
								
								my_travel/lib/views/widgets/city_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,67 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  |  | ||||||
|  | class CityCard extends StatelessWidget { | ||||||
|  |   final String name; | ||||||
|  |   final String image; | ||||||
|  |   final bool checked; // devient requis | ||||||
|  |   final VoidCallback updateChecked; | ||||||
|  |   const CityCard({ | ||||||
|  |     super.key, | ||||||
|  |     required this.name, | ||||||
|  |     required this.image, | ||||||
|  |     required this.checked, | ||||||
|  |     required this.updateChecked, | ||||||
|  |   }); | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Card( | ||||||
|  |       elevation: 5, | ||||||
|  |       clipBehavior: Clip.antiAlias, | ||||||
|  |       child: SizedBox( | ||||||
|  |         height: 150, | ||||||
|  |         child: Stack( | ||||||
|  |           fit: StackFit.expand, | ||||||
|  |           children: [ | ||||||
|  |             Ink.image( | ||||||
|  |               image: AssetImage(image), | ||||||
|  |               fit: BoxFit.cover, | ||||||
|  |               child: InkWell(onTap: updateChecked), // notifie le parent | ||||||
|  |             ), | ||||||
|  |             Padding( | ||||||
|  |               padding: const EdgeInsets.all(10), | ||||||
|  |               child: Column( | ||||||
|  |                 children: [ | ||||||
|  |                   // Icône favori : pleine si checked, sinon contour | ||||||
|  |                   Expanded( | ||||||
|  |                     child: Row( | ||||||
|  |                       mainAxisAlignment: MainAxisAlignment.end, | ||||||
|  |                       crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                       children: [ | ||||||
|  |                         Icon( | ||||||
|  |                           checked ? Icons.star : Icons.star_border, | ||||||
|  |                           size: 30, | ||||||
|  |                           color: Colors.white, | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                   Row( | ||||||
|  |                     children: [ | ||||||
|  |                       Text( | ||||||
|  |                         name, | ||||||
|  |                         style: const TextStyle( | ||||||
|  |                           fontSize: 30, | ||||||
|  |                           color: Colors.white, | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|  |                 ], | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -58,7 +58,9 @@ flutter: | |||||||
|   uses-material-design: true |   uses-material-design: true | ||||||
|  |  | ||||||
|   # To add assets to your application, add an assets section, like this: |   # To add assets to your application, add an assets section, like this: | ||||||
|   # assets: |   assets: | ||||||
|  |     - assets/images/ | ||||||
|  |     - assets/images/activities/ | ||||||
|   #   - images/a_dot_burr.jpeg |   #   - images/a_dot_burr.jpeg | ||||||
|   #   - images/a_dot_ham.jpeg |   #   - images/a_dot_ham.jpeg | ||||||
|  |  | ||||||
|   | |||||||