feat(frontend): Refonte infrastructure formulaires multi-modes

- 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>
This commit is contained in:
2026-02-07 14:51:33 +01:00
co-authored by Cursor
parent 45bd8a9ef1
commit b18d5c8a9e
23 changed files with 3799 additions and 1213 deletions
-45
View File
@@ -4,7 +4,6 @@ import 'package:provider/provider.dart';
// Models
import '../models/user_registration_data.dart';
import '../models/nanny_registration_data.dart';
import '../models/am_registration_data.dart';
// Screens
@@ -15,11 +14,6 @@ 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/auth/nanny_register_step1_screen.dart';
import '../screens/auth/nanny_register_step2_screen.dart';
import '../screens/auth/nanny_register_step3_screen.dart';
import '../screens/auth/nanny_register_step4_screen.dart';
import '../screens/auth/nanny_register_confirmation_screen.dart';
import '../screens/auth/am_register_step1_screen.dart';
import '../screens/auth/am_register_step2_screen.dart';
import '../screens/auth/am_register_step3_screen.dart';
@@ -33,7 +27,6 @@ import '../screens/unknown_screen.dart';
// For ShellRoute, creating them here and passing via .value is common.
final userRegistrationDataNotifier = UserRegistrationData();
final nannyRegistrationDataNotifier = NannyRegistrationData();
final amRegistrationDataNotifier = AmRegistrationData();
class AppRouter {
@@ -84,44 +77,6 @@ class AppRouter {
path: '/parent-register-step5',
builder: (BuildContext context, GoRouterState state) => const ParentRegisterStep5Screen(),
),
GoRoute(
path: '/parent-register-confirmation',
builder: (BuildContext context, GoRouterState state) => const NannyRegisterConfirmationScreen(),
),
],
),
// --- Nanny Registration Flow ---
ShellRoute(
builder: (context, state, child) {
return ChangeNotifierProvider<NannyRegistrationData>.value(
value: nannyRegistrationDataNotifier,
child: child,
);
},
routes: <RouteBase>[
GoRoute(
path: '/nanny-register-step1',
builder: (BuildContext context, GoRouterState state) => const NannyRegisterStep1Screen(),
),
GoRoute(
path: '/nanny-register-step2',
builder: (BuildContext context, GoRouterState state) => const NannyRegisterStep2Screen(),
),
GoRoute(
path: '/nanny-register-step3',
builder: (BuildContext context, GoRouterState state) => const NannyRegisterStep3Screen(),
),
GoRoute(
path: '/nanny-register-step4',
builder: (BuildContext context, GoRouterState state) => const NannyRegisterStep4Screen(),
),
GoRoute(
path: '/nanny-register-confirmation',
builder: (BuildContext context, GoRouterState state) {
return const NannyRegisterConfirmationScreen();
},
),
],
),
+106
View File
@@ -0,0 +1,106 @@
import 'package:flutter/material.dart';
/// Mode d'affichage d'un formulaire
enum DisplayMode {
/// Mode éditable (formulaire d'inscription)
editable,
/// Mode lecture seule (récapitulatif)
readonly,
}
/// Configuration d'affichage pour les widgets de formulaire
class DisplayConfig {
/// Mode d'affichage (editable/readonly)
final DisplayMode mode;
/// Type de layout détecté (mobile/desktop)
final LayoutType layoutType;
const DisplayConfig({
required this.mode,
required this.layoutType,
});
/// Crée une config à partir du contexte
factory DisplayConfig.fromContext(
BuildContext context, {
DisplayMode mode = DisplayMode.editable,
}) {
return DisplayConfig(
mode: mode,
layoutType: LayoutHelper.getLayoutType(context),
);
}
/// Est en mode éditable
bool get isEditable => mode == DisplayMode.editable;
/// Est en mode lecture seule
bool get isReadonly => mode == DisplayMode.readonly;
/// Est en layout mobile
bool get isMobile => layoutType == LayoutType.mobile;
/// Est en layout desktop
bool get isDesktop => layoutType == LayoutType.desktop;
/// Layout vertical (mobile)
bool get isVerticalLayout => isMobile;
/// Layout horizontal (desktop)
bool get isHorizontalLayout => isDesktop;
}
/// Type de layout
enum LayoutType {
/// Mobile (< 600px) - toujours vertical
mobile,
/// Desktop/Tablette (≥ 600px) - horizontal
desktop,
}
/// Utilitaires pour la détection de layout
class LayoutHelper {
/// Seuil de largeur pour mobile/desktop (en pixels)
static const double mobileBreakpoint = 600.0;
/// Détermine le type de layout selon la largeur d'écran
static LayoutType getLayoutType(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return width < mobileBreakpoint
? LayoutType.mobile
: LayoutType.desktop;
}
/// Vérifie si on est sur mobile
static bool isMobile(BuildContext context) {
return getLayoutType(context) == LayoutType.mobile;
}
/// Vérifie si on est sur desktop
static bool isDesktop(BuildContext context) {
return getLayoutType(context) == LayoutType.desktop;
}
/// Retourne un espacement adapté au layout
static double getSpacing(BuildContext context, {
double mobileSpacing = 12.0,
double desktopSpacing = 20.0,
}) {
return isMobile(context) ? mobileSpacing : desktopSpacing;
}
/// Retourne une largeur max adaptée au layout
static double getMaxWidth(BuildContext context, {
double? mobileMaxWidth,
double? desktopMaxWidth,
}) {
if (isMobile(context)) {
return mobileMaxWidth ?? double.infinity;
} else {
return desktopMaxWidth ?? 1200.0;
}
}
}