Création d’un calendrier personnalisé avec Flutter
Sommaire
- 1- Objectif
- 2- Image de référence :
- 3- Énoncé de l'exercice
- 3.1- Description
- 3.2- Instructions
- 3.2.1- Étape 1 : Initialisation du projet Flutter
- 3.2.2- Étape 2 : Structure de base de l'application
- 3.2.3- Étape 3 : AppBar personnalisée
- 3.2.4- Étape 4 : Section de période
- 3.2.5- Étape 5 : Sélection de plages de dates
- 3.2.6- Étape 6 : Personnalisation du style du calendrier
- 3.2.7- Étape 7 : Section de validation de réservation
- 3.3- Questions
- 3.3.1- Création de l'application :
- 3.3.2- Configuration de l'AppBar :
- 3.3.3- Affichage des dates :
- 3.3.4- Utilisation de TableCalendar :
- 3.4- Personnalisation du calendrier :
- 3.5- Validation de la sélection :
- 4- Réalisation
- 5- Solution
- 6- Objectif
- 7- Image de référence :
- 8- Énoncé de l'exercice
- 8.1- Description
- 8.2- Instructions
- 8.2.1- Étape 1 : Initialisation de la locale en français
- 8.2.2- Étape 2 : Configuration du TableCalendar
- 8.3- Changer le début de la semaine à lundi :
- 8.4- Afficher les textes des samedis et dimanches en rouge :
- 8.5- Questions
- 9- Réalisation
- 10- Solution
- 10.1.1- Cours Flutter
Création d’un calendrier personnalisé avec Flutter
Création d’un calendrier personnalisé avec Flutter (Partie 1)
-
Objectif
- Réaliser une application Flutter permettant d’afficher un calendrier avec des fonctionnalités de sélection de plages de dates, de personnalisation du style de l’en-tête et de la langue, ainsi que la possibilité de sélectionner une année.
-
Image de référence :
- Une capture d’écran de l’interface utilisateur attendue, montrant le calendrier avec les modifications spécifiées.
-
Énoncé de l’exercice
-
Description
- Vous allez créer une application Flutter qui affiche un calendrier avec les fonctionnalités suivantes :
- Affichage d’un calendrier avec possibilité de sélection de plages de dates.
- Personnalisation du style de l’en-tête du calendrier.
- Sélection d’une année dans le calendrier.
- Affichage des dates de début et de retour sélectionnées en haut de l’interface.
-
Instructions
-
Étape 1 : Initialisation du projet Flutter
- Créez un nouveau projet Flutter.
- Ajoutez les dépendances nécessaires pour table_calendar et google_fonts dans votre fichier pubspec.yaml.
-
Étape 2 : Structure de base de l’application
- Créez une classe MyApp qui retourne un MaterialApp avec une route par défaut menant à CalendarPage.
- Créez la classe CalendarPage en tant que StatefulWidget.
-
Étape 3 : AppBar personnalisée
- Dans CalendarPage, ajoutez un AppBar avec un titre « Réservation » et des icônes d’action.
- Utilisez la bibliothèque google_fonts pour appliquer une police personnalisée au titre de l’AppBar.
-
Étape 4 : Section de période
- Ajoutez un widget PeriodSection pour afficher les dates de début et de retour sélectionnées.
- Créez une fonction pour formater les dates de manière lisible (par exemple, « 12 Dec »).
-
Étape 5 : Sélection de plages de dates
- Implémentez le widget TableCalendar pour permettre la sélection de plages de dates.
- Ajoutez une fonction de rappel pour mettre à jour les dates sélectionnées dans CalendarPage.
-
Étape 6 : Personnalisation du style du calendrier
- Personnalisez le style de l’en-tête du calendrier pour centrer le titre et masquer le bouton de changement de format.
- Définissez un style pour les jours sélectionnés et les jours dans la plage de dates.
-
Étape 7 : Section de validation de réservation
- Ajoutez une section ValidateBookingSection avec un bouton « Appliquer » pour valider la sélection des dates.
-
Questions
-
Création de l’application :
- Comment initialiser un projet Flutter et ajouter des dépendances dans le fichier pubspec.yaml ?
-
Configuration de l’AppBar :
- Comment personnaliser le titre et ajouter des icônes dans l’AppBar d’une application Flutter ?
- Comment utiliser google_fonts pour appliquer une police personnalisée ?
-
Affichage des dates :
- Comment créer une section pour afficher les dates de début et de retour sélectionnées ?
- Comment formater une date pour l’afficher de manière lisible ?
-
Utilisation de TableCalendar :
- Comment implémenter un widget TableCalendar dans Flutter ?
- Comment gérer la sélection de plages de dates et mettre à jour l’état dans le widget parent ?
-
Personnalisation du calendrier :
- Comment personnaliser le style de l’en-tête du calendrier pour centrer le titre ?
- Comment définir un style spécifique pour les jours sélectionnés et les jours dans la plage de dates ?
-
Validation de la sélection :
- Comment ajouter une section de validation avec un bouton « Appliquer » ?
- Comment gérer l’événement de clic sur le bouton pour valider la sélection des dates ?
-
Réalisation
- Basé sur l’image fournie et les instructions ci-dessus, réalisez l’application en répondant aux questions posées. Utilisez les concepts appris pour implémenter les fonctionnalités demandées. Bonne chance !
- Remarque : Vous pouvez trouver des exemples et la documentation complète de Flutter et des packages utilisés (comme table_calendar et google_fonts) sur le site officiel de Flutter et les dépôts GitHub respectifs.
-
Solution
dependencies:
flutter:
sdk: flutter
table_calendar: ^3.0.0
google_fonts: ^2.1.0
Code
import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:google_fonts/google_fonts.dart';
void main() {
runApp(MyApp());
}
DateTime kNow = DateTime.now();
DateTime kFirstDay = DateTime(kNow.year, kNow.month - 3, kNow.day);
DateTime kLastDay = DateTime(kNow.year, kNow.month + 3, kNow.day);
const c_marron = Color(0xFF94812b);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: CalendarPage(),
);
}
}
class CalendarPage extends StatefulWidget {
@override
_CalendarPageState createState() => _CalendarPageState();
}
class _CalendarPageState extends State {
DateTime? _departureDate;
DateTime? _returnDate;
void _updateDates(DateTime? departure, DateTime? returnDate) {
setState(() {
_departureDate = departure;
_returnDate = returnDate;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(),
body: Column(
children: [
PeriodSection(departureDate: _departureDate, returnDate: _returnDate),
CalendarRange(updateDates: _updateDates),
//ValidateBookingSection(),
],
),
);
}
}
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
Size get preferredSize => new Size.fromHeight(50);
@override
Widget build(BuildContext context) {
return AppBar(
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.grey[800],
size: 20,
),
onPressed: null,
),
title: Text(
'Réservation',
style: GoogleFonts.nunito(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.w800,
),
),
actions: [
IconButton(
icon: Icon(
Icons.favorite_outline_rounded,
color: Colors.grey[800],
size: 20,
),
onPressed: null,
),
IconButton(
icon: Icon(
Icons.place,
color: Colors.grey[800],
size: 20,
),
onPressed: null,
),
],
centerTitle: true,
backgroundColor: Colors.white,
);
}
}
class PeriodSection extends StatelessWidget {
final DateTime? departureDate;
final DateTime? returnDate;
PeriodSection({this.departureDate, this.returnDate});
String _monthName(int month) {
switch (month) {
case 1:
return 'Jan';
case 2:
return 'Feb';
case 3:
return 'Mar';
case 4:
return 'Apr';
case 5:
return 'May';
case 6:
return 'Jun';
case 7:
return 'Jul';
case 8:
return 'Aug';
case 9:
return 'Sep';
case 10:
return 'Oct';
case 11:
return 'Nov';
case 12:
return 'Dec';
default:
return '';
}
}
String formatDate(DateTime? date) {
if (date == null) return 'Non sélectionnée';
return '${date.day} ${_monthName(date.month)}';
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Départ',
textAlign: TextAlign.left,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 16,
color: Colors.grey[700],
),
),
SizedBox(
height: 4,
),
Text(
formatDate(departureDate),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
],
),
Container(
height: 60,
width: 1,
color: Colors.grey[350],
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Retour',
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 16,
color: Colors.grey[700],
),
),
SizedBox(
height: 4,
),
Text(
formatDate(returnDate),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
],
),
],
),
Divider(
color: Colors.grey,
height: 1,
),
],
);
}
}
class CalendarRange extends StatefulWidget {
final Function(DateTime?, DateTime?) updateDates;
CalendarRange({required this.updateDates});
@override
_CalendarRangeState createState() => _CalendarRangeState();
}
class _CalendarRangeState extends State<CalendarRange> {
CalendarFormat _calendarFormat = CalendarFormat.month;
RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOn;
DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay;
DateTime? _rangeStart;
DateTime? _rangeEnd;
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(15),
color: Colors.grey[10],
child: TableCalendar(
lastDay: kLastDay,
focusedDay: _focusedDay,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
rangeStartDay: _rangeStart,
rangeEndDay: _rangeEnd,
calendarFormat: _calendarFormat,
headerStyle: HeaderStyle(
titleCentered: true,
formatButtonVisible: false,
titleTextStyle: TextStyle(fontSize: 18),
),
calendarStyle: CalendarStyle(
isTodayHighlighted: false,
rangeHighlightColor: c_marron,
rangeStartDecoration: BoxDecoration(
color: c_marron,
shape: BoxShape.circle,
border: Border.fromBorderSide(
BorderSide(
color: Colors.white,
width: 3,
style: BorderStyle.solid,
),
),
),
withinRangeTextStyle: TextStyle(
color: Colors.white,
),
rangeEndDecoration: BoxDecoration(
color: c_marron,
shape: BoxShape.circle,
border: Border.fromBorderSide(
BorderSide(
color: Colors.white,
width: 3,
style: BorderStyle.solid,
),
),
),
),
firstDay: kFirstDay,
rangeSelectionMode: _rangeSelectionMode,
onDaySelected: (selectedDay, focusedDay) {
if (!isSameDay(_selectedDay, selectedDay)) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
_rangeStart = null;
_rangeEnd = null;
_rangeSelectionMode = RangeSelectionMode.toggledOff;
widget.updateDates(selectedDay, _rangeEnd);
});
}
},
onRangeSelected: (start, end, focusedDay) {
setState(() {
_selectedDay = null;
_focusedDay = focusedDay;
_rangeStart = start;
_rangeEnd = end;
_rangeSelectionMode = RangeSelectionMode.toggledOn;
widget.updateDates(start, end);
});
},
onFormatChanged: (format) {
if (_calendarFormat != format) {
setState(() {
_calendarFormat = format;
});
}
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
),
);
}
}
Création d’un calendrier personnalisé avec Flutter (Partie 2)
-
Objectif
- Modifier l’application Flutter créée dans la première partie pour changer la langue en français, personnaliser les couleurs du texte du header du calendrier, colorer les dates de départ et de retour sélectionnées, changer le début de la semaine à lundi, et afficher les textes des samedis et dimanches en rouge.
-
Image de référence :
- Une capture d’écran de l’interface utilisateur attendue, montrant le calendrier avec les modifications spécifiées.
-
Énoncé de l’exercice
-
Description
- Vous allez améliorer l’application Flutter en effectuant les modifications suivantes :
- Changer la langue de l’application en français.
- Personnaliser la couleur du texte du header du calendrier.
- Colorer la date de départ en vert si elle est sélectionnée, sinon en noir.
- Colorer la date de retour en rouge si elle est sélectionnée, sinon en noir.
- Changer le début de la semaine pour qu’il commence le lundi.
- Afficher les textes des samedis et dimanches en rouge.
-
Instructions
-
Étape 1 : Initialisation de la locale en français
- Utilisez le package intl pour initialiser la locale en français.
- Assurez-vous que les formats de date français sont initialisés avant de démarrer l’application.
-
Étape 2 : Configuration du TableCalendar
- Changer la langue du calendrier :
- Définissez la locale du calendrier sur fr_FR pour afficher les dates en français.
- Personnaliser le style du header du calendrier :
- Modifiez la couleur du texte du header du calendrier pour une couleur spécifique.
- Colorer la date de départ et de retour :
- Utilisez une condition pour colorer la date de départ en vert si elle est sélectionnée, sinon en noir.
- Utilisez une condition pour colorer la date de retour en rouge si elle est sélectionnée, sinon en noir.
-
Changer le début de la semaine à lundi :
- Configurez le calendrier pour commencer la semaine le lundi.
-
Afficher les textes des samedis et dimanches en rouge :
- Personnalisez la couleur du texte des jours de week-end (samedi et dimanche) en rouge.
-
Questions
- Initialisation de la locale :
- Comment initialiser et configurer la locale en français pour une application Flutter ?
- Configuration de TableCalendar :
- Comment changer la langue du calendrier pour qu’il affiche les dates en français ?
- Comment modifier la couleur du texte du header du calendrier ?
- Comment configurer le calendrier pour qu’il commence la semaine le lundi ?
- Personnalisation des dates :
- Comment colorer la date de départ en vert si elle est sélectionnée et en noir sinon ?
- Comment colorer la date de retour en rouge si elle est sélectionnée et en noir sinon ?
- Comment afficher les textes des samedis et dimanches en rouge ?
-
Réalisation
- Basé sur l’image fournie et les instructions ci-dessus, réalisez les modifications nécessaires à l’application en répondant aux questions posées. Utilisez les concepts appris pour implémenter les fonctionnalités demandées. Bonne chance !
- Remarque : Vous pouvez trouver des exemples et la documentation complète de Flutter et des packages utilisés (comme table_calendar et intl) sur le site officiel de Flutter et les dépôts GitHub respectifs.
-
Solution
Code
import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
void main() {
// Initialize the localizations before running the app
initializeDateFormatting('fr_FR', null).then((_) {
runApp(MyApp());
});
}
DateTime kNow = DateTime.now();
DateTime kFirstDay = DateTime(kNow.year, kNow.month - 3, kNow.day);
DateTime kLastDay = DateTime(kNow.year, kNow.month + 3, kNow.day);
const c_marron = Color(0xFF94812b);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: CalendarPage(),
);
}
}
class CalendarPage extends StatefulWidget {
@override
_CalendarPageState createState() => _CalendarPageState();
}
class _CalendarPageState extends State {
DateTime? _departureDate;
DateTime? _returnDate;
void _updateDates(DateTime? departure, DateTime? returnDate) {
setState(() {
_departureDate = departure;
_returnDate = returnDate;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(),
body: Column(
children: [
PeriodSection(departureDate: _departureDate, returnDate: _returnDate),
CalendarRange(updateDates: _updateDates),
],
),
);
}
}
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
Size get preferredSize => new Size.fromHeight(50);
@override
Widget build(BuildContext context) {
return AppBar(
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.grey[800],
size: 20,
),
onPressed: null,
),
title: Text(
'Réservation',
style: GoogleFonts.nunito(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.w800,
),
),
actions: [
IconButton(
icon: Icon(
Icons.favorite_outline_rounded,
color: Colors.grey[800],
size: 20,
),
onPressed: null,
),
IconButton(
icon: Icon(
Icons.place,
color: Colors.grey[800],
size: 20,
),
onPressed: null,
),
],
centerTitle: true,
backgroundColor: Colors.white,
);
}
}
class PeriodSection extends StatelessWidget {
final DateTime? departureDate;
final DateTime? returnDate;
PeriodSection({this.departureDate, this.returnDate});
String formatDate(DateTime? date) {
if (date == null) return 'Non sélectionnée';
return DateFormat.yMMMd('fr_FR').format(date);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Départ',
textAlign: TextAlign.left,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 16,
color: departureDate != null ? Colors.green : Colors.black,
),
),
SizedBox(
height: 4,
),
Text(
formatDate(departureDate),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: departureDate != null ? Colors.green : Colors.black,
),
),
],
),
Container(
height: 60,
width: 1,
color: Colors.grey[350],
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Retour',
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 16,
color: returnDate != null ? Colors.red : Colors.black,
),
),
SizedBox(
height: 4,
),
Text(
formatDate(returnDate),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: returnDate != null ? Colors.red : Colors.black,
),
),
],
),
],
),
Divider(
color: Colors.grey,
height: 1,
),
],
);
}
}
class CalendarRange extends StatefulWidget {
final Function(DateTime?, DateTime?) updateDates;
CalendarRange({required this.updateDates});
@override
_CalendarRangeState createState() => _CalendarRangeState();
}
class _CalendarRangeState extends State {
CalendarFormat _calendarFormat = CalendarFormat.month;
RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOn;
DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay;
DateTime? _rangeStart;
DateTime? _rangeEnd;
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(15),
color: Colors.grey[10],
child: TableCalendar(
locale: 'fr_FR',
lastDay: kLastDay,
focusedDay: _focusedDay,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
rangeStartDay: _rangeStart,
rangeEndDay: _rangeEnd,
calendarFormat: _calendarFormat,
startingDayOfWeek: StartingDayOfWeek.monday,
headerStyle: HeaderStyle(
titleCentered: true,
formatButtonVisible: false,
titleTextStyle: TextStyle(fontSize: 20, color: Colors.blue),
),
calendarStyle: CalendarStyle(
todayDecoration: BoxDecoration(
color: c_marron,
shape: BoxShape.circle,
),
rangeHighlightColor: c_marron,
rangeStartDecoration: BoxDecoration(
color: c_marron,
shape: BoxShape.circle,
border: Border.fromBorderSide(
BorderSide(
color: Colors.white,
width: 3,
style: BorderStyle.solid,
),
),
),
withinRangeTextStyle: TextStyle(
color: Colors.white,
),
rangeEndDecoration: BoxDecoration(
color: c_marron,
shape: BoxShape.circle,
border: Border.fromBorderSide(
BorderSide(
color: Colors.white,
width: 3,
style: BorderStyle.solid,
),
),
),
weekendTextStyle: TextStyle(color: Colors.red),
holidayTextStyle: TextStyle(color: Colors.purple),
),
firstDay: kFirstDay,
rangeSelectionMode: _rangeSelectionMode,
onDaySelected: (selectedDay, focusedDay) {
setState(() {
if (_rangeStart == null || _rangeEnd != null) {
_rangeStart = selectedDay;
_rangeEnd = null;
} else if (selectedDay.isAfter(_rangeStart!)) {
_rangeEnd = selectedDay;
} else {
_rangeStart = selectedDay;
_rangeEnd = null;
}
_focusedDay = focusedDay;
widget.updateDates(_rangeStart, _rangeEnd);
});
},
onRangeSelected: (start, end, focusedDay) {
setState(() {
_selectedDay = null;
_focusedDay = focusedDay;
_rangeStart = start;
_rangeEnd = end;
_rangeSelectionMode = RangeSelectionMode.toggledOn;
widget.updateDates(start, end);
});
},
onFormatChanged: (format) {
if (_calendarFormat != format) {
setState(() {
_calendarFormat = format;
});
}
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
),
);
}
}