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
|
||||||
|
|
||||||
|
|||||||