petitspas/frontend/lib/widgets/base_form_screen.dart
Julien Martin b79f8c7e64 refactor(#78): Renommer assets images pour usage générique
Renommage des assets pour permettre leur utilisation
aussi bien pour les boutons que pour les champs :

**Images renommées :**
- input_field_bg.png → bg_beige.png
- input_field_jaune.png → bg_yellow.png
- input_field_lavande.png → bg_lavender.png
- btn_green.png → bg_green.png

**Fichiers mis à jour (8) :**
- custom_app_text_field.dart (champs de formulaire)
- custom_navigation_button.dart (nouveau widget boutons)
- base_form_screen.dart (structure de page)
- login_screen.dart
- change_password_dialog.dart
- am_register_step4_screen.dart
- parent_register_step5_screen.dart
- summary_screen.dart

**Avantages :**
 Noms génériques et cohérents
 Réutilisabilité boutons ET champs
 Maintenance facilitée

Référence: #78

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-04 11:01:54 +01:00

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,
),
),
],
);
}
}