Création de 2 nouveaux widgets génériques pour réduire la duplication : 1. presentation_form_screen.dart - Widget pour formulaires de présentation/motivation - Paramétrable : titre, couleur, hint, routes - Utilisé par Parent Step 4 et AM Step 3 - Réduction de ~350 lignes de code dupliqué 2. personal_info_form_screen.dart - Widget pour formulaires d'informations personnelles - Gère nom, prénom, téléphone, email, adresse - Options : toggle "2ème parent", checkbox "même adresse" - Utilisé par Parent Steps 1-2 et AM Step 1 - Réduction de ~460 lignes de code dupliqué Avantages : - Maintenance simplifiée (1 seul fichier à modifier) - Cohérence visuelle garantie entre tous les écrans - Extensibilité facile pour nouveaux types d'utilisateurs
170 lines
5.7 KiB
Dart
170 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'dart:math' as math;
|
|
|
|
import 'custom_decorated_text_field.dart';
|
|
import 'app_custom_checkbox.dart';
|
|
import '../models/card_assets.dart';
|
|
|
|
class PresentationFormScreen extends StatefulWidget {
|
|
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;
|
|
final String textFieldHint;
|
|
final String initialText;
|
|
final bool initialCguAccepted;
|
|
final String previousRoute;
|
|
final Function(String text, bool cguAccepted) onSubmit;
|
|
|
|
const PresentationFormScreen({
|
|
super.key,
|
|
required this.stepText,
|
|
required this.title,
|
|
required this.cardColor,
|
|
required this.textFieldHint,
|
|
required this.initialText,
|
|
required this.initialCguAccepted,
|
|
required this.previousRoute,
|
|
required this.onSubmit,
|
|
});
|
|
|
|
@override
|
|
State<PresentationFormScreen> createState() => _PresentationFormScreenState();
|
|
}
|
|
|
|
class _PresentationFormScreenState extends State<PresentationFormScreen> {
|
|
late TextEditingController _textController;
|
|
late bool _cguAccepted;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_textController = TextEditingController(text: widget.initialText);
|
|
_cguAccepted = widget.initialCguAccepted;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_textController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _handleSubmit() {
|
|
if (!_cguAccepted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Vous devez accepter les CGU pour continuer.'),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
widget.onSubmit(_textController.text, _cguAccepted);
|
|
}
|
|
|
|
@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;
|
|
|
|
return Scaffold(
|
|
body: Stack(
|
|
children: [
|
|
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),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
// 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',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|