- Support des modes Desktop/Mobile et Édition/Lecture seule - Refactoring des widgets de formulaire (PersonalInfo, ProfessionalInfo, Presentation, ChildCard) - Mise à jour des écrans de récapitulatif (ParentStep5, AmStep4) - Ajout de navigation (Précédent/Soumettre) sur mobile Closes #78 Co-authored-by: Cursor <cursoragent@cursor.com>
226 lines
6.6 KiB
Dart
226 lines
6.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import '../config/display_config.dart';
|
|
import '../models/card_assets.dart';
|
|
import 'hover_relief_widget.dart';
|
|
import 'image_button.dart';
|
|
|
|
/// Widget de base générique pour tous les écrans de formulaire
|
|
/// Gère automatiquement le layout, les boutons de navigation, etc.
|
|
class BaseFormScreen extends StatelessWidget {
|
|
/// Configuration d'affichage
|
|
final DisplayConfig config;
|
|
|
|
/// Texte de l'étape (ex: "Étape 1/4")
|
|
final String stepText;
|
|
|
|
/// Titre du formulaire
|
|
final String title;
|
|
|
|
/// Couleur de la carte (horizontal pour desktop)
|
|
final CardColorHorizontal cardColor;
|
|
|
|
/// Contenu du formulaire
|
|
final Widget content;
|
|
|
|
/// Texte du bouton de soumission (par défaut "Suivant")
|
|
final String? submitButtonText;
|
|
|
|
/// Callback de soumission
|
|
final VoidCallback onSubmit;
|
|
|
|
/// Route précédente (pour le bouton retour)
|
|
final String previousRoute;
|
|
|
|
/// Widget supplémentaire au-dessus du contenu (ex: toggle)
|
|
final Widget? headerWidget;
|
|
|
|
/// Widget supplémentaire en dessous du contenu (ex: checkbox CGU)
|
|
final Widget? footerWidget;
|
|
|
|
/// Padding personnalisé pour le contenu
|
|
final EdgeInsets? contentPadding;
|
|
|
|
const BaseFormScreen({
|
|
super.key,
|
|
required this.config,
|
|
required this.stepText,
|
|
required this.title,
|
|
required this.cardColor,
|
|
required this.content,
|
|
required this.onSubmit,
|
|
required this.previousRoute,
|
|
this.submitButtonText,
|
|
this.headerWidget,
|
|
this.footerWidget,
|
|
this.contentPadding,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFFFF8E1),
|
|
body: SafeArea(
|
|
child: SingleChildScrollView(
|
|
padding: EdgeInsets.all(
|
|
LayoutHelper.getSpacing(context,
|
|
mobileSpacing: 16.0,
|
|
desktopSpacing: 32.0,
|
|
),
|
|
),
|
|
child: Center(
|
|
child: ConstrainedBox(
|
|
constraints: BoxConstraints(
|
|
maxWidth: LayoutHelper.getMaxWidth(context),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Texte de l'étape
|
|
Text(
|
|
stepText,
|
|
style: GoogleFonts.merienda(
|
|
fontSize: config.isMobile ? 14 : 16,
|
|
fontWeight: FontWeight.w500,
|
|
color: const Color(0xFF6D4C41),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
|
|
// Titre
|
|
Text(
|
|
title,
|
|
style: GoogleFonts.merienda(
|
|
fontSize: config.isMobile ? 24 : 32,
|
|
fontWeight: FontWeight.bold,
|
|
color: const Color(0xFF4A4A4A),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Header widget (si fourni)
|
|
if (headerWidget != null) ...[
|
|
headerWidget!,
|
|
const SizedBox(height: 16),
|
|
],
|
|
|
|
// Carte principale
|
|
_buildCard(context),
|
|
|
|
const SizedBox(height: 24),
|
|
|
|
// Footer widget (si fourni)
|
|
if (footerWidget != null) ...[
|
|
footerWidget!,
|
|
const SizedBox(height: 24),
|
|
],
|
|
|
|
// Boutons de navigation
|
|
_buildNavigationButtons(context),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Construit la carte principale
|
|
Widget _buildCard(BuildContext context) {
|
|
final effectivePadding = contentPadding ??
|
|
EdgeInsets.all(
|
|
LayoutHelper.getSpacing(context,
|
|
mobileSpacing: 16.0,
|
|
desktopSpacing: 32.0,
|
|
),
|
|
);
|
|
|
|
if (config.isMobile) {
|
|
// Carte verticale sur mobile
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage(_getVerticalCardAsset()),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: Padding(
|
|
padding: effectivePadding,
|
|
child: content,
|
|
),
|
|
);
|
|
} else {
|
|
// Carte horizontale sur desktop
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage(cardColor.path),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: Padding(
|
|
padding: effectivePadding,
|
|
child: content,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Retourne l'asset de carte vertical correspondant à la couleur
|
|
String _getVerticalCardAsset() {
|
|
// Mapping couleur horizontale -> verticale
|
|
switch (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;
|
|
}
|
|
}
|
|
|
|
/// Construit les boutons de navigation
|
|
Widget _buildNavigationButtons(BuildContext context) {
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
// Bouton Précédent
|
|
HoverReliefWidget(
|
|
child: ImageButton(
|
|
bg: 'assets/images/bg_green.png',
|
|
text: 'Précédent',
|
|
textColor: Colors.white,
|
|
onPressed: () => Navigator.pushNamed(context, previousRoute),
|
|
width: config.isMobile ? 120 : 150,
|
|
height: config.isMobile ? 40 : 50,
|
|
),
|
|
),
|
|
|
|
// Bouton Suivant/Soumettre
|
|
HoverReliefWidget(
|
|
child: ImageButton(
|
|
bg: 'assets/images/bg_green.png',
|
|
text: submitButtonText ?? 'Suivant',
|
|
textColor: Colors.white,
|
|
onPressed: config.isReadonly ? onSubmit : () {
|
|
// En mode éditable, valider avant de soumettre
|
|
onSubmit();
|
|
},
|
|
width: config.isMobile ? 120 : 150,
|
|
height: config.isMobile ? 40 : 50,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|