diff --git a/frontend/lib/widgets/presentation_form_screen.dart b/frontend/lib/widgets/presentation_form_screen.dart index 8008f32..17a049f 100644 --- a/frontend/lib/widgets/presentation_form_screen.dart +++ b/frontend/lib/widgets/presentation_form_screen.dart @@ -5,9 +5,15 @@ import 'dart:math' as math; import 'custom_decorated_text_field.dart'; import 'app_custom_checkbox.dart'; +import 'custom_navigation_button.dart'; +import 'hover_relief_widget.dart'; import '../models/card_assets.dart'; +import '../config/display_config.dart'; +/// Widget générique pour le formulaire de présentation avec texte libre + CGU +/// Supporte mode éditable et readonly, responsive mobile/desktop class PresentationFormScreen extends StatefulWidget { + final DisplayMode mode; final String stepText; // Ex: "Étape 3/4" ou "Étape 4/5" final String title; // Ex: "Présentation et Conditions" ou "Motivation de votre demande" final CardColorHorizontal cardColor; @@ -19,6 +25,7 @@ class PresentationFormScreen extends StatefulWidget { const PresentationFormScreen({ super.key, + this.mode = DisplayMode.editable, required this.stepText, required this.title, required this.cardColor, @@ -66,9 +73,7 @@ class _PresentationFormScreenState extends State { @override Widget build(BuildContext context) { final screenSize = MediaQuery.of(context).size; - final cardWidth = screenSize.width * 0.6; - final double imageAspectRatio = 2.0; - final cardHeight = cardWidth / imageAspectRatio; + final config = DisplayConfig.fromContext(context, mode: widget.mode); return Scaffold( body: Stack( @@ -76,94 +81,268 @@ class _PresentationFormScreenState extends State { 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( - widget.stepText, - style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54), - ), - const SizedBox(height: 20), - Text( - widget.title, - 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(widget.cardColor.path), - fit: BoxFit.fill, - ), - ), - child: Padding( - padding: const EdgeInsets.all(40.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: CustomDecoratedTextField( - controller: _textController, - hintText: widget.textFieldHint, - fieldHeight: cardHeight * 0.6, - maxLines: 10, - expandDynamically: true, - fontSize: 18.0, - ), - ), - const SizedBox(height: 20), - AppCustomCheckbox( - label: 'J\'accepte les Conditions Générales\nd\'Utilisation et la Politique de confidentialité', - value: _cguAccepted, - onChanged: (value) => setState(() => _cguAccepted = value ?? false), - ), - ], - ), - ), - ), - ], + config.isMobile + ? _buildMobileLayout(context, config, screenSize) + : _buildDesktopLayout(context, config, screenSize), + // Chevrons desktop uniquement + if (!config.isMobile) ...[ + // Chevron Gauche (Retour) + 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: () { + if (context.canPop()) { + context.pop(); + } else { + context.go(widget.previousRoute); + } + }, + tooltip: 'Retour', + ), + ), + // Chevron Droit (Suivant) + Positioned( + top: screenSize.height / 2 - 20, + right: 40, + child: IconButton( + icon: Image.asset('assets/images/chevron_right.png', height: 40), + onPressed: _cguAccepted ? _handleSubmit : null, + tooltip: 'Suivant', + ), + ), + ], + ], + ), + ); + } + + /// Layout MOBILE : Plein écran sans scroll global + Widget _buildMobileLayout(BuildContext context, DisplayConfig config, Size screenSize) { + return Column( + children: [ + // Header fixe + Padding( + padding: const EdgeInsets.only(top: 20.0), + child: Column( + children: [ + Text( + widget.stepText, + style: GoogleFonts.merienda( + fontSize: 13, + color: Colors.black54, + ), + ), + const SizedBox(height: 6), + Text( + widget.title, + style: GoogleFonts.merienda( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + const SizedBox(height: 16), + // Carte qui prend tout l'espace restant + Expanded( + child: _buildMobileCard(context, config, screenSize), + ), + // Boutons en bas + const SizedBox(height: 20), + _buildMobileButtons(context, config, screenSize), + const SizedBox(height: 10), + ], + ); + } + + /// Layout DESKTOP : Avec scroll + Widget _buildDesktopLayout(BuildContext context, DisplayConfig config, Size screenSize) { + return Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 50.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + widget.stepText, + style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54), + ), + const SizedBox(height: 20), + Text( + widget.title, + style: GoogleFonts.merienda( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 30), + _buildDesktopCard(context, config, screenSize), + ], + ), + ), + ); + } + + /// Carte DESKTOP : Format horizontal 2:1 + Widget _buildDesktopCard(BuildContext context, DisplayConfig config, Size screenSize) { + final cardWidth = screenSize.width * 0.6; + final double imageAspectRatio = 2.0; + final cardHeight = cardWidth / imageAspectRatio; + + return Container( + width: cardWidth, + height: cardHeight, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(widget.cardColor.path), + fit: BoxFit.fill, + ), + ), + child: Padding( + padding: const EdgeInsets.all(40.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: CustomDecoratedTextField( + controller: _textController, + hintText: widget.textFieldHint, + fieldHeight: cardHeight * 0.6, + maxLines: 10, + expandDynamically: true, + fontSize: 18.0, + ), + ), + const SizedBox(height: 20), + AppCustomCheckbox( + label: 'J\'accepte les Conditions Générales\nd\'Utilisation et la Politique de confidentialité', + value: _cguAccepted, + onChanged: (value) => setState(() => _cguAccepted = value ?? false), + ), + ], + ), + ), + ); + } + + /// Carte MOBILE : Prend tout l'espace disponible + Widget _buildMobileCard(BuildContext context, DisplayConfig config, Size screenSize) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: screenSize.width * 0.05), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(_getVerticalCardAsset()), + fit: BoxFit.fill, + ), + ), + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 20), + child: Column( + children: [ + // Champ de texte qui prend l'espace disponible et scrollable + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return CustomDecoratedTextField( + controller: _textController, + hintText: widget.textFieldHint, + fieldHeight: constraints.maxHeight, + maxLines: 100, // Grande valeur pour permettre le scroll + expandDynamically: false, + fontSize: 14.0, + ); + }, + ), + ), + const SizedBox(height: 16), + // Checkbox en bas + Transform.scale( + scale: 0.85, + child: AppCustomCheckbox( + label: 'J\'accepte les CGU et la\nPolitique de confidentialité', + value: _cguAccepted, + onChanged: (value) => setState(() => _cguAccepted = value ?? false), + ), + ), + ], + ), + ), + ); + } + + /// Boutons mobile + Widget _buildMobileButtons(BuildContext context, DisplayConfig config, Size screenSize) { + return Padding( + padding: EdgeInsets.symmetric( + horizontal: screenSize.width * 0.05, + ), + child: Row( + children: [ + Expanded( + child: HoverReliefWidget( + child: CustomNavigationButton( + text: 'Précédent', + style: NavigationButtonStyle.purple, + onPressed: () { + if (context.canPop()) { + context.pop(); + } else { + context.go(widget.previousRoute); + } + }, + width: double.infinity, + height: 50, + fontSize: 16, ), ), ), - // Chevron Gauche (Retour) - 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), + const SizedBox(width: 16), + Expanded( + child: HoverReliefWidget( + child: CustomNavigationButton( + text: 'Suivant', + style: NavigationButtonStyle.green, + onPressed: _handleSubmit, + width: double.infinity, + height: 50, + fontSize: 16, ), - onPressed: () { - if (context.canPop()) { - context.pop(); - } else { - context.go(widget.previousRoute); - } - }, - tooltip: 'Retour', - ), - ), - // Chevron Droit (Suivant) - Positioned( - top: screenSize.height / 2 - 20, - right: 40, - child: IconButton( - icon: Image.asset('assets/images/chevron_right.png', height: 40), - onPressed: _cguAccepted ? _handleSubmit : null, - tooltip: 'Suivant', ), ), ], ), ); } + + /// Retourne l'asset de carte vertical correspondant à la couleur + String _getVerticalCardAsset() { + switch (widget.cardColor) { + case CardColorHorizontal.blue: + return CardColorVertical.blue.path; + case CardColorHorizontal.green: + return CardColorVertical.green.path; + case CardColorHorizontal.lavender: + return CardColorVertical.lavender.path; + case CardColorHorizontal.lime: + return CardColorVertical.lime.path; + case CardColorHorizontal.peach: + return CardColorVertical.peach.path; + case CardColorHorizontal.pink: + return CardColorVertical.pink.path; + case CardColorHorizontal.red: + return CardColorVertical.red.path; + } + } }