From acb602643a2e6bd0013c91aae65881f342552e8d Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Wed, 7 May 2025 17:43:07 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Avanc=C3=A9e=20majeure=20parcours=20ins?= =?UTF-8?q?cription=20parent=20et=20refactorisation=20widgets=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ce commit comprend plusieurs améliorations significatives : Inscription Parent - Étape 5 (Récapitulatif) : - Initialisation de l'écran pour l'étape 5/5 du parcours d'inscription parent. - Mise en place de la structure de base de l'écran de récapitulatif (titre, fond, bouton de soumission initial, modale de confirmation). - Intégration de la navigation vers l'étape 5 depuis l'étape 4, incluant le passage (actuellement factice) des données d'inscription. - Correction des erreurs de navigation et de typage liées à l'introduction de `PlaceholderRegistrationData` pour cette nouvelle étape. Refactorisation des Widgets UI : - `CustomAppTextField` : - Évolution majeure pour supporter différents styles de fond (beige, lavande, jaune) via un nouvel enum `CustomAppTextFieldStyle`. - Les images de fond pour les styles lavande et jaune (`input_field_lavande.png`, `input_field_jaune.png`) ont été renommées et sont maintenant utilisées. - Mise à jour de l'écran de login pour utiliser ce `CustomAppTextField` stylisé, remplaçant l'ancien widget privé `_ImageTextField`. - Réintégration des paramètres `isRequired`, `enabled`, `readOnly`, `onTap`, et `suffixIcon` qui avaient été omis lors d'une refactorisation précédente, assurant la compatibilité avec l'étape 3. - `ImageButton` : - Extraction du widget privé `_ImageButton` de l'écran de login en un widget public `ImageButton` (dans `widgets/image_button.dart`) pour une réutilisation globale. - Mise à jour de l'écran de login pour utiliser ce nouveau widget public. - Utilisation du nouveau `ImageButton` pour le bouton "Soumettre ma demande" sur l'écran de l'étape 5. Corrections : - Correction d'une erreur de `RenderFlex overflowed` dans la carte enfant (`_ChildCardWidget`) de l'étape 3 de l'inscription parent, en ajustant les espacements internes. - Résolution de diverses erreurs de compilation qui sont apparues pendant ces refactorisations. --- ...eld_password.png => input_field_jaune.png} | Bin ...ield_email.png => input_field_lavande.png} | Bin .../models/placeholder_registration_data.dart | 18 ++ frontend/lib/navigation/app_router.dart | 12 ++ frontend/lib/screens/auth/login_screen.dart | 176 ++---------------- .../auth/parent_register_step3_screen.dart | 26 +-- .../auth/parent_register_step4_screen.dart | 13 +- .../auth/parent_register_step5_screen.dart | 117 ++++++++++++ .../lib/widgets/custom_app_text_field.dart | 147 ++++++++++----- frontend/lib/widgets/image_button.dart | 53 ++++++ 10 files changed, 336 insertions(+), 226 deletions(-) rename frontend/assets/images/{field_password.png => input_field_jaune.png} (100%) rename frontend/assets/images/{field_email.png => input_field_lavande.png} (100%) create mode 100644 frontend/lib/models/placeholder_registration_data.dart create mode 100644 frontend/lib/screens/auth/parent_register_step5_screen.dart create mode 100644 frontend/lib/widgets/image_button.dart diff --git a/frontend/assets/images/field_password.png b/frontend/assets/images/input_field_jaune.png similarity index 100% rename from frontend/assets/images/field_password.png rename to frontend/assets/images/input_field_jaune.png diff --git a/frontend/assets/images/field_email.png b/frontend/assets/images/input_field_lavande.png similarity index 100% rename from frontend/assets/images/field_email.png rename to frontend/assets/images/input_field_lavande.png diff --git a/frontend/lib/models/placeholder_registration_data.dart b/frontend/lib/models/placeholder_registration_data.dart new file mode 100644 index 0000000..18b67bb --- /dev/null +++ b/frontend/lib/models/placeholder_registration_data.dart @@ -0,0 +1,18 @@ +// frontend/lib/models/placeholder_registration_data.dart +class PlaceholderRegistrationData { + final String? parent1Name; + // Ajoutez ici d'autres champs au fur et à mesure que nous définissons les données nécessaires + // pour parent 1, parent 2, enfants, motivation + + // Exemple de champ pour savoir si le parent 2 existe + final bool parent2Exists; + final List childrenNames; // Juste un exemple, à remplacer par une vraie structure enfant + final String? motivationText; + + PlaceholderRegistrationData({ + this.parent1Name, + this.parent2Exists = false, // Valeur par défaut + this.childrenNames = const [], // Valeur par défaut + this.motivationText, + }); +} \ No newline at end of file diff --git a/frontend/lib/navigation/app_router.dart b/frontend/lib/navigation/app_router.dart index 0e27700..d9ef8b9 100644 --- a/frontend/lib/navigation/app_router.dart +++ b/frontend/lib/navigation/app_router.dart @@ -6,7 +6,9 @@ import '../screens/auth/parent_register_step1_screen.dart'; import '../screens/auth/parent_register_step2_screen.dart'; import '../screens/auth/parent_register_step3_screen.dart'; import '../screens/auth/parent_register_step4_screen.dart'; +import '../screens/auth/parent_register_step5_screen.dart'; import '../screens/home/home_screen.dart'; +import '../models/placeholder_registration_data.dart'; class AppRouter { static const String login = '/login'; @@ -15,6 +17,7 @@ class AppRouter { static const String parentRegisterStep2 = '/parent-register/step2'; static const String parentRegisterStep3 = '/parent-register/step3'; static const String parentRegisterStep4 = '/parent-register/step4'; + static const String parentRegisterStep5 = '/parent-register/step5'; static const String home = '/home'; static Route generateRoute(RouteSettings settings) { @@ -45,6 +48,15 @@ class AppRouter { screen = const ParentRegisterStep4Screen(); slideTransition = true; break; + case parentRegisterStep5: + final args = settings.arguments as PlaceholderRegistrationData?; + if (args != null) { + screen = ParentRegisterStep5Screen(registrationData: args); + } else { + print("Erreur: Données d'inscription manquantes pour l'étape 5"); + screen = const RegisterChoiceScreen(); + } + break; case home: screen = const HomeScreen(); break; diff --git a/frontend/lib/screens/auth/login_screen.dart b/frontend/lib/screens/auth/login_screen.dart index d9b82b7..f6c8a8f 100644 --- a/frontend/lib/screens/auth/login_screen.dart +++ b/frontend/lib/screens/auth/login_screen.dart @@ -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 { 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, @@ -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 // ─────────────────────────────────────────────────────────────── diff --git a/frontend/lib/screens/auth/parent_register_step3_screen.dart b/frontend/lib/screens/auth/parent_register_step3_screen.dart index 0fcd231..f696b2f 100644 --- a/frontend/lib/screens/auth/parent_register_step3_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step3_screen.dart @@ -403,7 +403,7 @@ class _ChildCardWidget extends StatelessWidget { ), ), ), - const SizedBox(height: 10), + const SizedBox(height: 5), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -411,30 +411,30 @@ class _ChildCardWidget extends StatelessWidget { Switch(value: childData.isUnbornChild, onChanged: onToggleIsUnborn, activeColor: Theme.of(context).primaryColor), ], ), - const SizedBox(height: 15), - CustomAppTextField( // Utilisation du nouveau widget + const SizedBox(height: 8), + CustomAppTextField( controller: childData.firstNameController, - label: 'Prénom', - hintText: childData.isUnbornChild ? 'Prénom (optionnel)' : 'Prénom de l\'enfant', + labelText: 'Prénom', + hintText: 'Facultatif si à naître', isRequired: !childData.isUnbornChild, ), - const SizedBox(height: 10), - CustomAppTextField( // Utilisation du nouveau widget + const SizedBox(height: 5), + CustomAppTextField( controller: childData.lastNameController, - label: 'Nom', + labelText: 'Nom', hintText: 'Nom de l\'enfant', enabled: true, ), - const SizedBox(height: 10), - CustomAppTextField( // Utilisation du nouveau widget + const SizedBox(height: 8), + CustomAppTextField( controller: childData.dobController, - label: childData.isUnbornChild ? 'Date prévisionnelle de naissance' : 'Date de naissance', + labelText: childData.isUnbornChild ? 'Date prévisionnelle de naissance' : 'Date de naissance', hintText: 'JJ/MM/AAAA', readOnly: true, onTap: onDateSelect, suffixIcon: Icons.calendar_today, ), - const SizedBox(height: 18), + const SizedBox(height: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -443,7 +443,7 @@ class _ChildCardWidget extends StatelessWidget { value: childData.photoConsent, onChanged: onTogglePhotoConsent, ), - const SizedBox(height: 10), + const SizedBox(height: 5), AppCustomCheckbox( // Utilisation du nouveau widget label: 'Naissance multiple', value: childData.multipleBirth, diff --git a/frontend/lib/screens/auth/parent_register_step4_screen.dart b/frontend/lib/screens/auth/parent_register_step4_screen.dart index c9e776e..37fa46d 100644 --- a/frontend/lib/screens/auth/parent_register_step4_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step4_screen.dart @@ -3,6 +3,7 @@ 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'; class ParentRegisterStep4Screen extends StatefulWidget { const ParentRegisterStep4Screen({super.key}); @@ -183,10 +184,14 @@ Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lo ? () { print('Motivation: ${_motivationController.text}'); print('CGU acceptées: $_cguAccepted'); - // TODO: Naviguer vers l'étape 5 - // Navigator.pushNamed(context, '/parent-register/step5'); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Navigation vers Étape 5 (TODO)')), + + // TODO: Rassembler toutes les données des étapes précédentes + final dummyData = PlaceholderRegistrationData(parent1Name: "Parent 1 Nom (Exemple)"); + + Navigator.pushNamed( + context, + '/parent-register/step5', + arguments: dummyData // Passer les données (ici factices) ); } : null, // Désactiver si les CGU ne sont pas acceptées diff --git a/frontend/lib/screens/auth/parent_register_step5_screen.dart b/frontend/lib/screens/auth/parent_register_step5_screen.dart new file mode 100644 index 0000000..e030f9a --- /dev/null +++ b/frontend/lib/screens/auth/parent_register_step5_screen.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import '../../models/placeholder_registration_data.dart'; // Assurez-vous que le chemin est correct +import '../../widgets/image_button.dart'; // Import du ImageButton + +// La définition locale de PlaceholderRegistrationData est supprimée ici. + +class ParentRegisterStep5Screen extends StatelessWidget { + final PlaceholderRegistrationData registrationData; // Doit maintenant utiliser la version importée + + const ParentRegisterStep5Screen({super.key, required this.registrationData}); + + @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.repeatY), + ), + Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 50.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), + + // TODO: Construire les cartes récapitulatives ici + // _buildParent1Card(context, registrationData), + // if (registrationData.parent2Exists) _buildParent2Card(context, registrationData), + // ..._buildChildrenCards(context, registrationData), + // _buildMotivationCard(context, registrationData), + + const SizedBox(height: 40), + // Utilisation du ImageButton + ImageButton( + bg: 'assets/images/btn_green.png', + text: 'Soumettre ma demande', + textColor: const Color(0xFF2D6A4F), + width: 350, + height: 50, + fontSize: 18, + onPressed: () { + _showConfirmationModal(context); + }, + ), + ], + ), + ), + ), + // Chevrons de navigation (uniquement retour vers étape 4) + 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( + 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: [ + 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 route) => false); + }, + ), + ], + ); + }, + ); + } + + // TODO: Méthodes pour construire les cartes individuelles + // Widget _buildParent1Card(BuildContext context, PlaceholderRegistrationData data) { ... } + // Widget _buildParent2Card(BuildContext context, PlaceholderRegistrationData data) { ... } + // List _buildChildrenCards(BuildContext context, PlaceholderRegistrationData data) { ... } + // Widget _buildMotivationCard(BuildContext context, PlaceholderRegistrationData data) { ... } +} \ No newline at end of file diff --git a/frontend/lib/widgets/custom_app_text_field.dart b/frontend/lib/widgets/custom_app_text_field.dart index 252a3fc..8824451 100644 --- a/frontend/lib/widgets/custom_app_text_field.dart +++ b/frontend/lib/widgets/custom_app_text_field.dart @@ -1,81 +1,126 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -class CustomAppTextField extends StatelessWidget { +// Définition de l'enum pour les styles de couleur/fond +enum CustomAppTextFieldStyle { + beige, + lavande, + jaune, +} + +class CustomAppTextField extends StatefulWidget { final TextEditingController controller; - final String label; - final String? hintText; - final TextInputType? keyboardType; + final String labelText; + final String hintText; + final double fieldHeight; + final double fieldWidth; final bool obscureText; + final TextInputType keyboardType; + final String? Function(String?)? validator; + final CustomAppTextFieldStyle style; + final bool isRequired; final bool enabled; final bool readOnly; final VoidCallback? onTap; final IconData? suffixIcon; - final bool isRequired; - final String? Function(String?)? validator; // Permettre un validateur personnalisé const CustomAppTextField({ super.key, required this.controller, - required this.label, - this.hintText, - this.keyboardType, + required this.labelText, + this.hintText = '', + this.fieldHeight = 50.0, + this.fieldWidth = 300.0, this.obscureText = false, + this.keyboardType = TextInputType.text, + this.validator, + this.style = CustomAppTextFieldStyle.beige, + this.isRequired = false, this.enabled = true, this.readOnly = false, this.onTap, this.suffixIcon, - this.isRequired = true, - this.validator, }); + @override + State createState() => _CustomAppTextFieldState(); +} + +class _CustomAppTextFieldState extends State { + String getBackgroundImagePath() { + switch (widget.style) { + case CustomAppTextFieldStyle.lavande: + return 'assets/images/input_field_lavande.png'; + case CustomAppTextFieldStyle.jaune: + return 'assets/images/input_field_jaune.png'; + case CustomAppTextFieldStyle.beige: + default: + return 'assets/images/input_field_bg.png'; + } + } + @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ Text( - label, - style: GoogleFonts.merienda(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black87), - ), - const SizedBox(height: 4), - Container( - height: 45, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/input_field_bg.png'), - fit: BoxFit.fill, - ), + widget.labelText, + style: GoogleFonts.merienda( + fontSize: 14, + color: Colors.black87, + fontWeight: FontWeight.w500, ), - child: TextFormField( - controller: controller, - keyboardType: keyboardType, - obscureText: obscureText, - enabled: enabled, - readOnly: readOnly, - onTap: onTap, - style: GoogleFonts.merienda(fontSize: 15, color: enabled ? Colors.black87 : Colors.grey), - textAlignVertical: TextAlignVertical.center, - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: const EdgeInsets.fromLTRB(15, 13, 15, 11), - hintText: hintText, - hintStyle: GoogleFonts.merienda(fontSize: 15, color: Colors.black38), - suffixIcon: suffixIcon != null ? Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Icon(suffixIcon, color: Colors.black54, size: 20), - ) : null, - isDense: true, - ), - validator: validator ?? // Utilise le validateur fourni, ou celui par défaut - (value) { - if (!enabled) return null; - if (readOnly) return null; - if (isRequired && (value == null || value.isEmpty)) { - return 'Ce champ est obligatoire'; - } - return null; - }, + ), + const SizedBox(height: 6), + SizedBox( + width: widget.fieldWidth, + height: widget.fieldHeight, + child: Stack( + alignment: Alignment.centerLeft, + children: [ + Positioned.fill( + child: Image.asset( + getBackgroundImagePath(), + fit: BoxFit.fill, + ), + ), + Padding( + padding: const EdgeInsets.only(left: 18.0, right: 18.0, bottom: 2.0), + child: TextFormField( + controller: widget.controller, + obscureText: widget.obscureText, + keyboardType: widget.keyboardType, + enabled: widget.enabled, + readOnly: widget.readOnly, + onTap: widget.onTap, + style: GoogleFonts.merienda(fontSize: 15, color: widget.enabled ? Colors.black87 : Colors.grey), + validator: widget.validator ?? + (value) { + if (!widget.enabled || widget.readOnly) return null; + if (widget.isRequired && (value == null || value.isEmpty)) { + return 'Ce champ est obligatoire'; + } + return null; + }, + decoration: InputDecoration( + hintText: widget.hintText, + hintStyle: GoogleFonts.merienda(fontSize: 15, color: Colors.black54.withOpacity(0.7)), + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + suffixIcon: widget.suffixIcon != null + ? Padding( + padding: const EdgeInsets.only(right: 0.0), + child: Icon(widget.suffixIcon, color: Colors.black54, size: 20), + ) + : null, + isDense: true, + ), + textAlignVertical: TextAlignVertical.center, + ), + ), + ], ), ), ], diff --git a/frontend/lib/widgets/image_button.dart b/frontend/lib/widgets/image_button.dart new file mode 100644 index 0000000..2d81362 --- /dev/null +++ b/frontend/lib/widgets/image_button.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class ImageButton extends StatelessWidget { + final String bg; + final double width; + final double height; + final String text; + final Color textColor; + final VoidCallback onPressed; + final double fontSize; // Ajout pour la flexibilité + + const ImageButton({ + super.key, + required this.bg, + required this.width, + required this.height, + required this.text, + required this.textColor, + required this.onPressed, + this.fontSize = 16, // Valeur par défaut + }); + + @override + Widget build(BuildContext context) { + return MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: onPressed, + child: Container( + width: width, + height: height, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(bg), + fit: BoxFit.fill, + ), + ), + child: Center( + child: Text( + text, + style: GoogleFonts.merienda( + color: textColor, + fontSize: fontSize, // Utilisation du paramètre + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ); + } +} \ No newline at end of file