feat: ajout du parcours complet d’inscription parent avec UI harmonisée et gestion centralisée des données
This commit is contained in:
@@ -5,6 +5,8 @@ import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:p_tits_pas/services/bug_report_service.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../widgets/image_button.dart';
|
||||
import '../../widgets/custom_app_text_field.dart';
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
@@ -103,68 +105,40 @@ class _LoginPageState extends State<LoginPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Labels au-dessus des champs
|
||||
// Champs côte à côte
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Email',
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 20,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 20),
|
||||
child: Text(
|
||||
'Mot de passe',
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 20,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// Champs côte à côte
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _ImageTextField(
|
||||
bg: 'assets/images/field_email.png',
|
||||
width: 400,
|
||||
height: 53,
|
||||
hint: 'Email',
|
||||
child: CustomAppTextField(
|
||||
controller: _emailController,
|
||||
labelText: 'Email',
|
||||
hintText: 'Votre adresse email',
|
||||
validator: _validateEmail,
|
||||
style: CustomAppTextFieldStyle.lavande,
|
||||
fieldHeight: 53,
|
||||
fieldWidth: double.infinity,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: _ImageTextField(
|
||||
bg: 'assets/images/field_password.png',
|
||||
width: 400,
|
||||
height: 53,
|
||||
hint: 'Mot de passe',
|
||||
obscure: true,
|
||||
child: CustomAppTextField(
|
||||
controller: _passwordController,
|
||||
labelText: 'Mot de passe',
|
||||
hintText: 'Votre mot de passe',
|
||||
obscureText: true,
|
||||
validator: _validatePassword,
|
||||
style: CustomAppTextFieldStyle.jaune,
|
||||
fieldHeight: 53,
|
||||
fieldWidth: double.infinity,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20), // Réduit l'espacement
|
||||
const SizedBox(height: 20),
|
||||
// Bouton centré
|
||||
Center(
|
||||
child: _ImageButton(
|
||||
child: ImageButton(
|
||||
bg: 'assets/images/btn_green.png',
|
||||
width: 300,
|
||||
height: 40,
|
||||
@@ -199,7 +173,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, '/parent-register');
|
||||
Navigator.pushNamed(context, '/register-choice');
|
||||
},
|
||||
child: Text(
|
||||
'Créer un compte',
|
||||
@@ -393,120 +367,6 @@ class ImageDimensions {
|
||||
ImageDimensions({required this.width, required this.height});
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
// Champ texte avec fond image
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
class _ImageTextField extends StatelessWidget {
|
||||
final String bg;
|
||||
final double width;
|
||||
final double height;
|
||||
final String hint;
|
||||
final bool obscure;
|
||||
final TextEditingController? controller;
|
||||
final String? Function(String?)? validator;
|
||||
|
||||
const _ImageTextField({
|
||||
required this.bg,
|
||||
required this.width,
|
||||
required this.height,
|
||||
required this.hint,
|
||||
this.obscure = false,
|
||||
this.controller,
|
||||
this.validator,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: width,
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(bg),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
obscureText: obscure,
|
||||
textAlign: TextAlign.left,
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: height * 0.25,
|
||||
color: Colors.black87,
|
||||
),
|
||||
validator: validator,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: hint,
|
||||
hintStyle: GoogleFonts.merienda(
|
||||
fontSize: height * 0.25,
|
||||
color: Colors.black38,
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: width * 0.1,
|
||||
vertical: height * 0.3,
|
||||
),
|
||||
errorStyle: GoogleFonts.merienda(
|
||||
fontSize: height * 0.2,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
// Bouton avec fond image
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
class _ImageButton extends StatelessWidget {
|
||||
final String bg;
|
||||
final double width;
|
||||
final double height;
|
||||
final String text;
|
||||
final Color textColor;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
const _ImageButton({
|
||||
required this.bg,
|
||||
required this.width,
|
||||
required this.height,
|
||||
required this.text,
|
||||
required this.textColor,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: width,
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(bg),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onPressed,
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: height * 0.4,
|
||||
color: textColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
// Lien du pied de page
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'dart:math' as math; // Pour la rotation du chevron
|
||||
import '../../models/user_registration_data.dart'; // Import du modèle de données
|
||||
import '../../utils/data_generator.dart'; // Import du générateur de données
|
||||
import '../../widgets/custom_app_text_field.dart'; // Import du widget CustomAppTextField
|
||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
||||
|
||||
class ParentRegisterStep1Screen extends StatefulWidget {
|
||||
const ParentRegisterStep1Screen({super.key});
|
||||
|
||||
@override
|
||||
State<ParentRegisterStep1Screen> createState() => _ParentRegisterStep1ScreenState();
|
||||
}
|
||||
|
||||
class _ParentRegisterStep1ScreenState extends State<ParentRegisterStep1Screen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late UserRegistrationData _registrationData;
|
||||
|
||||
// Contrôleurs pour les champs (restauration CP et Ville)
|
||||
final _lastNameController = TextEditingController();
|
||||
final _firstNameController = TextEditingController();
|
||||
final _phoneController = TextEditingController();
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
final _confirmPasswordController = TextEditingController();
|
||||
final _addressController = TextEditingController(); // Rue seule
|
||||
final _postalCodeController = TextEditingController(); // Restauré
|
||||
final _cityController = TextEditingController(); // Restauré
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registrationData = UserRegistrationData();
|
||||
_generateAndFillData();
|
||||
}
|
||||
|
||||
void _generateAndFillData() {
|
||||
final String genFirstName = DataGenerator.firstName();
|
||||
final String genLastName = DataGenerator.lastName();
|
||||
|
||||
// Utilisation des méthodes publiques de DataGenerator
|
||||
_addressController.text = DataGenerator.address();
|
||||
_postalCodeController.text = DataGenerator.postalCode();
|
||||
_cityController.text = DataGenerator.city();
|
||||
|
||||
_firstNameController.text = genFirstName;
|
||||
_lastNameController.text = genLastName;
|
||||
_phoneController.text = DataGenerator.phone();
|
||||
_emailController.text = DataGenerator.email(genFirstName, genLastName);
|
||||
_passwordController.text = DataGenerator.password();
|
||||
_confirmPasswordController.text = _passwordController.text;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_lastNameController.dispose();
|
||||
_firstNameController.dispose();
|
||||
_phoneController.dispose();
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
_confirmPasswordController.dispose();
|
||||
_addressController.dispose();
|
||||
_postalCodeController.dispose();
|
||||
_cityController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
// Fond papier
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
'assets/images/paper2.png',
|
||||
fit: BoxFit.cover,
|
||||
repeat: ImageRepeat.repeat,
|
||||
),
|
||||
),
|
||||
|
||||
// Contenu centré
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Indicateur d'étape (à rendre dynamique)
|
||||
Text(
|
||||
'Étape 1/5',
|
||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// Texte d'instruction
|
||||
Text(
|
||||
'Informations du Parent Principal',
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
|
||||
// Carte jaune contenant le formulaire
|
||||
Container(
|
||||
width: screenSize.width * 0.6,
|
||||
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50),
|
||||
constraints: const BoxConstraints(minHeight: 570),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(CardColorHorizontal.peach.path),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _lastNameController, labelText: 'Nom', hintText: 'Votre nom de famille', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _firstNameController, labelText: 'Prénom', hintText: 'Votre prénom', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _phoneController, labelText: 'Téléphone', keyboardType: TextInputType.phone, hintText: 'Votre numéro de téléphone', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _emailController, labelText: 'Email', keyboardType: TextInputType.emailAddress, hintText: 'Votre adresse e-mail', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _passwordController, labelText: 'Mot de passe', obscureText: true, hintText: 'Créez votre mot de passe', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, validator: (value) {
|
||||
if (value == null || value.isEmpty) return 'Mot de passe requis';
|
||||
if (value.length < 6) return '6 caractères minimum';
|
||||
return null;
|
||||
})),
|
||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _confirmPasswordController, labelText: 'Confirmation', obscureText: true, hintText: 'Confirmez le mot de passe', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, validator: (value) {
|
||||
if (value == null || value.isEmpty) return 'Confirmation requise';
|
||||
if (value != _passwordController.text) return 'Ne correspond pas';
|
||||
return null;
|
||||
})),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
CustomAppTextField(
|
||||
controller: _addressController,
|
||||
labelText: 'Adresse (N° et Rue)',
|
||||
hintText: 'Numéro et nom de votre rue',
|
||||
style: CustomAppTextFieldStyle.beige,
|
||||
fieldWidth: double.infinity,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: CustomAppTextField(controller: _postalCodeController, labelText: 'Code Postal', keyboardType: TextInputType.number, hintText: 'Code postal', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(flex: 4, child: CustomAppTextField(controller: _cityController, labelText: 'Ville', hintText: 'Votre ville', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Chevron de navigation gauche (Retour)
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20, // Centré verticalement
|
||||
left: 40,
|
||||
child: IconButton(
|
||||
icon: Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.rotationY(math.pi), // Inverse horizontalement
|
||||
child: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context), // Retour à l'écran de choix
|
||||
tooltip: 'Retour',
|
||||
),
|
||||
),
|
||||
|
||||
// Chevron de navigation droit (Suivant)
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20, // Centré verticalement
|
||||
right: 40,
|
||||
child: IconButton(
|
||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||
onPressed: () {
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
_registrationData.updateParent1(
|
||||
ParentData(
|
||||
firstName: _firstNameController.text,
|
||||
lastName: _lastNameController.text,
|
||||
address: _addressController.text, // Rue
|
||||
postalCode: _postalCodeController.text, // Ajout
|
||||
city: _cityController.text, // Ajout
|
||||
phone: _phoneController.text,
|
||||
email: _emailController.text,
|
||||
password: _passwordController.text,
|
||||
)
|
||||
);
|
||||
Navigator.pushNamed(context, '/parent-register/step2', arguments: _registrationData);
|
||||
}
|
||||
},
|
||||
tooltip: 'Suivant',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'dart:math' as math; // Pour la rotation du chevron
|
||||
import '../../models/user_registration_data.dart'; // Import du modèle
|
||||
import '../../utils/data_generator.dart'; // Import du générateur
|
||||
import '../../widgets/custom_app_text_field.dart'; // Import du widget
|
||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
||||
|
||||
class ParentRegisterStep2Screen extends StatefulWidget {
|
||||
final UserRegistrationData registrationData; // Accepte les données de l'étape 1
|
||||
|
||||
const ParentRegisterStep2Screen({super.key, required this.registrationData});
|
||||
|
||||
@override
|
||||
State<ParentRegisterStep2Screen> createState() => _ParentRegisterStep2ScreenState();
|
||||
}
|
||||
|
||||
class _ParentRegisterStep2ScreenState extends State<ParentRegisterStep2Screen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late UserRegistrationData _registrationData; // Copie locale pour modification
|
||||
|
||||
bool _addParent2 = true; // Pour le test, on ajoute toujours le parent 2
|
||||
bool _sameAddressAsParent1 = false; // Peut être généré aléatoirement aussi
|
||||
|
||||
// Contrôleurs pour les champs du parent 2 (restauration CP et Ville)
|
||||
final _lastNameController = TextEditingController();
|
||||
final _firstNameController = TextEditingController();
|
||||
final _phoneController = TextEditingController();
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
final _confirmPasswordController = TextEditingController();
|
||||
final _addressController = TextEditingController(); // Rue seule
|
||||
final _postalCodeController = TextEditingController(); // Restauré
|
||||
final _cityController = TextEditingController(); // Restauré
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registrationData = widget.registrationData; // Récupère les données de l'étape 1
|
||||
if (_addParent2) {
|
||||
_generateAndFillParent2Data();
|
||||
}
|
||||
}
|
||||
|
||||
void _generateAndFillParent2Data() {
|
||||
final String genFirstName = DataGenerator.firstName();
|
||||
final String genLastName = DataGenerator.lastName();
|
||||
_firstNameController.text = genFirstName;
|
||||
_lastNameController.text = genLastName;
|
||||
_phoneController.text = DataGenerator.phone();
|
||||
_emailController.text = DataGenerator.email(genFirstName, genLastName);
|
||||
_passwordController.text = DataGenerator.password();
|
||||
_confirmPasswordController.text = _passwordController.text;
|
||||
|
||||
_sameAddressAsParent1 = DataGenerator.boolean();
|
||||
if (!_sameAddressAsParent1) {
|
||||
// Générer adresse, CP, Ville séparément
|
||||
_addressController.text = DataGenerator.address();
|
||||
_postalCodeController.text = DataGenerator.postalCode();
|
||||
_cityController.text = DataGenerator.city();
|
||||
} else {
|
||||
// Vider les champs si même adresse (seront désactivés)
|
||||
_addressController.clear();
|
||||
_postalCodeController.clear();
|
||||
_cityController.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_lastNameController.dispose();
|
||||
_firstNameController.dispose();
|
||||
_phoneController.dispose();
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
_confirmPasswordController.dispose();
|
||||
_addressController.dispose();
|
||||
_postalCodeController.dispose();
|
||||
_cityController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool get _parent2FieldsEnabled => _addParent2;
|
||||
bool get _addressFieldsEnabled => _addParent2 && !_sameAddressAsParent1;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
||||
),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Étape 2/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Informations du Deuxième Parent (Optionnel)',
|
||||
style: GoogleFonts.merienda(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Container(
|
||||
width: screenSize.width * 0.6,
|
||||
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 50),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(image: AssetImage(CardColorHorizontal.blue.path), fit: BoxFit.fill),
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: Row(children: [
|
||||
const Icon(Icons.person_add_alt_1, size: 20), const SizedBox(width: 8),
|
||||
Flexible(child: Text('Ajouter Parent 2 ?', style: GoogleFonts.merienda(fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis)),
|
||||
const Spacer(),
|
||||
Switch(value: _addParent2, onChanged: (val) => setState(() {
|
||||
_addParent2 = val ?? false;
|
||||
if (_addParent2) _generateAndFillParent2Data(); else _clearParent2Fields();
|
||||
}), activeColor: Theme.of(context).primaryColor),
|
||||
]),
|
||||
),
|
||||
Expanded(flex: 1, child: const SizedBox()),
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: Row(children: [
|
||||
Icon(Icons.home_work_outlined, size: 20, color: _addParent2 ? null : Colors.grey),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(child: Text('Même Adresse ?', style: GoogleFonts.merienda(color: _addParent2 ? null : Colors.grey), overflow: TextOverflow.ellipsis)),
|
||||
const Spacer(),
|
||||
Switch(value: _sameAddressAsParent1, onChanged: _addParent2 ? (val) => setState(() {
|
||||
_sameAddressAsParent1 = val ?? false;
|
||||
if (_sameAddressAsParent1) {
|
||||
_addressController.text = _registrationData.parent1.address;
|
||||
_postalCodeController.text = _registrationData.parent1.postalCode;
|
||||
_cityController.text = _registrationData.parent1.city;
|
||||
} else {
|
||||
_addressController.text = DataGenerator.address();
|
||||
_postalCodeController.text = DataGenerator.postalCode();
|
||||
_cityController.text = DataGenerator.city();
|
||||
}
|
||||
}) : null, activeColor: Theme.of(context).primaryColor),
|
||||
]),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _lastNameController, labelText: 'Nom', hintText: 'Nom du parent 2', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _firstNameController, labelText: 'Prénom', hintText: 'Prénom du parent 2', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _phoneController, labelText: 'Téléphone', keyboardType: TextInputType.phone, hintText: 'Son téléphone', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _emailController, labelText: 'Email', keyboardType: TextInputType.emailAddress, hintText: 'Son email', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _passwordController, labelText: 'Mot de passe', obscureText: true, hintText: 'Son mot de passe', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, validator: _addParent2 ? (v) => (v == null || v.isEmpty ? 'Requis' : (v.length < 6 ? '6 car. min' : null)) : null)),
|
||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
||||
Expanded(flex: 12, child: CustomAppTextField(controller: _confirmPasswordController, labelText: 'Confirmation', obscureText: true, hintText: 'Confirmer mot de passe', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, validator: _addParent2 ? (v) => (v == null || v.isEmpty ? 'Requis' : (v != _passwordController.text ? 'Différent' : null)) : null)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
CustomAppTextField(controller: _addressController, labelText: 'Adresse (N° et Rue)', hintText: 'Son numéro et nom de rue', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: CustomAppTextField(controller: _postalCodeController, labelText: 'Code Postal', keyboardType: TextInputType.number, hintText: 'Son code postal', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(flex: 4, child: CustomAppTextField(controller: _cityController, labelText: 'Ville', hintText: 'Sa ville', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
left: 40,
|
||||
child: IconButton(
|
||||
icon: Transform(alignment: Alignment.center, transform: Matrix4.rotationY(math.pi), child: Image.asset('assets/images/chevron_right.png', height: 40)),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
tooltip: 'Retour',
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
right: 40,
|
||||
child: IconButton(
|
||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||
onPressed: () {
|
||||
if (!_addParent2 || (_formKey.currentState?.validate() ?? false)) {
|
||||
if (_addParent2) {
|
||||
_registrationData.updateParent2(
|
||||
ParentData(
|
||||
firstName: _firstNameController.text,
|
||||
lastName: _lastNameController.text,
|
||||
address: _sameAddressAsParent1 ? _registrationData.parent1.address : _addressController.text,
|
||||
postalCode: _sameAddressAsParent1 ? _registrationData.parent1.postalCode : _postalCodeController.text,
|
||||
city: _sameAddressAsParent1 ? _registrationData.parent1.city : _cityController.text,
|
||||
phone: _phoneController.text,
|
||||
email: _emailController.text,
|
||||
password: _passwordController.text,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
_registrationData.updateParent2(null);
|
||||
}
|
||||
Navigator.pushNamed(context, '/parent-register/step3', arguments: _registrationData);
|
||||
}
|
||||
},
|
||||
tooltip: 'Suivant',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _clearParent2Fields() {
|
||||
_formKey.currentState?.reset();
|
||||
_lastNameController.clear(); _firstNameController.clear(); _phoneController.clear();
|
||||
_emailController.clear(); _passwordController.clear(); _confirmPasswordController.clear();
|
||||
_addressController.clear();
|
||||
_postalCodeController.clear();
|
||||
_cityController.clear();
|
||||
_sameAddressAsParent1 = false;
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'dart:math' as math; // Pour la rotation du chevron
|
||||
import 'package:flutter/gestures.dart'; // Pour PointerDeviceKind
|
||||
import '../../widgets/hover_relief_widget.dart'; // Import du nouveau widget
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
// import 'package:image_cropper/image_cropper.dart'; // Supprimé
|
||||
import 'dart:io' show File, Platform; // Ajout de Platform
|
||||
import 'package:flutter/foundation.dart' show kIsWeb; // Import pour kIsWeb
|
||||
import '../../widgets/custom_app_text_field.dart'; // Import du nouveau widget TextField
|
||||
import '../../widgets/app_custom_checkbox.dart'; // Import du nouveau widget Checkbox
|
||||
import '../../models/user_registration_data.dart'; // Import du modèle de données
|
||||
import '../../utils/data_generator.dart'; // Import du générateur
|
||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
||||
|
||||
// La classe _ChildFormData est supprimée car on utilise ChildData du modèle
|
||||
|
||||
class ParentRegisterStep3Screen extends StatefulWidget {
|
||||
final UserRegistrationData registrationData; // Accepte les données
|
||||
|
||||
const ParentRegisterStep3Screen({super.key, required this.registrationData});
|
||||
|
||||
@override
|
||||
State<ParentRegisterStep3Screen> createState() => _ParentRegisterStep3ScreenState();
|
||||
}
|
||||
|
||||
class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
|
||||
late UserRegistrationData _registrationData; // Stocke l'état complet
|
||||
final ScrollController _scrollController = ScrollController(); // Pour le défilement horizontal
|
||||
bool _isScrollable = false;
|
||||
bool _showLeftFade = false;
|
||||
bool _showRightFade = false;
|
||||
static const double _fadeExtent = 0.05; // Pourcentage de fondu
|
||||
|
||||
// Liste ordonnée des couleurs de cartes pour les enfants
|
||||
static const List<CardColorVertical> _childCardColors = [
|
||||
CardColorVertical.lavender, // Premier enfant toujours lavande
|
||||
CardColorVertical.pink,
|
||||
CardColorVertical.peach,
|
||||
CardColorVertical.lime,
|
||||
CardColorVertical.red,
|
||||
CardColorVertical.green,
|
||||
CardColorVertical.blue,
|
||||
];
|
||||
|
||||
// Garder une trace des couleurs déjà utilisées
|
||||
final Set<CardColorVertical> _usedColors = {};
|
||||
|
||||
// Utilisation de GlobalKey pour les cartes enfants si validation complexe future
|
||||
// Map<int, GlobalKey<FormState>> _childFormKeys = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registrationData = widget.registrationData;
|
||||
// Initialiser les couleurs utilisées avec les enfants existants
|
||||
for (var child in _registrationData.children) {
|
||||
_usedColors.add(child.cardColor);
|
||||
}
|
||||
// S'il n'y a pas d'enfant, en ajouter un automatiquement avec des données générées
|
||||
if (_registrationData.children.isEmpty) {
|
||||
_addChild();
|
||||
}
|
||||
_scrollController.addListener(_scrollListener);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollListener());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.removeListener(_scrollListener);
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _scrollListener() {
|
||||
if (!_scrollController.hasClients) return;
|
||||
final position = _scrollController.position;
|
||||
final newIsScrollable = position.maxScrollExtent > 0.0;
|
||||
final newShowLeftFade = newIsScrollable && position.pixels > (position.viewportDimension * _fadeExtent / 2);
|
||||
final newShowRightFade = newIsScrollable && position.pixels < (position.maxScrollExtent - (position.viewportDimension * _fadeExtent / 2));
|
||||
if (newIsScrollable != _isScrollable || newShowLeftFade != _showLeftFade || newShowRightFade != _showRightFade) {
|
||||
setState(() {
|
||||
_isScrollable = newIsScrollable;
|
||||
_showLeftFade = newShowLeftFade;
|
||||
_showRightFade = newShowRightFade;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _addChild() {
|
||||
setState(() {
|
||||
bool isUnborn = DataGenerator.boolean();
|
||||
|
||||
// Trouver la première couleur non utilisée
|
||||
CardColorVertical cardColor = _childCardColors.firstWhere(
|
||||
(color) => !_usedColors.contains(color),
|
||||
orElse: () => _childCardColors[0], // Fallback sur la première couleur si toutes sont utilisées
|
||||
);
|
||||
|
||||
final newChild = ChildData(
|
||||
lastName: _registrationData.parent1.lastName,
|
||||
firstName: DataGenerator.firstName(),
|
||||
dob: DataGenerator.dob(isUnborn: isUnborn),
|
||||
isUnbornChild: isUnborn,
|
||||
photoConsent: DataGenerator.boolean(),
|
||||
multipleBirth: DataGenerator.boolean(),
|
||||
cardColor: cardColor,
|
||||
);
|
||||
_registrationData.addChild(newChild);
|
||||
_usedColors.add(cardColor);
|
||||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollListener();
|
||||
if (_scrollController.hasClients && _scrollController.position.maxScrollExtent > 0.0) {
|
||||
_scrollController.animateTo(_scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _removeChild(int index) {
|
||||
if (_registrationData.children.length > 1 && index >= 0 && index < _registrationData.children.length) {
|
||||
setState(() {
|
||||
// Ne pas retirer la couleur de _usedColors pour éviter sa réutilisation
|
||||
_registrationData.children.removeAt(index);
|
||||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollListener());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickImage(int childIndex) async {
|
||||
final ImagePicker picker = ImagePicker();
|
||||
try {
|
||||
final XFile? pickedFile = await picker.pickImage(
|
||||
source: ImageSource.gallery, imageQuality: 70, maxWidth: 1024, maxHeight: 1024);
|
||||
if (pickedFile != null) {
|
||||
setState(() {
|
||||
if (childIndex < _registrationData.children.length) {
|
||||
_registrationData.children[childIndex].imageFile = File(pickedFile.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) { print("Erreur image: $e"); }
|
||||
}
|
||||
|
||||
Future<void> _selectDate(BuildContext context, int childIndex) async {
|
||||
final ChildData currentChild = _registrationData.children[childIndex];
|
||||
final DateTime now = DateTime.now();
|
||||
DateTime initialDatePickerDate = now;
|
||||
DateTime firstDatePickerDate = DateTime(1980); DateTime lastDatePickerDate = now;
|
||||
|
||||
if (currentChild.isUnbornChild) {
|
||||
firstDatePickerDate = now; lastDatePickerDate = now.add(const Duration(days: 300));
|
||||
if (currentChild.dob.isNotEmpty) {
|
||||
try {
|
||||
List<String> parts = currentChild.dob.split('/');
|
||||
DateTime? parsedDate = DateTime.tryParse("${parts[2]}-${parts[1].padLeft(2, '0')}-${parts[0].padLeft(2, '0')}");
|
||||
if (parsedDate != null && !parsedDate.isBefore(firstDatePickerDate) && !parsedDate.isAfter(lastDatePickerDate)) {
|
||||
initialDatePickerDate = parsedDate;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
} else {
|
||||
if (currentChild.dob.isNotEmpty) {
|
||||
try {
|
||||
List<String> parts = currentChild.dob.split('/');
|
||||
DateTime? parsedDate = DateTime.tryParse("${parts[2]}-${parts[1].padLeft(2, '0')}-${parts[0].padLeft(2, '0')}");
|
||||
if (parsedDate != null && !parsedDate.isBefore(firstDatePickerDate) && !parsedDate.isAfter(lastDatePickerDate)) {
|
||||
initialDatePickerDate = parsedDate;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context, initialDate: initialDatePickerDate, firstDate: firstDatePickerDate,
|
||||
lastDate: lastDatePickerDate, locale: const Locale('fr', 'FR'),
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
currentChild.dob = "${picked.day.toString().padLeft(2, '0')}/${picked.month.toString().padLeft(2, '0')}/${picked.year}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Étape 3/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Informations Enfants',
|
||||
style: GoogleFonts.merienda(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 150.0),
|
||||
child: SizedBox(
|
||||
height: 684.0,
|
||||
child: ShaderMask(
|
||||
shaderCallback: (Rect bounds) {
|
||||
final Color leftFade = (_isScrollable && _showLeftFade) ? Colors.transparent : Colors.black;
|
||||
final Color rightFade = (_isScrollable && _showRightFade) ? Colors.transparent : Colors.black;
|
||||
if (!_isScrollable) { return LinearGradient(colors: const <Color>[Colors.black, Colors.black, Colors.black, Colors.black], stops: const [0.0, _fadeExtent, 1.0 - _fadeExtent, 1.0],).createShader(bounds); }
|
||||
return LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: <Color>[ leftFade, Colors.black, Colors.black, rightFade ], stops: const [0.0, _fadeExtent, 1.0 - _fadeExtent, 1.0], ).createShader(bounds);
|
||||
},
|
||||
blendMode: BlendMode.dstIn,
|
||||
child: Scrollbar(
|
||||
controller: _scrollController,
|
||||
thumbVisibility: true,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
itemCount: _registrationData.children.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index < _registrationData.children.length) {
|
||||
// Carte Enfant
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 20.0),
|
||||
child: _ChildCardWidget(
|
||||
key: ValueKey(_registrationData.children[index].hashCode), // Utiliser une clé basée sur les données
|
||||
childData: _registrationData.children[index],
|
||||
childIndex: index,
|
||||
onPickImage: () => _pickImage(index),
|
||||
onDateSelect: () => _selectDate(context, index),
|
||||
onFirstNameChanged: (value) => setState(() => _registrationData.children[index].firstName = value),
|
||||
onLastNameChanged: (value) => setState(() => _registrationData.children[index].lastName = value),
|
||||
onTogglePhotoConsent: (newValue) => setState(() => _registrationData.children[index].photoConsent = newValue),
|
||||
onToggleMultipleBirth: (newValue) => setState(() => _registrationData.children[index].multipleBirth = newValue),
|
||||
onToggleIsUnborn: (newValue) => setState(() {
|
||||
_registrationData.children[index].isUnbornChild = newValue;
|
||||
// Générer une nouvelle date si on change le statut
|
||||
_registrationData.children[index].dob = DataGenerator.dob(isUnborn: newValue);
|
||||
}),
|
||||
onRemove: () => _removeChild(index),
|
||||
canBeRemoved: _registrationData.children.length > 1,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Bouton Ajouter
|
||||
return Center(
|
||||
child: HoverReliefWidget(
|
||||
onPressed: _addChild,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
child: Image.asset('assets/images/plus.png', height: 80, width: 80),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Chevrons de navigation
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
left: 40,
|
||||
child: IconButton(
|
||||
icon: Transform(alignment: Alignment.center, transform: Matrix4.rotationY(math.pi), child: Image.asset('assets/images/chevron_right.png', height: 40)),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
tooltip: 'Retour',
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
right: 40,
|
||||
child: IconButton(
|
||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||
onPressed: () {
|
||||
// TODO: Validation (si nécessaire)
|
||||
Navigator.pushNamed(context, '/parent-register/step4', arguments: _registrationData);
|
||||
},
|
||||
tooltip: 'Suivant',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Widget pour la carte enfant (adapté pour prendre ChildData et des callbacks)
|
||||
class _ChildCardWidget extends StatefulWidget { // Transformé en StatefulWidget pour gérer les contrôleurs internes
|
||||
final ChildData childData;
|
||||
final int childIndex;
|
||||
final VoidCallback onPickImage;
|
||||
final VoidCallback onDateSelect;
|
||||
final ValueChanged<String> onFirstNameChanged;
|
||||
final ValueChanged<String> onLastNameChanged;
|
||||
final ValueChanged<bool> onTogglePhotoConsent;
|
||||
final ValueChanged<bool> onToggleMultipleBirth;
|
||||
final ValueChanged<bool> onToggleIsUnborn;
|
||||
final VoidCallback onRemove;
|
||||
final bool canBeRemoved;
|
||||
|
||||
const _ChildCardWidget({
|
||||
required Key key,
|
||||
required this.childData,
|
||||
required this.childIndex,
|
||||
required this.onPickImage,
|
||||
required this.onDateSelect,
|
||||
required this.onFirstNameChanged,
|
||||
required this.onLastNameChanged,
|
||||
required this.onTogglePhotoConsent,
|
||||
required this.onToggleMultipleBirth,
|
||||
required this.onToggleIsUnborn,
|
||||
required this.onRemove,
|
||||
required this.canBeRemoved,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<_ChildCardWidget> createState() => _ChildCardWidgetState();
|
||||
}
|
||||
|
||||
class _ChildCardWidgetState extends State<_ChildCardWidget> {
|
||||
late TextEditingController _firstNameController;
|
||||
late TextEditingController _lastNameController;
|
||||
late TextEditingController _dobController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Initialiser les contrôleurs avec les données du widget
|
||||
_firstNameController = TextEditingController(text: widget.childData.firstName);
|
||||
_lastNameController = TextEditingController(text: widget.childData.lastName);
|
||||
_dobController = TextEditingController(text: widget.childData.dob);
|
||||
|
||||
// Ajouter des listeners pour mettre à jour les données sources via les callbacks
|
||||
_firstNameController.addListener(() => widget.onFirstNameChanged(_firstNameController.text));
|
||||
_lastNameController.addListener(() => widget.onLastNameChanged(_lastNameController.text));
|
||||
// Pour dob, la mise à jour se fait via _selectDate, pas besoin de listener ici
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _ChildCardWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
// Mettre à jour les contrôleurs si les données externes changent
|
||||
// (peut arriver si on recharge l'état global)
|
||||
if (widget.childData.firstName != _firstNameController.text) {
|
||||
_firstNameController.text = widget.childData.firstName;
|
||||
}
|
||||
if (widget.childData.lastName != _lastNameController.text) {
|
||||
_lastNameController.text = widget.childData.lastName;
|
||||
}
|
||||
if (widget.childData.dob != _dobController.text) {
|
||||
_dobController.text = widget.childData.dob;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_firstNameController.dispose();
|
||||
_lastNameController.dispose();
|
||||
_dobController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final File? currentChildImage = widget.childData.imageFile;
|
||||
// Utiliser la couleur de la carte de childData pour l'ombre si besoin, ou directement pour le fond
|
||||
final Color baseCardColorForShadow = widget.childData.cardColor == CardColorVertical.lavender
|
||||
? Colors.purple.shade200
|
||||
: (widget.childData.cardColor == CardColorVertical.pink ? Colors.pink.shade200 : Colors.grey.shade200); // Placeholder pour autres couleurs
|
||||
final Color initialPhotoShadow = baseCardColorForShadow.withAlpha(90);
|
||||
final Color hoverPhotoShadow = baseCardColorForShadow.withAlpha(130);
|
||||
|
||||
return Container(
|
||||
width: 345.0 * 1.1, // 379.5
|
||||
height: 570.0 * 1.2, // 684.0
|
||||
padding: const EdgeInsets.all(22.0 * 1.1), // 24.2
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(image: AssetImage(widget.childData.cardColor.path), fit: BoxFit.cover),
|
||||
borderRadius: BorderRadius.circular(20 * 1.1), // 22
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
HoverReliefWidget(
|
||||
onPressed: widget.onPickImage,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
initialShadowColor: initialPhotoShadow,
|
||||
hoverShadowColor: hoverPhotoShadow,
|
||||
child: SizedBox(
|
||||
height: 200.0,
|
||||
width: 200.0,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0 * 1.1), // 5.5
|
||||
child: currentChildImage != null
|
||||
? ClipRRect(borderRadius: BorderRadius.circular(10 * 1.1), child: kIsWeb ? Image.network(currentChildImage.path, fit: BoxFit.cover) : Image.file(currentChildImage, fit: BoxFit.cover))
|
||||
: Image.asset('assets/images/photo.png', fit: BoxFit.contain),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12.0 * 1.1), // Augmenté pour plus d'espace après la photo
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Enfant à naître ?', style: GoogleFonts.merienda(fontSize: 16 * 1.1, fontWeight: FontWeight.w600)),
|
||||
Switch(value: widget.childData.isUnbornChild, onChanged: widget.onToggleIsUnborn, activeColor: Theme.of(context).primaryColor),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 9.0 * 1.1), // 9.9
|
||||
CustomAppTextField(
|
||||
controller: _firstNameController,
|
||||
labelText: 'Prénom',
|
||||
hintText: 'Facultatif si à naître',
|
||||
isRequired: !widget.childData.isUnbornChild,
|
||||
fieldHeight: 55.0 * 1.1, // 60.5
|
||||
),
|
||||
const SizedBox(height: 6.0 * 1.1), // 6.6
|
||||
CustomAppTextField(
|
||||
controller: _lastNameController,
|
||||
labelText: 'Nom',
|
||||
hintText: 'Nom de l\'enfant',
|
||||
enabled: true,
|
||||
fieldHeight: 55.0 * 1.1, // 60.5
|
||||
),
|
||||
const SizedBox(height: 9.0 * 1.1), // 9.9
|
||||
CustomAppTextField(
|
||||
controller: _dobController,
|
||||
labelText: widget.childData.isUnbornChild ? 'Date prévisionnelle de naissance' : 'Date de naissance',
|
||||
hintText: 'JJ/MM/AAAA',
|
||||
readOnly: true,
|
||||
onTap: widget.onDateSelect,
|
||||
suffixIcon: Icons.calendar_today,
|
||||
fieldHeight: 55.0 * 1.1, // 60.5
|
||||
),
|
||||
const SizedBox(height: 11.0 * 1.1), // 12.1
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AppCustomCheckbox(
|
||||
label: 'Consentement photo',
|
||||
value: widget.childData.photoConsent,
|
||||
onChanged: widget.onTogglePhotoConsent,
|
||||
checkboxSize: 22.0 * 1.1, // 24.2
|
||||
),
|
||||
const SizedBox(height: 6.0 * 1.1), // 6.6
|
||||
AppCustomCheckbox(
|
||||
label: 'Naissance multiple',
|
||||
value: widget.childData.multipleBirth,
|
||||
onChanged: widget.onToggleMultipleBirth,
|
||||
checkboxSize: 22.0 * 1.1, // 24.2
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.canBeRemoved)
|
||||
Positioned(
|
||||
top: -5, right: -5,
|
||||
child: InkWell(
|
||||
onTap: widget.onRemove,
|
||||
customBorder: const CircleBorder(),
|
||||
child: Image.asset(
|
||||
'images/red_cross2.png',
|
||||
width: 36,
|
||||
height: 36,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:p_tits_pas/widgets/custom_decorated_text_field.dart'; // Import du nouveau widget
|
||||
import 'dart:math' as math; // Pour la rotation du chevron
|
||||
import 'package:p_tits_pas/widgets/app_custom_checkbox.dart'; // Import de la checkbox personnalisée
|
||||
// import 'package:p_tits_pas/models/placeholder_registration_data.dart'; // Remplacé
|
||||
import '../../models/user_registration_data.dart'; // Import du vrai modèle
|
||||
import '../../utils/data_generator.dart'; // Import du générateur
|
||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
||||
|
||||
class ParentRegisterStep4Screen extends StatefulWidget {
|
||||
final UserRegistrationData registrationData; // Accepte les données
|
||||
|
||||
const ParentRegisterStep4Screen({super.key, required this.registrationData});
|
||||
|
||||
@override
|
||||
State<ParentRegisterStep4Screen> createState() => _ParentRegisterStep4ScreenState();
|
||||
}
|
||||
|
||||
class _ParentRegisterStep4ScreenState extends State<ParentRegisterStep4Screen> {
|
||||
late UserRegistrationData _registrationData; // État local
|
||||
final _motivationController = TextEditingController();
|
||||
bool _cguAccepted = true; // Pour le test, CGU acceptées par défaut
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registrationData = widget.registrationData;
|
||||
_motivationController.text = DataGenerator.motivation(); // Générer la motivation
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_motivationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _showCGUModal() {
|
||||
// Un long texte Lorem Ipsum pour simuler les CGU
|
||||
const String loremIpsumText = '''
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.
|
||||
|
||||
Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna.
|
||||
|
||||
Aliquam convallis sollicitudin purus. Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet.
|
||||
|
||||
Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.
|
||||
|
||||
Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna. Etiam et felis dolor.
|
||||
|
||||
Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
|
||||
|
||||
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
|
||||
''';
|
||||
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false, // L'utilisateur doit utiliser le bouton
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
'Conditions Générales d\'Utilisation',
|
||||
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: SizedBox(
|
||||
width: MediaQuery.of(dialogContext).size.width * 0.7, // 70% de la largeur de l'écran
|
||||
height: MediaQuery.of(dialogContext).size.height * 0.6, // 60% de la hauteur de l'écran
|
||||
child: SingleChildScrollView(
|
||||
child: Text(
|
||||
loremIpsumText,
|
||||
style: GoogleFonts.merienda(fontSize: 13),
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
),
|
||||
),
|
||||
actionsPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
|
||||
actionsAlignment: MainAxisAlignment.center,
|
||||
actions: <Widget>[
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(dialogContext).primaryColor,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
|
||||
),
|
||||
child: Text(
|
||||
'Valider et Accepter',
|
||||
style: GoogleFonts.merienda(fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(dialogContext).pop(); // Ferme la modale
|
||||
setState(() {
|
||||
_cguAccepted = true; // Met à jour l'état
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final cardWidth = screenSize.width * 0.6; // Largeur de la carte (60% de l'écran)
|
||||
final double imageAspectRatio = 2.0; // Ratio corrigé (1024/512 = 2.0)
|
||||
final cardHeight = cardWidth / imageAspectRatio;
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
||||
),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 50.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Étape 4/5',
|
||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'Motivation de votre demande',
|
||||
style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Container(
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(CardColorHorizontal.green.path),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(40.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomDecoratedTextField(
|
||||
controller: _motivationController,
|
||||
hintText: 'Écrivez ici pour motiver votre demande...',
|
||||
fieldHeight: cardHeight * 0.6,
|
||||
maxLines: 10,
|
||||
expandDynamically: true,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (!_cguAccepted) {
|
||||
_showCGUModal();
|
||||
}
|
||||
},
|
||||
child: AppCustomCheckbox(
|
||||
label: 'J\'accepte les conditions générales d\'utilisation',
|
||||
value: _cguAccepted,
|
||||
onChanged: (newValue) {
|
||||
if (!_cguAccepted) {
|
||||
_showCGUModal();
|
||||
} else {
|
||||
setState(() => _cguAccepted = false);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Chevrons de navigation
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
left: 40,
|
||||
child: IconButton(
|
||||
icon: Transform(alignment: Alignment.center, transform: Matrix4.rotationY(math.pi), child: Image.asset('assets/images/chevron_right.png', height: 40)),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
tooltip: 'Retour',
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
right: 40,
|
||||
child: IconButton(
|
||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||
onPressed: _cguAccepted
|
||||
? () {
|
||||
_registrationData.updateMotivation(_motivationController.text);
|
||||
_registrationData.acceptCGU();
|
||||
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
'/parent-register/step5',
|
||||
arguments: _registrationData
|
||||
);
|
||||
}
|
||||
: null,
|
||||
tooltip: 'Suivant',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../../models/user_registration_data.dart'; // Utilisation du vrai modèle
|
||||
import '../../widgets/image_button.dart'; // Import du ImageButton
|
||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import '../../widgets/custom_decorated_text_field.dart'; // Import du CustomDecoratedTextField
|
||||
|
||||
// Nouvelle méthode helper pour afficher un champ de type "lecture seule" stylisé
|
||||
Widget _buildDisplayFieldValue(BuildContext context, String label, String value, {bool multiLine = false, double fieldHeight = 50.0, double labelFontSize = 18.0}) {
|
||||
const FontWeight labelFontWeight = FontWeight.w600;
|
||||
|
||||
// Ne pas afficher le label si labelFontSize est 0 ou si label est vide
|
||||
bool showLabel = label.isNotEmpty && labelFontSize > 0;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (showLabel)
|
||||
Text(label, style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: labelFontWeight)),
|
||||
if (showLabel)
|
||||
const SizedBox(height: 4),
|
||||
// Utiliser Expanded si multiLine et pas de hauteur fixe, sinon Container
|
||||
multiLine && fieldHeight == null
|
||||
? Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
image: const DecorationImage(
|
||||
image: AssetImage('assets/images/input_field_bg.png'),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: SingleChildScrollView( // Pour le défilement si le texte dépasse
|
||||
child: Text(
|
||||
value.isNotEmpty ? value : '-',
|
||||
style: GoogleFonts.merienda(fontSize: labelFontSize > 0 ? labelFontSize : 18.0), // Garder une taille de texte par défaut si label caché
|
||||
maxLines: null, // Permettre un nombre illimité de lignes
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
width: double.infinity,
|
||||
height: multiLine ? null : fieldHeight,
|
||||
constraints: multiLine ? BoxConstraints(minHeight: fieldHeight) : null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
image: const DecorationImage(
|
||||
image: AssetImage('assets/images/input_field_bg.png'),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
value.isNotEmpty ? value : '-',
|
||||
style: GoogleFonts.merienda(fontSize: labelFontSize > 0 ? labelFontSize : 18.0),
|
||||
maxLines: multiLine ? null : 1,
|
||||
overflow: multiLine ? TextOverflow.visible : TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class ParentRegisterStep5Screen extends StatelessWidget {
|
||||
final UserRegistrationData registrationData;
|
||||
|
||||
const ParentRegisterStep5Screen({super.key, required this.registrationData});
|
||||
|
||||
// Méthode pour construire la carte Parent 1
|
||||
Widget _buildParent1Card(BuildContext context, ParentData data) {
|
||||
const double verticalSpacing = 28.0; // Espacement vertical augmenté
|
||||
const double labelFontSize = 22.0; // Taille de label augmentée
|
||||
|
||||
List<Widget> details = [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Nom:", data.lastName, labelFontSize: labelFontSize)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Prénom:", data.firstName, labelFontSize: labelFontSize)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Téléphone:", data.phone, labelFontSize: labelFontSize)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Email:", data.email, multiLine: true, labelFontSize: labelFontSize)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
_buildDisplayFieldValue(context, "Adresse:", "${data.address}\n${data.postalCode} ${data.city}".trim(), multiLine: true, fieldHeight: 80, labelFontSize: labelFontSize),
|
||||
];
|
||||
return _SummaryCard(
|
||||
backgroundImagePath: CardColorHorizontal.peach.path,
|
||||
title: 'Parent Principal',
|
||||
content: details,
|
||||
onEdit: () => Navigator.of(context).pushNamed('/parent-register/step1', arguments: registrationData),
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode pour construire la carte Parent 2
|
||||
Widget _buildParent2Card(BuildContext context, ParentData data) {
|
||||
const double verticalSpacing = 28.0;
|
||||
const double labelFontSize = 22.0;
|
||||
List<Widget> details = [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Nom:", data.lastName, labelFontSize: labelFontSize)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Prénom:", data.firstName, labelFontSize: labelFontSize)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Téléphone:", data.phone, labelFontSize: labelFontSize)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Email:", data.email, multiLine: true, labelFontSize: labelFontSize)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
_buildDisplayFieldValue(context, "Adresse:", "${data.address}\n${data.postalCode} ${data.city}".trim(), multiLine: true, fieldHeight: 80, labelFontSize: labelFontSize),
|
||||
];
|
||||
return _SummaryCard(
|
||||
backgroundImagePath: CardColorHorizontal.blue.path,
|
||||
title: 'Deuxième Parent',
|
||||
content: details,
|
||||
onEdit: () => Navigator.of(context).pushNamed('/parent-register/step2', arguments: registrationData),
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode pour construire les cartes Enfants
|
||||
List<Widget> _buildChildrenCards(BuildContext context, List<ChildData> children) {
|
||||
return children.asMap().entries.map((entry) {
|
||||
int index = entry.key;
|
||||
ChildData child = entry.value;
|
||||
|
||||
CardColorHorizontal cardColorHorizontal = CardColorHorizontal.values.firstWhere(
|
||||
(e) => e.name == child.cardColor.name,
|
||||
orElse: () => CardColorHorizontal.lavender,
|
||||
);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20.0),
|
||||
child: Stack(
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 2.0,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 25.0),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(cardColorHorizontal.path),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// Titre centré dans la carte
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Enfant ${index + 1}' + (child.isUnbornChild ? ' (à naître)' : ''),
|
||||
style: GoogleFonts.merienda(fontSize: 28, fontWeight: FontWeight.w600),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit, color: Colors.black54, size: 28),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
'/parent-register/step3',
|
||||
arguments: registrationData,
|
||||
);
|
||||
},
|
||||
tooltip: 'Modifier',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// IMAGE SANS CADRE BLANC, PREND LA HAUTEUR
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: (child.imageFile != null)
|
||||
? (kIsWeb
|
||||
? Image.network(child.imageFile!.path, fit: BoxFit.cover)
|
||||
: Image.file(child.imageFile!, fit: BoxFit.cover))
|
||||
: Image.asset('assets/images/photo.png', fit: BoxFit.contain),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 32),
|
||||
// INFOS À DROITE (2/3)
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildDisplayFieldValue(context, 'Prénom :', child.firstName, labelFontSize: 22.0),
|
||||
const SizedBox(height: 12),
|
||||
_buildDisplayFieldValue(context, 'Nom :', child.lastName, labelFontSize: 22.0),
|
||||
const SizedBox(height: 12),
|
||||
_buildDisplayFieldValue(context, child.isUnbornChild ? 'Date de naissance :' : 'Date de naissance :', child.dob, labelFontSize: 22.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
// Ligne des consentements
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: child.photoConsent,
|
||||
onChanged: null,
|
||||
),
|
||||
Text('Consentement photo', style: GoogleFonts.merienda(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 32),
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: child.multipleBirth,
|
||||
onChanged: null,
|
||||
),
|
||||
Text('Naissance multiple', style: GoogleFonts.merienda(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Méthode pour construire la carte Motivation
|
||||
Widget _buildMotivationCard(BuildContext context, String motivation) {
|
||||
return _SummaryCard(
|
||||
backgroundImagePath: CardColorHorizontal.green.path,
|
||||
title: 'Votre Motivation',
|
||||
content: [
|
||||
Expanded(
|
||||
child: CustomDecoratedTextField(
|
||||
controller: TextEditingController(text: motivation),
|
||||
hintText: 'Aucune motivation renseignée.',
|
||||
fieldHeight: 200,
|
||||
maxLines: 10,
|
||||
expandDynamically: true,
|
||||
readOnly: true,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
onEdit: () => Navigator.of(context).pushNamed('/parent-register/step4', arguments: registrationData),
|
||||
);
|
||||
}
|
||||
|
||||
// Helper pour afficher une ligne de détail (police et agencement amélioré)
|
||||
Widget _buildDetailRow(String label, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"$label: ",
|
||||
style: GoogleFonts.merienda(fontSize: 18, fontWeight: FontWeight.w600),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value.isNotEmpty ? value : '-',
|
||||
style: GoogleFonts.merienda(fontSize: 18),
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final cardWidth = screenSize.width / 2.0; // Largeur de la carte (50% de l'écran)
|
||||
final double imageAspectRatio = 2.0; // Ratio corrigé (1024/512 = 2.0)
|
||||
final cardHeight = cardWidth / imageAspectRatio;
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeatY),
|
||||
),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 40.0), // Padding horizontal supprimé ici
|
||||
child: Padding( // Ajout du Padding horizontal externe
|
||||
padding: EdgeInsets.symmetric(horizontal: screenSize.width / 4.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text('Étape 5/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
||||
const SizedBox(height: 20),
|
||||
Text('Récapitulatif de votre demande', style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), textAlign: TextAlign.center),
|
||||
const SizedBox(height: 30),
|
||||
|
||||
_buildParent1Card(context, registrationData.parent1),
|
||||
const SizedBox(height: 20),
|
||||
if (registrationData.parent2 != null) ...[
|
||||
_buildParent2Card(context, registrationData.parent2!),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
..._buildChildrenCards(context, registrationData.children),
|
||||
_buildMotivationCard(context, registrationData.motivationText),
|
||||
const SizedBox(height: 40),
|
||||
ImageButton(
|
||||
bg: 'assets/images/btn_green.png',
|
||||
text: 'Soumettre ma demande',
|
||||
textColor: const Color(0xFF2D6A4F),
|
||||
width: 350,
|
||||
height: 50,
|
||||
fontSize: 18,
|
||||
onPressed: () {
|
||||
print("Données finales: ${registrationData.parent1.firstName}, Enfant(s): ${registrationData.children.length}");
|
||||
_showConfirmationModal(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
left: 40,
|
||||
child: IconButton(
|
||||
icon: Transform.flip(flipX: true, child: Image.asset('assets/images/chevron_right.png', height: 40)),
|
||||
onPressed: () => Navigator.pop(context), // Retour à l'étape 4
|
||||
tooltip: 'Retour',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showConfirmationModal(BuildContext context) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
'Demande enregistrée',
|
||||
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: Text(
|
||||
'Votre dossier a bien été pris en compte. Un gestionnaire le validera bientôt.',
|
||||
style: GoogleFonts.merienda(fontSize: 14),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)),
|
||||
onPressed: () {
|
||||
Navigator.of(dialogContext).pop(); // Ferme la modale
|
||||
// TODO: Naviguer vers l'écran de connexion ou tableau de bord
|
||||
Navigator.of(context).pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Widget générique _SummaryCard (ajusté)
|
||||
class _SummaryCard extends StatelessWidget {
|
||||
final String backgroundImagePath;
|
||||
final String title;
|
||||
final List<Widget> content;
|
||||
final VoidCallback onEdit;
|
||||
|
||||
const _SummaryCard({
|
||||
super.key,
|
||||
required this.backgroundImagePath,
|
||||
required this.title,
|
||||
required this.content,
|
||||
required this.onEdit,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 2.0,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 25.0),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(backgroundImagePath),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: GoogleFonts.merienda(fontSize: 28, fontWeight: FontWeight.w600),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit, color: Colors.black54, size: 28),
|
||||
onPressed: onEdit,
|
||||
tooltip: 'Modifier',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: content,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'dart:math' as math; // Pour la rotation du chevron
|
||||
import '../../widgets/hover_relief_widget.dart'; // Import du widget générique
|
||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
||||
|
||||
class RegisterChoiceScreen extends StatelessWidget {
|
||||
const RegisterChoiceScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
// Fond papier
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
'assets/images/paper2.png',
|
||||
fit: BoxFit.cover,
|
||||
repeat: ImageRepeat.repeat,
|
||||
),
|
||||
),
|
||||
|
||||
// Bouton Retour (chevron gauche)
|
||||
Positioned(
|
||||
top: 40,
|
||||
left: 40,
|
||||
child: IconButton(
|
||||
icon: Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.rotationY(math.pi),
|
||||
child: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
tooltip: 'Retour',
|
||||
),
|
||||
),
|
||||
|
||||
// Contenu principal en Row (Gauche / Droite)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: screenSize.width * 0.05),
|
||||
child: Row(
|
||||
children: [
|
||||
// Partie Gauche: Texte d'instruction centré
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Veuillez choisir votre\ntype de compte :',
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
height: 1.5,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
// Espace entre les deux parties
|
||||
SizedBox(width: screenSize.width * 0.05),
|
||||
|
||||
// Partie Droite: Carte rose avec les boutons
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: screenSize.height * 0.78, // Augmenté pour éviter l'overflow
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 2 / 3,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(CardColorVertical.pink.path),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// Bouton "Parents" avec HoverReliefWidget appliqué uniquement à l'image
|
||||
_buildChoiceButton(
|
||||
context: context,
|
||||
iconPath: 'assets/images/icon_parents.png',
|
||||
label: 'Parents',
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, '/parent-register/step1');
|
||||
},
|
||||
),
|
||||
// Bouton "Assistante Maternelle" avec HoverReliefWidget appliqué uniquement à l'image
|
||||
_buildChoiceButton(
|
||||
context: context,
|
||||
iconPath: 'assets/images/icon_assmat.png',
|
||||
label: 'Assistante Maternelle',
|
||||
onPressed: () {
|
||||
// TODO: Naviguer vers l'écran d'inscription assmat
|
||||
print('Choix: Assistante Maternelle');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Nouvelle méthode helper pour construire les boutons de choix
|
||||
Widget _buildChoiceButton({
|
||||
required BuildContext context,
|
||||
required String iconPath,
|
||||
required String label,
|
||||
required VoidCallback onPressed,
|
||||
}) {
|
||||
// TODO: Déterminer la couleur de base de card_rose.png et ajuster ces couleurs d'ombre
|
||||
final Color baseRoseColor = Colors.pink.shade300; // Placeholder
|
||||
final Color initialShadow = baseRoseColor.withAlpha(90); // Rose plus foncé et transparent pour l'ombre initiale
|
||||
final Color hoverShadow = baseRoseColor.withAlpha(130); // Rose encore plus foncé pour l'ombre au survol
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
HoverReliefWidget(
|
||||
onPressed: onPressed,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
initialShadowColor: initialShadow, // Ombre rose initiale
|
||||
hoverShadowColor: hoverShadow, // Ombre rose au survol
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Image.asset(iconPath, height: 140),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Text(
|
||||
label,
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black.withOpacity(0.85),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// --- La classe HoverChoiceButton peut maintenant être supprimée si elle n'est plus utilisée ailleurs ---
|
||||
// class HoverChoiceButton extends StatefulWidget { ... }
|
||||
// class _HoverChoiceButtonState extends State<HoverChoiceButton> { ... }
|
||||
@@ -1,50 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../theme/theme_provider.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
String _getThemeName(ThemeType type) {
|
||||
switch (type) {
|
||||
case ThemeType.defaultTheme:
|
||||
return "P'titsPas";
|
||||
case ThemeType.pastelTheme:
|
||||
return "Pastel";
|
||||
case ThemeType.darkTheme:
|
||||
return "Sombre";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Accueil'),
|
||||
actions: [
|
||||
Consumer<ThemeProvider>(
|
||||
builder: (context, themeProvider, child) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: DropdownButton<ThemeType>(
|
||||
value: themeProvider.currentTheme,
|
||||
items: ThemeType.values.map((ThemeType type) {
|
||||
return DropdownMenuItem<ThemeType>(
|
||||
value: type,
|
||||
child: Text(_getThemeName(type)),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (ThemeType? newValue) {
|
||||
if (newValue != null) {
|
||||
themeProvider.setTheme(newValue);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: const Center(
|
||||
child: Text('Bienvenue sur P\'titsPas !'),
|
||||
|
||||
Reference in New Issue
Block a user