Compare commits
13 Commits
bd81561e41
...
acb8e72a7c
| Author | SHA1 | Date | |
|---|---|---|---|
| acb8e72a7c | |||
| 96794919a8 | |||
| 271dc713a3 | |||
| 13741b0430 | |||
| 8e3af711e5 | |||
| e700e50924 | |||
| 36ef0f8d5c | |||
| f09deb5efc | |||
| 26a0e31b32 | |||
| 21430dca41 | |||
| dcb81d3feb | |||
| 7c86feeb78 | |||
| df87abbb85 |
@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
|||||||
// Models
|
// Models
|
||||||
import '../models/user_registration_data.dart';
|
import '../models/user_registration_data.dart';
|
||||||
import '../models/nanny_registration_data.dart';
|
import '../models/nanny_registration_data.dart';
|
||||||
|
import '../models/am_registration_data.dart';
|
||||||
|
|
||||||
// Screens
|
// Screens
|
||||||
import '../screens/auth/login_screen.dart';
|
import '../screens/auth/login_screen.dart';
|
||||||
@ -19,6 +20,10 @@ import '../screens/auth/nanny_register_step2_screen.dart';
|
|||||||
import '../screens/auth/nanny_register_step3_screen.dart';
|
import '../screens/auth/nanny_register_step3_screen.dart';
|
||||||
import '../screens/auth/nanny_register_step4_screen.dart';
|
import '../screens/auth/nanny_register_step4_screen.dart';
|
||||||
import '../screens/auth/nanny_register_confirmation_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';
|
||||||
|
import '../screens/auth/am_register_step4_screen.dart';
|
||||||
import '../screens/home/home_screen.dart';
|
import '../screens/home/home_screen.dart';
|
||||||
import '../screens/unknown_screen.dart';
|
import '../screens/unknown_screen.dart';
|
||||||
|
|
||||||
@ -29,6 +34,7 @@ import '../screens/unknown_screen.dart';
|
|||||||
|
|
||||||
final userRegistrationDataNotifier = UserRegistrationData();
|
final userRegistrationDataNotifier = UserRegistrationData();
|
||||||
final nannyRegistrationDataNotifier = NannyRegistrationData();
|
final nannyRegistrationDataNotifier = NannyRegistrationData();
|
||||||
|
final amRegistrationDataNotifier = AmRegistrationData();
|
||||||
|
|
||||||
class AppRouter {
|
class AppRouter {
|
||||||
static final GoRouter router = GoRouter(
|
static final GoRouter router = GoRouter(
|
||||||
@ -118,6 +124,34 @@ class AppRouter {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// --- AM (Assistante Maternelle) Registration Flow ---
|
||||||
|
ShellRoute(
|
||||||
|
builder: (context, state, child) {
|
||||||
|
return ChangeNotifierProvider<AmRegistrationData>.value(
|
||||||
|
value: amRegistrationDataNotifier,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
path: '/am-register-step1',
|
||||||
|
builder: (BuildContext context, GoRouterState state) => const AmRegisterStep1Screen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/am-register-step2',
|
||||||
|
builder: (BuildContext context, GoRouterState state) => const AmRegisterStep2Screen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/am-register-step3',
|
||||||
|
builder: (BuildContext context, GoRouterState state) => const AmRegisterStep3Screen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/am-register-step4',
|
||||||
|
builder: (BuildContext context, GoRouterState state) => const AmRegisterStep4Screen(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
136
frontend/lib/models/am_registration_data.dart
Normal file
136
frontend/lib/models/am_registration_data.dart
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class AmRegistrationData extends ChangeNotifier {
|
||||||
|
// Step 1: Identity Info
|
||||||
|
String firstName = '';
|
||||||
|
String lastName = '';
|
||||||
|
String streetAddress = ''; // Nouveau pour N° et Rue
|
||||||
|
String postalCode = ''; // Nouveau
|
||||||
|
String city = ''; // Nouveau
|
||||||
|
String phone = '';
|
||||||
|
String email = '';
|
||||||
|
String password = '';
|
||||||
|
// String? photoPath; // Déplacé ou géré à l'étape 2
|
||||||
|
// bool photoConsent = false; // Déplacé ou géré à l'étape 2
|
||||||
|
|
||||||
|
// Step 2: Professional Info
|
||||||
|
String? photoPath; // Ajouté pour l'étape 2
|
||||||
|
bool photoConsent = false; // Ajouté pour l'étape 2
|
||||||
|
DateTime? dateOfBirth;
|
||||||
|
String birthCity = ''; // Nouveau
|
||||||
|
String birthCountry = ''; // Nouveau
|
||||||
|
// String placeOfBirth = ''; // Remplacé par birthCity et birthCountry
|
||||||
|
String nir = ''; // Numéro de Sécurité Sociale
|
||||||
|
String agrementNumber = ''; // Numéro d'agrément
|
||||||
|
int? capacity; // Number of children the AM can look after
|
||||||
|
|
||||||
|
// Step 3: Presentation & CGU
|
||||||
|
String presentationText = '';
|
||||||
|
bool cguAccepted = false;
|
||||||
|
|
||||||
|
// --- Methods to update data and notify listeners ---
|
||||||
|
|
||||||
|
void updateIdentityInfo({
|
||||||
|
String? firstName,
|
||||||
|
String? lastName,
|
||||||
|
String? streetAddress, // Modifié
|
||||||
|
String? postalCode, // Nouveau
|
||||||
|
String? city, // Nouveau
|
||||||
|
String? phone,
|
||||||
|
String? email,
|
||||||
|
String? password,
|
||||||
|
}) {
|
||||||
|
this.firstName = firstName ?? this.firstName;
|
||||||
|
this.lastName = lastName ?? this.lastName;
|
||||||
|
this.streetAddress = streetAddress ?? this.streetAddress; // Modifié
|
||||||
|
this.postalCode = postalCode ?? this.postalCode; // Nouveau
|
||||||
|
this.city = city ?? this.city; // Nouveau
|
||||||
|
this.phone = phone ?? this.phone;
|
||||||
|
this.email = email ?? this.email;
|
||||||
|
this.password = password ?? this.password;
|
||||||
|
// if (photoPath != null || this.photoPath != null) { // Supprimé de l'étape 1
|
||||||
|
// this.photoPath = photoPath;
|
||||||
|
// }
|
||||||
|
// this.photoConsent = photoConsent ?? this.photoConsent; // Supprimé de l'étape 1
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateProfessionalInfo({
|
||||||
|
String? photoPath,
|
||||||
|
bool? photoConsent,
|
||||||
|
DateTime? dateOfBirth,
|
||||||
|
String? birthCity, // Nouveau
|
||||||
|
String? birthCountry, // Nouveau
|
||||||
|
// String? placeOfBirth, // Remplacé
|
||||||
|
String? nir,
|
||||||
|
String? agrementNumber,
|
||||||
|
int? capacity,
|
||||||
|
}) {
|
||||||
|
// Allow setting photoPath to null explicitly
|
||||||
|
if (photoPath != null || this.photoPath != null) {
|
||||||
|
this.photoPath = photoPath;
|
||||||
|
}
|
||||||
|
this.photoConsent = photoConsent ?? this.photoConsent;
|
||||||
|
this.dateOfBirth = dateOfBirth ?? this.dateOfBirth;
|
||||||
|
this.birthCity = birthCity ?? this.birthCity; // Nouveau
|
||||||
|
this.birthCountry = birthCountry ?? this.birthCountry; // Nouveau
|
||||||
|
// this.placeOfBirth = placeOfBirth ?? this.placeOfBirth; // Remplacé
|
||||||
|
this.nir = nir ?? this.nir;
|
||||||
|
this.agrementNumber = agrementNumber ?? this.agrementNumber;
|
||||||
|
this.capacity = capacity ?? this.capacity;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePresentationAndCgu({
|
||||||
|
String? presentationText,
|
||||||
|
bool? cguAccepted,
|
||||||
|
}) {
|
||||||
|
this.presentationText = presentationText ?? this.presentationText;
|
||||||
|
this.cguAccepted = cguAccepted ?? this.cguAccepted;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Getters for validation or display ---
|
||||||
|
bool get isStep1Complete =>
|
||||||
|
firstName.isNotEmpty &&
|
||||||
|
lastName.isNotEmpty &&
|
||||||
|
streetAddress.isNotEmpty && // Modifié
|
||||||
|
postalCode.isNotEmpty && // Nouveau
|
||||||
|
city.isNotEmpty && // Nouveau
|
||||||
|
phone.isNotEmpty &&
|
||||||
|
email.isNotEmpty;
|
||||||
|
// password n'est pas requis à l'inscription (défini après validation par lien email)
|
||||||
|
|
||||||
|
bool get isStep2Complete =>
|
||||||
|
// photoConsent is mandatory if a photo is system-required, otherwise optional.
|
||||||
|
// For now, let's assume if photoPath is present, consent should ideally be true.
|
||||||
|
// Or, make consent always mandatory if photo section exists.
|
||||||
|
// Based on new mockup, photo is present, so consent might be implicitly or explicitly needed.
|
||||||
|
(photoPath != null ? photoConsent == true : true) && // Ajuster selon la logique de consentement désirée
|
||||||
|
dateOfBirth != null &&
|
||||||
|
birthCity.isNotEmpty &&
|
||||||
|
birthCountry.isNotEmpty &&
|
||||||
|
nir.isNotEmpty && // Basic check, could add validation
|
||||||
|
agrementNumber.isNotEmpty &&
|
||||||
|
capacity != null && capacity! > 0;
|
||||||
|
|
||||||
|
bool get isStep3Complete =>
|
||||||
|
// presentationText is optional as per CDC (message au gestionnaire)
|
||||||
|
cguAccepted;
|
||||||
|
|
||||||
|
bool get isRegistrationComplete =>
|
||||||
|
isStep1Complete && isStep2Complete && isStep3Complete;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AmRegistrationData('
|
||||||
|
'firstName: $firstName, lastName: $lastName, '
|
||||||
|
'streetAddress: $streetAddress, postalCode: $postalCode, city: $city, '
|
||||||
|
'phone: $phone, email: $email, '
|
||||||
|
// 'photoPath: $photoPath, photoConsent: $photoConsent, ' // Commenté car déplacé/modifié
|
||||||
|
'dateOfBirth: $dateOfBirth, birthCity: $birthCity, birthCountry: $birthCountry, '
|
||||||
|
'nir: $nir, agrementNumber: $agrementNumber, capacity: $capacity, '
|
||||||
|
'photoPath (step2): $photoPath, photoConsent (step2): $photoConsent, '
|
||||||
|
'presentationText: $presentationText, cguAccepted: $cguAccepted)';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,115 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../screens/auth/login_screen.dart';
|
|
||||||
import '../screens/auth/register_choice_screen.dart';
|
|
||||||
import '../screens/auth/parent_register_step1_screen.dart';
|
|
||||||
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/home/home_screen.dart';
|
|
||||||
import '../screens/administrateurs/admin_dashboardScreen.dart';
|
|
||||||
import '../screens/home/parent_screen/ParentDashboardScreen.dart';
|
|
||||||
import '../models/user_registration_data.dart';
|
|
||||||
|
|
||||||
class AppRouter {
|
|
||||||
static const String login = '/login';
|
|
||||||
static const String registerChoice = '/register-choice';
|
|
||||||
static const String parentRegisterStep1 = '/parent-register/step1';
|
|
||||||
static const String parentRegisterStep2 = '/parent-register/step2';
|
|
||||||
static const String parentRegisterStep3 = '/parent-register/step3';
|
|
||||||
static const String parentRegisterStep4 = '/parent-register/step4';
|
|
||||||
static const String parentRegisterStep5 = '/parent-register/step5';
|
|
||||||
static const String home = '/home';
|
|
||||||
static const String adminDashboard = '/admin-dashboard';
|
|
||||||
static const String parentDashboard = '/parent-dashboard';
|
|
||||||
static const String amDashboard = '/am-dashboard';
|
|
||||||
|
|
||||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
|
||||||
Widget screen;
|
|
||||||
bool slideTransition = false;
|
|
||||||
Object? args = settings.arguments;
|
|
||||||
|
|
||||||
Widget buildErrorScreen(String step) {
|
|
||||||
print("Erreur: Données UserRegistrationData manquantes ou de mauvais type pour l'étape $step");
|
|
||||||
return const ParentRegisterStep1Screen();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (settings.name) {
|
|
||||||
case login:
|
|
||||||
screen = const LoginPage();
|
|
||||||
break;
|
|
||||||
case registerChoice:
|
|
||||||
screen = const RegisterChoiceScreen();
|
|
||||||
slideTransition = true;
|
|
||||||
break;
|
|
||||||
case parentRegisterStep1:
|
|
||||||
screen = const ParentRegisterStep1Screen();
|
|
||||||
slideTransition = true;
|
|
||||||
break;
|
|
||||||
case parentRegisterStep2:
|
|
||||||
if (args is UserRegistrationData) {
|
|
||||||
screen = ParentRegisterStep2Screen(registrationData: args);
|
|
||||||
} else {
|
|
||||||
screen = buildErrorScreen('2');
|
|
||||||
}
|
|
||||||
slideTransition = true;
|
|
||||||
break;
|
|
||||||
case parentRegisterStep3:
|
|
||||||
if (args is UserRegistrationData) {
|
|
||||||
screen = ParentRegisterStep3Screen(registrationData: args);
|
|
||||||
} else {
|
|
||||||
screen = buildErrorScreen('3');
|
|
||||||
}
|
|
||||||
slideTransition = true;
|
|
||||||
break;
|
|
||||||
case parentRegisterStep4:
|
|
||||||
if (args is UserRegistrationData) {
|
|
||||||
screen = ParentRegisterStep4Screen(registrationData: args);
|
|
||||||
} else {
|
|
||||||
screen = buildErrorScreen('4');
|
|
||||||
}
|
|
||||||
slideTransition = true;
|
|
||||||
break;
|
|
||||||
case parentRegisterStep5:
|
|
||||||
screen = const ParentRegisterStep5Screen();
|
|
||||||
slideTransition = true;
|
|
||||||
break;
|
|
||||||
case home:
|
|
||||||
screen = const HomeScreen();
|
|
||||||
break;
|
|
||||||
case adminDashboard:
|
|
||||||
screen = const AdminDashboardScreen();
|
|
||||||
break;
|
|
||||||
case parentDashboard:
|
|
||||||
screen = const ParentDashboardScreen();
|
|
||||||
break;
|
|
||||||
case amDashboard:
|
|
||||||
// TODO: Créer l'écran dashboard pour les assistantes maternelles
|
|
||||||
screen = const HomeScreen();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
screen = Scaffold(
|
|
||||||
body: Center(
|
|
||||||
child: Text('Route non définie : ${settings.name}'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slideTransition) {
|
|
||||||
return PageRouteBuilder(
|
|
||||||
pageBuilder: (context, animation, secondaryAnimation) => screen,
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
const begin = Offset(1.0, 0.0);
|
|
||||||
const end = Offset.zero;
|
|
||||||
const curve = Curves.easeInOut;
|
|
||||||
var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
|
|
||||||
var offsetAnimation = animation.drive(tween);
|
|
||||||
return SlideTransition(position: offsetAnimation, child: child);
|
|
||||||
},
|
|
||||||
transitionDuration: const Duration(milliseconds: 400),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return MaterialPageRoute(builder: (_) => screen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,282 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:p_tits_pas/models/am_user_registration_data.dart';
|
|
||||||
import 'package:p_tits_pas/models/card_assets.dart';
|
|
||||||
import 'package:p_tits_pas/utils/data_generator.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/FormFieldConfig.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
|
|
||||||
class AmRegisterStep1Screen extends StatefulWidget {
|
|
||||||
const AmRegisterStep1Screen({super.key});
|
|
||||||
@override
|
|
||||||
State <AmRegisterStep1Screen> createState() => _AmRegisterStep1ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AmRegisterStep1ScreenState extends State<AmRegisterStep1Screen> {
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
late ChildminderRegistrationData _registrationData;
|
|
||||||
|
|
||||||
final _lastNameController = TextEditingController();
|
|
||||||
final _firstNameController = TextEditingController();
|
|
||||||
final _phoneController = TextEditingController();
|
|
||||||
final _emailController = TextEditingController();
|
|
||||||
final _passwordController = TextEditingController();
|
|
||||||
final _confirmPasswordController = TextEditingController();
|
|
||||||
final _addressController = TextEditingController();
|
|
||||||
final _postalCodeController = TextEditingController();
|
|
||||||
final _cityController = TextEditingController();
|
|
||||||
|
|
||||||
// File? _selectedImage;
|
|
||||||
// bool _photoConsent = false;
|
|
||||||
// final ImagePicker _picker = ImagePicker();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_registrationData = ChildminderRegistrationData();
|
|
||||||
_generateAndFillData();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _generateAndFillData() {
|
|
||||||
final String genFirstName = DataGenerator.firstName();
|
|
||||||
final String genLastName = DataGenerator.lastName();
|
|
||||||
final String genAddress = DataGenerator.address();
|
|
||||||
final String genPostalCode = DataGenerator.postalCode();
|
|
||||||
final String genCity = DataGenerator.city();
|
|
||||||
final String genPhone = DataGenerator.phone();
|
|
||||||
final String genEmail = DataGenerator.email(genFirstName, genLastName);
|
|
||||||
final String genPassword = DataGenerator.password();
|
|
||||||
|
|
||||||
_addressController.text = genAddress;
|
|
||||||
_postalCodeController.text = genPostalCode;
|
|
||||||
_cityController.text = genCity;
|
|
||||||
_firstNameController.text = genFirstName;
|
|
||||||
_lastNameController.text = genLastName;
|
|
||||||
_phoneController.text = genPhone;
|
|
||||||
_emailController.text = genEmail;
|
|
||||||
_passwordController.text = genPassword;
|
|
||||||
_confirmPasswordController.text = genPassword;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_registrationData.updateIdentity(
|
|
||||||
ChildminderId(
|
|
||||||
firstName: genFirstName,
|
|
||||||
lastName: genLastName,
|
|
||||||
address: genAddress,
|
|
||||||
postalCode: genPostalCode,
|
|
||||||
city: genCity,
|
|
||||||
phone: genPhone,
|
|
||||||
email: genEmail,
|
|
||||||
password: genPassword.trim(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_lastNameController.dispose();
|
|
||||||
_firstNameController.dispose();
|
|
||||||
_phoneController.dispose();
|
|
||||||
_emailController.dispose();
|
|
||||||
_passwordController.dispose();
|
|
||||||
_confirmPasswordController.dispose();
|
|
||||||
_addressController.dispose();
|
|
||||||
_postalCodeController.dispose();
|
|
||||||
_cityController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<List<ModularFormField>> get formFields => [
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Nom',
|
|
||||||
hint: 'Votre nom de famille',
|
|
||||||
controller: _lastNameController,
|
|
||||||
isRequired: true,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Prénom',
|
|
||||||
hint: 'Votre prénom',
|
|
||||||
controller: _firstNameController,
|
|
||||||
isRequired: true,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Téléphone',
|
|
||||||
hint: 'Votre numéro de téléphone',
|
|
||||||
controller: _phoneController,
|
|
||||||
keyboardType: TextInputType.phone,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Email',
|
|
||||||
hint: 'Votre adresse email',
|
|
||||||
controller: _emailController,
|
|
||||||
keyboardType: TextInputType.emailAddress,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Mot de passe',
|
|
||||||
hint: 'Votre mot de passe',
|
|
||||||
controller: _passwordController,
|
|
||||||
isPassword: true,
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) return 'Mot de passe requis';
|
|
||||||
if (value.length < 6) return '6 caractères minimum';
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
isRequired: true,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Confirmer le mot de passe',
|
|
||||||
hint: 'Confirmez votre mot de passe',
|
|
||||||
controller: _confirmPasswordController,
|
|
||||||
isPassword: true,
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) return 'Mot de passe requis';
|
|
||||||
if (value != _passwordController.text) return 'Les mots de passe ne correspondent pas';
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
isRequired: true,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Adresse (N° et Rue)',
|
|
||||||
hint: 'Numéro et nom de votre rue',
|
|
||||||
controller: _addressController,
|
|
||||||
isRequired: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Code postal',
|
|
||||||
hint: 'Votre code postal',
|
|
||||||
controller: _postalCodeController,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
isRequired: true,
|
|
||||||
flex: 1,
|
|
||||||
),
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Ville',
|
|
||||||
hint: 'Votre ville',
|
|
||||||
controller: _cityController,
|
|
||||||
flex: 4,
|
|
||||||
isRequired: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
void _handleSubmit() {
|
|
||||||
if (_formKey.currentState?.validate() ?? false) {
|
|
||||||
_registrationData.updateIdentity(
|
|
||||||
ChildminderId(
|
|
||||||
firstName: _firstNameController.text,
|
|
||||||
lastName: _lastNameController.text,
|
|
||||||
address: _addressController.text,
|
|
||||||
postalCode: _postalCodeController.text,
|
|
||||||
city: _cityController.text,
|
|
||||||
phone: _phoneController.text,
|
|
||||||
email: _emailController.text,
|
|
||||||
password: _passwordController.text,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
print('Vérification des données:');
|
|
||||||
print('Adresse: ${_registrationData.identity.address}');
|
|
||||||
print('Nom: ${_registrationData.identity.lastName}');
|
|
||||||
print('Prénom: ${_registrationData.identity.firstName}');
|
|
||||||
Navigator.pushNamed(context, '/am-register/step2',
|
|
||||||
arguments: _registrationData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/images/paper2.png',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
repeat: ImageRepeat.repeat,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Étape 1/4',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text(
|
|
||||||
'Informations d\'identité de l\'assistante maternelle',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Container(
|
|
||||||
width: screenSize.width * 0.6,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50),
|
|
||||||
constraints: const BoxConstraints(minHeight: 570),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(CardColorHorizontal.lavender.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: ModularForm(
|
|
||||||
formKey: _formKey,
|
|
||||||
fieldGroups: formFields,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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: () => Navigator.pop(context),
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: _handleSubmit,
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,251 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:p_tits_pas/models/am_user_registration_data.dart';
|
|
||||||
import 'package:p_tits_pas/models/card_assets.dart';
|
|
||||||
import 'package:p_tits_pas/utils/data_generator.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/FormFieldConfig.dart';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
class AmRegisterStep2Screen extends StatefulWidget {
|
|
||||||
final ChildminderRegistrationData registrationData;
|
|
||||||
const AmRegisterStep2Screen({super.key, required this.registrationData});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<AmRegisterStep2Screen> createState() => _AmRegisterStep2ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AmRegisterStep2ScreenState extends State<AmRegisterStep2Screen> {
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
late ChildminderRegistrationData _registrationData;
|
|
||||||
|
|
||||||
final _dateOfBirthController = TextEditingController();
|
|
||||||
final _birthCityController = TextEditingController();
|
|
||||||
final _birthCountryController = TextEditingController();
|
|
||||||
final _socialSecurityController = TextEditingController();
|
|
||||||
final _agreementNumberController = TextEditingController();
|
|
||||||
final _maxChildrenController = TextEditingController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_registrationData = widget.registrationData;
|
|
||||||
_generateAndFillData();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _generateAndFillData() {
|
|
||||||
_dateOfBirthController.text = DataGenerator.birthDate();
|
|
||||||
_birthCityController.text = DataGenerator.city();
|
|
||||||
_birthCountryController.text = "France";
|
|
||||||
_socialSecurityController.text = DataGenerator.socialSecurityNumber();
|
|
||||||
_agreementNumberController.text = DataGenerator.agreementNumber();
|
|
||||||
_maxChildrenController.text = "3";
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_dateOfBirthController.dispose();
|
|
||||||
_birthCityController.dispose();
|
|
||||||
_birthCountryController.dispose();
|
|
||||||
_socialSecurityController.dispose();
|
|
||||||
_agreementNumberController.dispose();
|
|
||||||
_maxChildrenController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _validateSocialSecurity(String? value) {
|
|
||||||
if (value == null || value.isEmpty)
|
|
||||||
return 'Numéro de sécurité sociale requis';
|
|
||||||
|
|
||||||
// Supprime les espaces pour la validation
|
|
||||||
String cleanValue = value.replaceAll(' ', '');
|
|
||||||
|
|
||||||
// Vérifie que c'est bien 13 ou 15 chiffres
|
|
||||||
if (cleanValue.length != 13 && cleanValue.length != 15) {
|
|
||||||
return 'Format invalide (13 ou 15 chiffres)';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!RegExp(r'^[0-9]+$').hasMatch(cleanValue)) {
|
|
||||||
return 'Seuls les chiffres sont autorisés';
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<List<ModularFormField>> get formFields => [
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Date de naissance',
|
|
||||||
hint: 'JJ/MM/AAAA',
|
|
||||||
controller: _dateOfBirthController,
|
|
||||||
keyboardType: TextInputType.datetime,
|
|
||||||
isRequired: true,
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty)
|
|
||||||
return 'Date de naissance requise';
|
|
||||||
// Validation basique du format de date
|
|
||||||
if (!RegExp(r'^[0-3][0-9]/[0-1][0-9]/[1-2][0-9]{3}$')
|
|
||||||
.hasMatch(value)) {
|
|
||||||
return 'Format invalide (JJ/MM/AAAA)';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Ville de naissance',
|
|
||||||
hint: 'Votre ville de naissance',
|
|
||||||
controller: _birthCityController,
|
|
||||||
isRequired: true,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Pays de naissance',
|
|
||||||
hint: 'Votre pays de naissance',
|
|
||||||
controller: _birthCountryController,
|
|
||||||
isRequired: true,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Numéro de Sécurité Sociale (NIR)',
|
|
||||||
hint: '1234567890123',
|
|
||||||
controller: _socialSecurityController,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
isRequired: true,
|
|
||||||
validator: _validateSocialSecurity,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Numéro d\'agrément',
|
|
||||||
hint: 'Votre numéro d\'agrément',
|
|
||||||
controller: _agreementNumberController,
|
|
||||||
isRequired: true,
|
|
||||||
flex: 12,
|
|
||||||
),
|
|
||||||
ModularFormField(
|
|
||||||
label: 'Nombre d\'enfants max',
|
|
||||||
hint: 'Ex: 3',
|
|
||||||
controller: _maxChildrenController,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
isRequired: true,
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) return 'Nombre requis';
|
|
||||||
int? number = int.tryParse(value);
|
|
||||||
if (number == null || number < 1 || number > 6) {
|
|
||||||
return 'Entre 1 et 6 enfants';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
flex: 6,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
void _handleSubmit() {
|
|
||||||
print('Vérification des données2:');
|
|
||||||
print('Adresse: ${_registrationData.identity.address}');
|
|
||||||
print('Nom: ${_registrationData.identity.lastName}');
|
|
||||||
print('Prénom: ${_registrationData.identity.firstName}');
|
|
||||||
if (_formKey.currentState?.validate() ?? false) {
|
|
||||||
_registrationData.updateProfessional(
|
|
||||||
ChildminderProfessional(
|
|
||||||
dateOfBirth: _dateOfBirthController.text,
|
|
||||||
birthCity: _birthCityController.text,
|
|
||||||
birthCountry: _birthCountryController.text,
|
|
||||||
socialSecurityNumber: _socialSecurityController.text,
|
|
||||||
agreementNumber: _agreementNumberController.text,
|
|
||||||
maxChildren: int.tryParse(_maxChildrenController.text) ?? 1,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Navigator.pushNamed(context, '/am-register/step3',
|
|
||||||
arguments: _registrationData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/images/paper2.png',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
repeat: ImageRepeat.repeat,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Étape 2/4',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 16, color: Colors.black54),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text(
|
|
||||||
'Informations professionnelles de l\'assistante maternelle',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Container(
|
|
||||||
width: screenSize.width * 0.6,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 50, horizontal: 50),
|
|
||||||
constraints: const BoxConstraints(minHeight: 570),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(CardColorHorizontal.peach.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: ModularForm(
|
|
||||||
formKey: _formKey,
|
|
||||||
fieldGroups: formFields,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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: () => Navigator.pop(context),
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: _handleSubmit,
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,216 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:p_tits_pas/models/am_user_registration_data.dart';
|
|
||||||
import 'package:p_tits_pas/models/card_assets.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/app_custom_checkbox.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/custom_decorated_text_field.dart';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
|
|
||||||
class AmRegisterStep3Screen extends StatefulWidget {
|
|
||||||
final ChildminderRegistrationData registrationData;
|
|
||||||
const AmRegisterStep3Screen({super.key, required this.registrationData});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<AmRegisterStep3Screen> createState() => _AmRegisterStep3ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AmRegisterStep3ScreenState extends State<AmRegisterStep3Screen> {
|
|
||||||
|
|
||||||
late ChildminderRegistrationData _registrationData;
|
|
||||||
final _presentationMessageController = TextEditingController();
|
|
||||||
bool _cguAccepted = true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_registrationData = widget.registrationData;
|
|
||||||
_presentationMessageController.text = _registrationData.presentationMessage;
|
|
||||||
// _cguAccepted = _registrationData.cguAccepted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_presentationMessageController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showCGUModal() {
|
|
||||||
const String loremIpsumText = '''
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.
|
|
||||||
|
|
||||||
Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna.
|
|
||||||
|
|
||||||
Aliquam convallis sollicitudin purus. Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet.
|
|
||||||
|
|
||||||
Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.
|
|
||||||
|
|
||||||
Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna. Etiam et felis dolor.
|
|
||||||
|
|
||||||
Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
|
|
||||||
|
|
||||||
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
|
|
||||||
''';
|
|
||||||
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false, // L'utilisateur doit utiliser le bouton
|
|
||||||
builder: (BuildContext dialogContext) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
'Conditions Générales d\'Utilisation',
|
|
||||||
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
content: SizedBox(
|
|
||||||
width: MediaQuery.of(dialogContext).size.width * 0.7, // 70% de la largeur de l'écran
|
|
||||||
height: MediaQuery.of(dialogContext).size.height * 0.6, // 60% de la hauteur de l'écran
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Text(
|
|
||||||
loremIpsumText,
|
|
||||||
style: GoogleFonts.merienda(fontSize: 13),
|
|
||||||
textAlign: TextAlign.justify,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actionsPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
|
|
||||||
actionsAlignment: MainAxisAlignment.center,
|
|
||||||
actions: <Widget>[
|
|
||||||
ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(dialogContext).primaryColor,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Valider et Accepter',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(dialogContext).pop(); // Ferme la modale
|
|
||||||
setState(() {
|
|
||||||
_cguAccepted = true; // Met à jour l'état
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@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(
|
|
||||||
'Étape 3/4',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Text(
|
|
||||||
'Message à destination du gestionnaire pour justifier votre demande ou ajouter des précisions',
|
|
||||||
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(CardColorHorizontal.green.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(40.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: CustomDecoratedTextField(
|
|
||||||
controller: _presentationMessageController,
|
|
||||||
hintText: 'Écrivez ici pour motiver votre demande...',
|
|
||||||
fieldHeight: cardHeight * 0.6,
|
|
||||||
maxLines: 10,
|
|
||||||
expandDynamically: true,
|
|
||||||
fontSize: 18.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (!_cguAccepted) {
|
|
||||||
_showCGUModal();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: AppCustomCheckbox(
|
|
||||||
label: 'J\'accepte les conditions générales d\'utilisation',
|
|
||||||
value: _cguAccepted,
|
|
||||||
onChanged: (newValue) {
|
|
||||||
if (!_cguAccepted) {
|
|
||||||
_showCGUModal();
|
|
||||||
} else {
|
|
||||||
setState(() => _cguAccepted = false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Chevrons de navigation
|
|
||||||
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: () => Navigator.pop(context),
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: _cguAccepted
|
|
||||||
? () {
|
|
||||||
_registrationData.updatePresentation(_presentationMessageController.text);
|
|
||||||
_registrationData.acceptCGU();
|
|
||||||
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context,
|
|
||||||
'/am-register/step4',
|
|
||||||
arguments: _registrationData
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,269 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:p_tits_pas/models/am_user_registration_data.dart';
|
|
||||||
import 'package:p_tits_pas/models/card_assets.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/Summary.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/custom_decorated_text_field.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/image_button.dart';
|
|
||||||
|
|
||||||
Widget _buildDisplayFieldValue(BuildContext context, String label, String value, {bool multiLine = false, double fieldHeight = 50.0, double labelFontSize = 18.0}) {
|
|
||||||
const FontWeight labelFontWeight = FontWeight.w600;
|
|
||||||
|
|
||||||
// Ne pas afficher le label si labelFontSize est 0 ou si label est vide
|
|
||||||
bool showLabel = label.isNotEmpty && labelFontSize > 0;
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (showLabel)
|
|
||||||
Text(label, style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: labelFontWeight)),
|
|
||||||
if (showLabel)
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
// Utiliser Expanded si multiLine et pas de hauteur fixe, sinon Container
|
|
||||||
multiLine && fieldHeight == null
|
|
||||||
? Expanded(
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: const DecorationImage(
|
|
||||||
image: AssetImage('assets/images/input_field_bg.png'),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: SingleChildScrollView( // Pour le défilement si le texte dépasse
|
|
||||||
child: Text(
|
|
||||||
value.isNotEmpty ? value : '-',
|
|
||||||
style: GoogleFonts.merienda(fontSize: labelFontSize > 0 ? labelFontSize : 18.0), // Garder une taille de texte par défaut si label caché
|
|
||||||
maxLines: null, // Permettre un nombre illimité de lignes
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: multiLine ? null : fieldHeight,
|
|
||||||
constraints: multiLine ? BoxConstraints(minHeight: fieldHeight) : null,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: const DecorationImage(
|
|
||||||
image: AssetImage('assets/images/input_field_bg.png'),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
value.isNotEmpty ? value : '-',
|
|
||||||
style: GoogleFonts.merienda(fontSize: labelFontSize > 0 ? labelFontSize : 18.0),
|
|
||||||
maxLines: multiLine ? null : 1,
|
|
||||||
overflow: multiLine ? TextOverflow.visible : TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class AmRegisterStep4Screen extends StatelessWidget {
|
|
||||||
final ChildminderRegistrationData registrationData;
|
|
||||||
|
|
||||||
const AmRegisterStep4Screen({super.key, required this.registrationData});
|
|
||||||
|
|
||||||
Widget _buildAm1Card(BuildContext context, ChildminderRegistrationData data) {
|
|
||||||
const double verticalSpacing = 28.0; // Espacement vertical augmenté
|
|
||||||
const double labelFontSize = 22.0; // Taille de label augmentée
|
|
||||||
|
|
||||||
List<Widget> details = [
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Nom:", data.identity.lastName, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Prénom:", data.identity.firstName, labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: verticalSpacing),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Téléphone:", data.identity.phone, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Email:", data.identity.email, multiLine: true, labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: verticalSpacing),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Adresse:", "${data.identity.address}\n${data.identity.postalCode} ${data.identity.city}".trim(), labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
return SummaryCard(
|
|
||||||
backgroundImagePath: CardColorHorizontal.peach.path,
|
|
||||||
title: 'Informations d’identité',
|
|
||||||
content: details,
|
|
||||||
onEdit: () => Navigator.of(context).pushNamed('/am-register/step1', arguments: registrationData),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAm2Card(BuildContext context, ChildminderRegistrationData data) {
|
|
||||||
const double verticalSpacing = 28.0;
|
|
||||||
const double labelFontSize = 22.0;
|
|
||||||
|
|
||||||
List<Widget> myDetails = [
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Date de naissance:", data.professional.dateOfBirth, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Ville de naissance:", data.professional.birthCity, labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: verticalSpacing),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Pays de naissance:", data.professional.birthCountry, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Numéro de sécurité sociale:", data.professional.socialSecurityNumber, labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: verticalSpacing),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Numéro d'agrément:", data.professional.agreementNumber, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Nombre d'enfants maximum:", data.professional.maxChildren.toString(), labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
return SummaryCard(
|
|
||||||
backgroundImagePath: CardColorHorizontal.lavender.path,
|
|
||||||
title: 'Informations professionnelles',
|
|
||||||
content: myDetails,
|
|
||||||
onEdit: () => Navigator.of(context)
|
|
||||||
.pushNamed('/am-register/step2', arguments: registrationData),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildMotivationCard(BuildContext context, ChildminderRegistrationData data) {
|
|
||||||
return SummaryCard(
|
|
||||||
backgroundImagePath: CardColorHorizontal.green.path,
|
|
||||||
title: 'Motivation',
|
|
||||||
content: [
|
|
||||||
Expanded(child: CustomDecoratedTextField(
|
|
||||||
controller: TextEditingController(text: data.presentationMessage),
|
|
||||||
hintText: 'Parlez-nous de votre motivation',
|
|
||||||
fieldHeight: 200,
|
|
||||||
maxLines: 10,
|
|
||||||
expandDynamically: true,
|
|
||||||
readOnly: true,
|
|
||||||
fontSize: 18.0,)),
|
|
||||||
],
|
|
||||||
onEdit: () => Navigator.of(context)
|
|
||||||
.pushNamed('/am-register/step3', arguments: registrationData),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
return Scaffold(
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeatY),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.all(40.0),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: screenSize.width / 4.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text('Etape 4/4',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 16, color: Colors.black54)),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Text('Récapitulatif de votre demande',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 22,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87),
|
|
||||||
textAlign: TextAlign.center),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
_buildAm1Card(context, registrationData),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
if (registrationData.professional != null) ...[
|
|
||||||
_buildAm2Card(context, registrationData),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
],
|
|
||||||
_buildMotivationCard(context, registrationData),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
ImageButton(
|
|
||||||
bg: 'assets/images/btn_green.png',
|
|
||||||
text: 'Soumettre ma demande',
|
|
||||||
textColor: const Color(0xFF2D6A4F),
|
|
||||||
width: 350,
|
|
||||||
height: 50,
|
|
||||||
fontSize: 18,
|
|
||||||
onPressed: () {
|
|
||||||
// Vérification des données requises
|
|
||||||
_showConfirmationModal(context);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
left: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Transform.flip(
|
|
||||||
flipX: true,
|
|
||||||
child: Image.asset('assets/images/chevron_right.png',
|
|
||||||
height: 40)),
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showConfirmationModal(BuildContext context) {
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (BuildContext dialogContext) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
'Demande enregistrée',
|
|
||||||
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
content: Text(
|
|
||||||
'Votre dossier a bien été pris en compte. Un gestionnaire le validera bientôt.',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 14),
|
|
||||||
),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
child: Text('OK',
|
|
||||||
style: GoogleFonts.merienda(fontWeight: FontWeight.bold)),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(dialogContext).pop();
|
|
||||||
Navigator.of(context)
|
|
||||||
.pushNamedAndRemoveUntil('/login', (route) => false);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
64
frontend/lib/screens/auth/am_register_step1_screen.dart
Normal file
64
frontend/lib/screens/auth/am_register_step1_screen.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../../models/am_registration_data.dart';
|
||||||
|
import '../../utils/data_generator.dart';
|
||||||
|
import '../../widgets/personal_info_form_screen.dart';
|
||||||
|
import '../../models/card_assets.dart';
|
||||||
|
|
||||||
|
class AmRegisterStep1Screen extends StatelessWidget {
|
||||||
|
const AmRegisterStep1Screen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final registrationData = Provider.of<AmRegistrationData>(context, listen: false);
|
||||||
|
|
||||||
|
// Générer des données de test si vide
|
||||||
|
PersonalInfoData initialData;
|
||||||
|
if (registrationData.firstName.isEmpty) {
|
||||||
|
final genFirstName = DataGenerator.firstName();
|
||||||
|
final genLastName = DataGenerator.lastName();
|
||||||
|
initialData = PersonalInfoData(
|
||||||
|
firstName: genFirstName,
|
||||||
|
lastName: genLastName,
|
||||||
|
phone: DataGenerator.phone(),
|
||||||
|
email: DataGenerator.email(genFirstName, genLastName),
|
||||||
|
address: DataGenerator.address(),
|
||||||
|
postalCode: DataGenerator.postalCode(),
|
||||||
|
city: DataGenerator.city(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
initialData = PersonalInfoData(
|
||||||
|
firstName: registrationData.firstName,
|
||||||
|
lastName: registrationData.lastName,
|
||||||
|
phone: registrationData.phone,
|
||||||
|
email: registrationData.email,
|
||||||
|
address: registrationData.streetAddress,
|
||||||
|
postalCode: registrationData.postalCode,
|
||||||
|
city: registrationData.city,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PersonalInfoFormScreen(
|
||||||
|
stepText: 'Étape 1/4',
|
||||||
|
title: 'Vos informations personnelles',
|
||||||
|
cardColor: CardColorHorizontal.blue,
|
||||||
|
initialData: initialData,
|
||||||
|
previousRoute: '/register-choice',
|
||||||
|
onSubmit: (data, {hasSecondPerson, sameAddress}) {
|
||||||
|
registrationData.updateIdentityInfo(
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
phone: data.phone,
|
||||||
|
email: data.email,
|
||||||
|
streetAddress: data.address,
|
||||||
|
postalCode: data.postalCode,
|
||||||
|
city: data.city,
|
||||||
|
password: '',
|
||||||
|
);
|
||||||
|
context.go('/am-register-step2');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
93
frontend/lib/screens/auth/am_register_step2_screen.dart
Normal file
93
frontend/lib/screens/auth/am_register_step2_screen.dart
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import '../../models/am_registration_data.dart';
|
||||||
|
import '../../models/card_assets.dart';
|
||||||
|
import '../../utils/data_generator.dart';
|
||||||
|
import '../../widgets/professional_info_form_screen.dart';
|
||||||
|
|
||||||
|
class AmRegisterStep2Screen extends StatefulWidget {
|
||||||
|
const AmRegisterStep2Screen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AmRegisterStep2Screen> createState() => _AmRegisterStep2ScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AmRegisterStep2ScreenState extends State<AmRegisterStep2Screen> {
|
||||||
|
String? _photoPathFramework;
|
||||||
|
File? _photoFile;
|
||||||
|
|
||||||
|
Future<void> _pickPhoto() async {
|
||||||
|
// TODO: Remplacer par la vraie logique ImagePicker
|
||||||
|
// final imagePicker = ImagePicker();
|
||||||
|
// final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);
|
||||||
|
// if (pickedFile != null) {
|
||||||
|
// setState(() {
|
||||||
|
// _photoFile = File(pickedFile.path);
|
||||||
|
// _photoPathFramework = pickedFile.path;
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
setState(() {
|
||||||
|
_photoPathFramework = 'assets/images/icon_assmat.png';
|
||||||
|
_photoFile = null;
|
||||||
|
});
|
||||||
|
// }
|
||||||
|
print("Photo sélectionnée: $_photoPathFramework");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final registrationData = Provider.of<AmRegistrationData>(context, listen: false);
|
||||||
|
|
||||||
|
// Préparer les données initiales
|
||||||
|
ProfessionalInfoData initialData = ProfessionalInfoData(
|
||||||
|
photoPath: registrationData.photoPath,
|
||||||
|
photoConsent: registrationData.photoConsent,
|
||||||
|
dateOfBirth: registrationData.dateOfBirth,
|
||||||
|
birthCity: registrationData.birthCity,
|
||||||
|
birthCountry: registrationData.birthCountry,
|
||||||
|
nir: registrationData.nir,
|
||||||
|
agrementNumber: registrationData.agrementNumber,
|
||||||
|
capacity: registrationData.capacity,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Générer des données de test si les champs sont vides
|
||||||
|
if (registrationData.dateOfBirth == null && registrationData.nir.isEmpty) {
|
||||||
|
initialData = ProfessionalInfoData(
|
||||||
|
photoPath: 'assets/images/icon_assmat.png',
|
||||||
|
photoConsent: true,
|
||||||
|
dateOfBirth: DateTime(1985, 3, 15),
|
||||||
|
birthCity: DataGenerator.city(),
|
||||||
|
birthCountry: 'France',
|
||||||
|
nir: '${DataGenerator.randomIntInRange(1, 3)}${DataGenerator.randomIntInRange(80, 96)}${DataGenerator.randomIntInRange(1, 13).toString().padLeft(2, '0')}${DataGenerator.randomIntInRange(1, 100).toString().padLeft(2, '0')}${DataGenerator.randomIntInRange(100, 1000).toString().padLeft(3, '0')}${DataGenerator.randomIntInRange(100, 1000).toString().padLeft(3, '0')}${DataGenerator.randomIntInRange(10, 100).toString().padLeft(2, '0')}',
|
||||||
|
agrementNumber: 'AM${DataGenerator.randomIntInRange(10000, 100000)}',
|
||||||
|
capacity: DataGenerator.randomIntInRange(1, 5),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProfessionalInfoFormScreen(
|
||||||
|
stepText: 'Étape 2/4',
|
||||||
|
title: 'Vos informations professionnelles',
|
||||||
|
cardColor: CardColorHorizontal.green,
|
||||||
|
initialData: initialData,
|
||||||
|
previousRoute: '/am-register-step1',
|
||||||
|
onPickPhoto: _pickPhoto,
|
||||||
|
onSubmit: (data) {
|
||||||
|
registrationData.updateProfessionalInfo(
|
||||||
|
photoPath: _photoPathFramework ?? data.photoPath,
|
||||||
|
photoConsent: data.photoConsent,
|
||||||
|
dateOfBirth: data.dateOfBirth,
|
||||||
|
birthCity: data.birthCity,
|
||||||
|
birthCountry: data.birthCountry,
|
||||||
|
nir: data.nir,
|
||||||
|
agrementNumber: data.agrementNumber,
|
||||||
|
capacity: data.capacity,
|
||||||
|
);
|
||||||
|
context.go('/am-register-step3');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
frontend/lib/screens/auth/am_register_step3_screen.dart
Normal file
42
frontend/lib/screens/auth/am_register_step3_screen.dart
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../../models/am_registration_data.dart';
|
||||||
|
import '../../widgets/presentation_form_screen.dart';
|
||||||
|
import '../../models/card_assets.dart';
|
||||||
|
|
||||||
|
class AmRegisterStep3Screen extends StatelessWidget {
|
||||||
|
const AmRegisterStep3Screen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final data = Provider.of<AmRegistrationData>(context, listen: false);
|
||||||
|
|
||||||
|
// Générer un texte de test si vide
|
||||||
|
String initialText = data.presentationText;
|
||||||
|
bool initialCgu = data.cguAccepted;
|
||||||
|
|
||||||
|
if (initialText.isEmpty) {
|
||||||
|
initialText = 'Disponible immédiatement, plus de 10 ans d\'expérience avec les tout-petits. Formation aux premiers secours à jour. Je dispose d\'un jardin sécurisé et d\'un espace de jeu adapté.';
|
||||||
|
initialCgu = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PresentationFormScreen(
|
||||||
|
stepText: 'Étape 3/4',
|
||||||
|
title: 'Présentation et Conditions',
|
||||||
|
cardColor: CardColorHorizontal.peach,
|
||||||
|
textFieldHint: 'Ex: Disponible immédiatement, 10 ans d\'expérience, formation premiers secours...',
|
||||||
|
initialText: initialText,
|
||||||
|
initialCguAccepted: initialCgu,
|
||||||
|
previousRoute: '/am-register-step2',
|
||||||
|
onSubmit: (text, cguAccepted) {
|
||||||
|
data.updatePresentationAndCgu(
|
||||||
|
presentationText: text,
|
||||||
|
cguAccepted: cguAccepted,
|
||||||
|
);
|
||||||
|
context.go('/am-register-step4');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
339
frontend/lib/screens/auth/am_register_step4_screen.dart
Normal file
339
frontend/lib/screens/auth/am_register_step4_screen.dart
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import '../../models/am_registration_data.dart';
|
||||||
|
import '../../widgets/image_button.dart';
|
||||||
|
import '../../models/card_assets.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
// Méthode helper pour afficher un champ de type "lecture seule" stylisé
|
||||||
|
Widget _buildDisplayFieldValue(BuildContext context, String label, String value, {bool multiLine = false, double fieldHeight = 50.0, double labelFontSize = 18.0}) {
|
||||||
|
const FontWeight labelFontWeight = FontWeight.w600;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(label, style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: labelFontWeight)),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: multiLine ? null : fieldHeight,
|
||||||
|
constraints: multiLine ? const BoxConstraints(minHeight: 50.0) : null,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage('assets/images/input_field_bg.png'),
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
value.isNotEmpty ? value : '-',
|
||||||
|
style: GoogleFonts.merienda(fontSize: labelFontSize),
|
||||||
|
maxLines: multiLine ? null : 1,
|
||||||
|
overflow: multiLine ? TextOverflow.visible : TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AmRegisterStep4Screen extends StatefulWidget {
|
||||||
|
const AmRegisterStep4Screen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AmRegisterStep4ScreenState createState() => _AmRegisterStep4ScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AmRegisterStep4ScreenState extends State<AmRegisterStep4Screen> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final registrationData = Provider.of<AmRegistrationData>(context);
|
||||||
|
final screenSize = MediaQuery.of(context).size;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeatY),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 40.0),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: screenSize.width / 4.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text('Étape 4/4', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text('Récapitulatif de votre demande', style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), textAlign: TextAlign.center),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
_buildPersonalInfoCard(context, registrationData),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildProfessionalInfoCard(context, registrationData),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildPresentationCard(context, registrationData),
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
|
||||||
|
ImageButton(
|
||||||
|
bg: 'assets/images/btn_green.png',
|
||||||
|
text: 'Soumettre ma demande',
|
||||||
|
textColor: const Color(0xFF2D6A4F),
|
||||||
|
width: 350,
|
||||||
|
height: 50,
|
||||||
|
fontSize: 18,
|
||||||
|
onPressed: () {
|
||||||
|
print("Données AM finales: ${registrationData.firstName} ${registrationData.lastName}");
|
||||||
|
_showConfirmationModal(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: screenSize.height / 2 - 20,
|
||||||
|
left: 40,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Transform.flip(flipX: true, child: Image.asset('assets/images/chevron_right.png', height: 40)),
|
||||||
|
onPressed: () {
|
||||||
|
if (context.canPop()) {
|
||||||
|
context.pop();
|
||||||
|
} else {
|
||||||
|
context.go('/am-register-step3');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: 'Retour',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showConfirmationModal(BuildContext context) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext dialogContext) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(
|
||||||
|
'Demande enregistrée',
|
||||||
|
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
content: Text(
|
||||||
|
'Votre dossier a bien été pris en compte. Un gestionnaire le validera bientôt.',
|
||||||
|
style: GoogleFonts.merienda(fontSize: 14),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(dialogContext).pop();
|
||||||
|
context.go('/login');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carte Informations personnelles
|
||||||
|
Widget _buildPersonalInfoCard(BuildContext context, AmRegistrationData data) {
|
||||||
|
const double verticalSpacing = 28.0;
|
||||||
|
const double labelFontSize = 22.0;
|
||||||
|
|
||||||
|
List<Widget> details = [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(child: _buildDisplayFieldValue(context, "Nom:", data.lastName, labelFontSize: labelFontSize)),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Expanded(child: _buildDisplayFieldValue(context, "Prénom:", data.firstName, labelFontSize: labelFontSize)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: verticalSpacing),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(child: _buildDisplayFieldValue(context, "Téléphone:", data.phone, labelFontSize: labelFontSize)),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Expanded(child: _buildDisplayFieldValue(context, "Email:", data.email, multiLine: true, labelFontSize: labelFontSize)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: verticalSpacing),
|
||||||
|
_buildDisplayFieldValue(context, "Adresse:", "${data.streetAddress}\n${data.postalCode} ${data.city}".trim(), multiLine: true, fieldHeight: 80, labelFontSize: labelFontSize),
|
||||||
|
const SizedBox(height: verticalSpacing),
|
||||||
|
_buildDisplayFieldValue(context, "Consentement photo:", data.photoConsent ? "Oui" : "Non", labelFontSize: labelFontSize),
|
||||||
|
];
|
||||||
|
|
||||||
|
return _SummaryCard(
|
||||||
|
backgroundImagePath: CardColorHorizontal.blue.path,
|
||||||
|
title: 'Informations personnelles',
|
||||||
|
content: details,
|
||||||
|
onEdit: () => context.go('/am-register-step1'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carte Informations professionnelles
|
||||||
|
Widget _buildProfessionalInfoCard(BuildContext context, AmRegistrationData data) {
|
||||||
|
const double verticalSpacing = 28.0;
|
||||||
|
const double labelFontSize = 22.0;
|
||||||
|
|
||||||
|
String formattedDate = '-';
|
||||||
|
if (data.dateOfBirth != null) {
|
||||||
|
formattedDate = '${data.dateOfBirth!.day.toString().padLeft(2, '0')}/${data.dateOfBirth!.month.toString().padLeft(2, '0')}/${data.dateOfBirth!.year}';
|
||||||
|
}
|
||||||
|
String birthPlace = '${data.birthCity}, ${data.birthCountry}'.trim();
|
||||||
|
if (birthPlace == ',') birthPlace = '-';
|
||||||
|
|
||||||
|
List<Widget> details = [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(child: _buildDisplayFieldValue(context, "Date de naissance:", formattedDate, labelFontSize: labelFontSize)),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Expanded(child: _buildDisplayFieldValue(context, "Lieu de naissance:", birthPlace, labelFontSize: labelFontSize, multiLine: true)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: verticalSpacing),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(child: _buildDisplayFieldValue(context, "N° Sécurité Sociale:", data.nir, labelFontSize: labelFontSize)),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Expanded(child: _buildDisplayFieldValue(context, "N° Agrément:", data.agrementNumber, labelFontSize: labelFontSize)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: verticalSpacing),
|
||||||
|
_buildDisplayFieldValue(context, "Capacité d'accueil:", data.capacity?.toString() ?? '-', labelFontSize: labelFontSize),
|
||||||
|
];
|
||||||
|
|
||||||
|
return _SummaryCard(
|
||||||
|
backgroundImagePath: CardColorHorizontal.green.path,
|
||||||
|
title: 'Informations professionnelles',
|
||||||
|
content: details,
|
||||||
|
onEdit: () => context.go('/am-register-step2'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carte Présentation & CGU
|
||||||
|
Widget _buildPresentationCard(BuildContext context, AmRegistrationData data) {
|
||||||
|
const double labelFontSize = 22.0;
|
||||||
|
|
||||||
|
List<Widget> details = [
|
||||||
|
Text(
|
||||||
|
'Votre présentation (facultatif) :',
|
||||||
|
style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
constraints: const BoxConstraints(minHeight: 80.0),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage('assets/images/input_field_bg.png'),
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
data.presentationText.isNotEmpty ? data.presentationText : 'Aucune présentation rédigée.',
|
||||||
|
style: GoogleFonts.merienda(
|
||||||
|
fontSize: 18,
|
||||||
|
fontStyle: data.presentationText.isNotEmpty ? FontStyle.normal : FontStyle.italic,
|
||||||
|
color: data.presentationText.isNotEmpty ? Colors.black87 : Colors.black54,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
data.cguAccepted ? Icons.check_circle : Icons.cancel,
|
||||||
|
color: data.cguAccepted ? Colors.green : Colors.red,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
data.cguAccepted ? 'CGU acceptées' : 'CGU non acceptées',
|
||||||
|
style: GoogleFonts.merienda(fontSize: 18),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
return _SummaryCard(
|
||||||
|
backgroundImagePath: CardColorHorizontal.peach.path,
|
||||||
|
title: 'Présentation & CGU',
|
||||||
|
content: details,
|
||||||
|
onEdit: () => context.go('/am-register-step3'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget générique _SummaryCard
|
||||||
|
class _SummaryCard extends StatelessWidget {
|
||||||
|
final String backgroundImagePath;
|
||||||
|
final String title;
|
||||||
|
final List<Widget> content;
|
||||||
|
final VoidCallback onEdit;
|
||||||
|
|
||||||
|
const _SummaryCard({
|
||||||
|
super.key,
|
||||||
|
required this.backgroundImagePath,
|
||||||
|
required this.title,
|
||||||
|
required this.content,
|
||||||
|
required this.onEdit,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: 2.0,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 25.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(backgroundImagePath),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
...content,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.edit, color: Colors.black54, size: 28),
|
||||||
|
onPressed: onEdit,
|
||||||
|
tooltip: 'Modifier',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,239 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
|
||||||
|
|
||||||
import '../../../models/nanny_registration_data.dart';
|
|
||||||
import '../../../widgets/custom_app_text_field.dart';
|
|
||||||
import '../../../models/card_assets.dart'; // Pour les cartes
|
|
||||||
import '../../../utils/data_generator.dart'; // Implied import for DataGenerator
|
|
||||||
|
|
||||||
class NannyRegisterStep1Screen extends StatefulWidget {
|
|
||||||
const NannyRegisterStep1Screen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<NannyRegisterStep1Screen> createState() => _NannyRegisterStep1ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NannyRegisterStep1ScreenState extends State<NannyRegisterStep1Screen> {
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
|
|
||||||
final _firstNameController = TextEditingController();
|
|
||||||
final _lastNameController = TextEditingController();
|
|
||||||
final _streetAddressController = TextEditingController();
|
|
||||||
final _postalCodeController = TextEditingController();
|
|
||||||
final _cityController = TextEditingController();
|
|
||||||
final _phoneController = TextEditingController();
|
|
||||||
final _emailController = TextEditingController();
|
|
||||||
final _passwordController = TextEditingController();
|
|
||||||
final _confirmPasswordController = TextEditingController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final data = Provider.of<NannyRegistrationData>(context, listen: false);
|
|
||||||
|
|
||||||
_firstNameController.text = data.firstName;
|
|
||||||
_lastNameController.text = data.lastName;
|
|
||||||
_streetAddressController.text = data.streetAddress;
|
|
||||||
_postalCodeController.text = data.postalCode;
|
|
||||||
_cityController.text = data.city;
|
|
||||||
_phoneController.text = data.phone;
|
|
||||||
_emailController.text = data.email;
|
|
||||||
_passwordController.text = data.password;
|
|
||||||
_confirmPasswordController.text = data.password.isNotEmpty ? data.password : '';
|
|
||||||
|
|
||||||
if (data.firstName.isEmpty && data.lastName.isEmpty) {
|
|
||||||
final String genFirstName = DataGenerator.firstName();
|
|
||||||
final String genLastName = DataGenerator.lastName();
|
|
||||||
_firstNameController.text = genFirstName;
|
|
||||||
_lastNameController.text = genLastName;
|
|
||||||
_streetAddressController.text = DataGenerator.address();
|
|
||||||
_postalCodeController.text = DataGenerator.postalCode();
|
|
||||||
_cityController.text = DataGenerator.city();
|
|
||||||
_phoneController.text = DataGenerator.phone();
|
|
||||||
_emailController.text = DataGenerator.email(genFirstName, genLastName);
|
|
||||||
_passwordController.text = DataGenerator.password();
|
|
||||||
_confirmPasswordController.text = _passwordController.text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_firstNameController.dispose();
|
|
||||||
_lastNameController.dispose();
|
|
||||||
_streetAddressController.dispose();
|
|
||||||
_postalCodeController.dispose();
|
|
||||||
_cityController.dispose();
|
|
||||||
_phoneController.dispose();
|
|
||||||
_emailController.dispose();
|
|
||||||
_passwordController.dispose();
|
|
||||||
_confirmPasswordController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _submitForm() {
|
|
||||||
if (_formKey.currentState?.validate() ?? false) {
|
|
||||||
Provider.of<NannyRegistrationData>(context, listen: false)
|
|
||||||
.updateIdentityInfo(
|
|
||||||
firstName: _firstNameController.text,
|
|
||||||
lastName: _lastNameController.text,
|
|
||||||
streetAddress: _streetAddressController.text,
|
|
||||||
postalCode: _postalCodeController.text,
|
|
||||||
city: _cityController.text,
|
|
||||||
phone: _phoneController.text,
|
|
||||||
email: _emailController.text,
|
|
||||||
password: _passwordController.text,
|
|
||||||
);
|
|
||||||
context.go('/nanny-register-step2');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
const cardColor = CardColorHorizontal.blue;
|
|
||||||
|
|
||||||
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),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Étape 1/4',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Container(
|
|
||||||
width: screenSize.width * 0.7,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 50),
|
|
||||||
constraints: const BoxConstraints(minHeight: 600),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(cardColor.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Vos informations personnelles',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _lastNameController, labelText: 'Nom', hintText: 'Votre nom', fieldWidth: double.infinity)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _firstNameController, labelText: 'Prénom', hintText: 'Votre prénom', fieldWidth: double.infinity)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _phoneController, labelText: 'Téléphone', hintText: 'Votre téléphone', keyboardType: TextInputType.phone, fieldWidth: double.infinity)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _emailController, labelText: 'Email', hintText: 'Votre e-mail', keyboardType: TextInputType.emailAddress, fieldWidth: double.infinity)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _passwordController, labelText: 'Mot de passe', hintText: 'Minimum 6 caractères', obscureText: true, fieldWidth: double.infinity,
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) return 'Mot de passe requis';
|
|
||||||
if (value.length < 6) return '6 caractères minimum';
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _confirmPasswordController, labelText: 'Confirmation', hintText: 'Confirmez le mot de passe', obscureText: true, fieldWidth: double.infinity,
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) return 'Confirmation requise';
|
|
||||||
if (value != _passwordController.text) return 'Les mots de passe ne correspondent pas';
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _streetAddressController,
|
|
||||||
labelText: 'Adresse (N° et Rue)',
|
|
||||||
hintText: 'Numéro et nom de votre rue',
|
|
||||||
fieldWidth: double.infinity,
|
|
||||||
validator: (v) => v!.isEmpty ? 'Adresse requise' : null,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 5, child: CustomAppTextField(controller: _postalCodeController, labelText: 'Code Postal', hintText: 'C.P.', keyboardType: TextInputType.number, fieldWidth: double.infinity, validator: (v) => v!.isEmpty ? 'C.P. requis' : null)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _cityController, labelText: 'Ville', hintText: 'Votre ville', fieldWidth: double.infinity, validator: (v) => v!.isEmpty ? 'Ville requise' : null)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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('/register-choice');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: _submitForm,
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,145 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
import '../../../models/nanny_registration_data.dart';
|
|
||||||
import '../../../widgets/custom_decorated_text_field.dart';
|
|
||||||
import '../../../models/card_assets.dart';
|
|
||||||
|
|
||||||
class NannyRegisterStep3Screen extends StatefulWidget {
|
|
||||||
const NannyRegisterStep3Screen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<NannyRegisterStep3Screen> createState() => _NannyRegisterStep3ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NannyRegisterStep3ScreenState extends State<NannyRegisterStep3Screen> {
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
final _presentationController = TextEditingController();
|
|
||||||
bool _cguAccepted = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final data = Provider.of<NannyRegistrationData>(context, listen: false);
|
|
||||||
_presentationController.text = 'Disponible immédiatement, expérience avec les tout-petits.';
|
|
||||||
_cguAccepted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_presentationController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _submitForm() {
|
|
||||||
final nannyData = Provider.of<NannyRegistrationData>(context, listen: false);
|
|
||||||
nannyData.updatePresentationAndCgu(presentationText: _presentationController.text);
|
|
||||||
// Validation CGU désactivée temporairement
|
|
||||||
nannyData.updatePresentationAndCgu(cguAccepted: _cguAccepted);
|
|
||||||
context.go('/nanny-register-step4');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final nannyData = Provider.of<NannyRegistrationData>(context, listen: false);
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
const cardColor = CardColorHorizontal.peach; // Couleur différente
|
|
||||||
|
|
||||||
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),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text('Étape 3/4', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Container(
|
|
||||||
width: screenSize.width * 0.7,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 50),
|
|
||||||
constraints: const BoxConstraints(minHeight: 500), // Ajuster hauteur
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(image: AssetImage(cardColor.path), fit: BoxFit.fill),
|
|
||||||
),
|
|
||||||
child: Form( // Garder Form même si validation simple
|
|
||||||
key: _formKey,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Présentation et Conditions',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
'Rédigez un court message à destination du gestionnaire (facultatif) :',
|
|
||||||
style: TextStyle(fontSize: 16, color: Colors.black87),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
CustomDecoratedTextField(
|
|
||||||
controller: _presentationController,
|
|
||||||
hintText: 'Ex: Disponible immédiatement, formation premiers secours...',
|
|
||||||
maxLines: 6,
|
|
||||||
// style: cardColor.textFieldStyle, // Utiliser style par défaut ou adapter
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
CheckboxListTile(
|
|
||||||
title: const Text('J\'ai lu et j\'accepte les Conditions Générales d\'Utilisation et la Politique de confidentialité de P\'titsPas.', style: TextStyle(fontSize: 14)),
|
|
||||||
subtitle: Text('Vous devez accepter pour continuer.', style: TextStyle(color: Colors.black54.withOpacity(0.7))),
|
|
||||||
value: _cguAccepted,
|
|
||||||
onChanged: (bool? value) {
|
|
||||||
setState(() { _cguAccepted = value ?? false; });
|
|
||||||
},
|
|
||||||
controlAffinity: ListTileControlAffinity.leading,
|
|
||||||
dense: true,
|
|
||||||
activeColor: Theme.of(context).primaryColor,
|
|
||||||
),
|
|
||||||
// TODO: Ajouter lien vers CGU
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// 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('/nanny-register-step2');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Précédent',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Chevron Droit (Suivant)
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: _submitForm,
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,251 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
// import 'package:p_tits_pas/utils/resources/card_color_horizontal.dart'; // Supprimé car incorrect
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
import 'package:p_tits_pas/models/card_assets.dart';
|
|
||||||
import '../../../models/nanny_registration_data.dart';
|
|
||||||
// import '../../../widgets/registration_scaffold.dart'; // Widget inexistant
|
|
||||||
// import '../../../widgets/recap_card.dart'; // Widget inexistant
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
|
|
||||||
class NannyRegisterStep4Screen extends StatelessWidget {
|
|
||||||
const NannyRegisterStep4Screen({super.key});
|
|
||||||
|
|
||||||
void _submitRegistration(BuildContext context) {
|
|
||||||
final nannyData = Provider.of<NannyRegistrationData>(context, listen: false);
|
|
||||||
print('Submitting Nanny Registration: ${nannyData.toString()}');
|
|
||||||
// TODO: Implement actual submission logic (e.g., API call)
|
|
||||||
context.go('/nanny-register-confirmation');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final nannyData = Provider.of<NannyRegistrationData>(context);
|
|
||||||
final dateFormat = DateFormat('dd/MM/yyyy');
|
|
||||||
final size = MediaQuery.of(context).size;
|
|
||||||
final bool canSubmit = nannyData.isRegistrationComplete; // Check completeness
|
|
||||||
|
|
||||||
return Scaffold( // Main scaffold to contain the stack
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
// Background image
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/images/paper2.png', // Assurez-vous que le chemin est correct
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Content centered
|
|
||||||
Center(
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
maxWidth: size.width * 0.8, // Adjust width as needed
|
|
||||||
maxHeight: size.height * 0.85, // Adjust height as needed
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(CardColorHorizontal.blue.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 40.0, bottom: 10.0),
|
|
||||||
child: Text(
|
|
||||||
'Récapitulatif',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 15.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Veuillez vérifier attentivement les informations avant de soumettre.',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Colors.white70),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// --- Identity Card (Using standard Card for grouping) ---
|
|
||||||
Card(
|
|
||||||
elevation: 2.0,
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(15.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_buildCardTitle(context, 'Informations personnelles', '/nanny-register-step1'),
|
|
||||||
const Divider(),
|
|
||||||
if (nannyData.photoPath != null && nannyData.photoPath!.isNotEmpty)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
|
||||||
child: Center(
|
|
||||||
child: CircleAvatar(
|
|
||||||
radius: 40,
|
|
||||||
backgroundImage: FileImage(File(nannyData.photoPath!)),
|
|
||||||
onBackgroundImageError: (exception, stackTrace) {
|
|
||||||
print("Erreur chargement image: $exception");
|
|
||||||
// Optionnel: afficher un placeholder ou icône d'erreur
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_buildRecapRow('Nom:', '${nannyData.firstName} ${nannyData.lastName}'),
|
|
||||||
_buildRecapRow('Adresse:', '${nannyData.streetAddress}${nannyData.postalCode.isNotEmpty ? '\n${nannyData.postalCode}' : ''}${nannyData.city.isNotEmpty ? ' ${nannyData.city}' : ''}'.trim()),
|
|
||||||
_buildRecapRow('Téléphone:', nannyData.phone),
|
|
||||||
_buildRecapRow('Email:', nannyData.email),
|
|
||||||
_buildRecapRow('Consentement Photo:', nannyData.photoConsent ? 'Oui' : 'Non'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// --- Professional Info Card (Using standard Card) ---
|
|
||||||
Card(
|
|
||||||
elevation: 2.0,
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(15.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_buildCardTitle(context, 'Informations professionnelles', '/nanny-register-step2'),
|
|
||||||
const Divider(),
|
|
||||||
_buildRecapRow('Date de naissance:', nannyData.dateOfBirth != null ? dateFormat.format(nannyData.dateOfBirth!) : 'Non renseigné'),
|
|
||||||
_buildRecapRow('Lieu de naissance:', '${nannyData.birthCity}, ${nannyData.birthCountry}'.isNotEmpty ? '${nannyData.birthCity}, ${nannyData.birthCountry}' : 'Non renseigné'),
|
|
||||||
_buildRecapRow('N° Sécurité Sociale:', nannyData.nir.isNotEmpty ? nannyData.nir : 'Non renseigné'), // TODO: Mask this?
|
|
||||||
_buildRecapRow('N° Agrément:', nannyData.agrementNumber.isNotEmpty ? nannyData.agrementNumber : 'Non renseigné'),
|
|
||||||
_buildRecapRow('Capacité d\'accueil:', nannyData.capacity?.toString() ?? 'Non renseigné'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// --- Presentation Card (Using standard Card) ---
|
|
||||||
Card(
|
|
||||||
elevation: 2.0,
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(15.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_buildCardTitle(context, 'Présentation & CGU', '/nanny-register-step3'),
|
|
||||||
const Divider(),
|
|
||||||
const Text('Votre présentation (facultatif) :', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
||||||
const SizedBox(height: 5),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
width: double.infinity,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey[100],
|
|
||||||
borderRadius: BorderRadius.circular(5),
|
|
||||||
border: Border.all(color: Colors.grey[300]!)
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
nannyData.presentationText.isNotEmpty
|
|
||||||
? nannyData.presentationText
|
|
||||||
: 'Aucune présentation rédigée.',
|
|
||||||
style: TextStyle(
|
|
||||||
color: nannyData.presentationText.isNotEmpty ? Colors.black87 : Colors.grey,
|
|
||||||
fontStyle: nannyData.presentationText.isNotEmpty ? FontStyle.normal : FontStyle.italic
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
_buildRecapRow('CGU Acceptées:', nannyData.cguAccepted ? 'Oui' : 'Non'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!canSubmit) // Show warning if incomplete
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 15.0, bottom: 5.0), // Add some space
|
|
||||||
child: Text(
|
|
||||||
'Veuillez compléter toutes les étapes requises et accepter les CGU pour pouvoir soumettre.',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.error, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Navigation buttons
|
|
||||||
Positioned(
|
|
||||||
top: size.height / 2 - 20, // Centré verticalement
|
|
||||||
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: () => context.go('/nanny-register-step3'),
|
|
||||||
tooltip: 'Précédent',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: size.height / 2 - 20, // Centré verticalement
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: canSubmit ? () => _submitRegistration(context) : null,
|
|
||||||
tooltip: 'Soumettre',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to build title row with edit button
|
|
||||||
Widget _buildCardTitle(BuildContext context, String title, String editRoute) {
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.edit, size: 20),
|
|
||||||
onPressed: () => context.go(editRoute),
|
|
||||||
tooltip: 'Modifier',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to build data row
|
|
||||||
Widget _buildRecapRow(String label, String value) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('$label ', style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
||||||
Expanded(child: Text(value)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,209 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
|
||||||
import '../../../models/parent_user_registration_data.dart'; // Import du modèle de données
|
|
||||||
import '../../../utils/data_generator.dart'; // Import du générateur de données
|
|
||||||
import '../../../widgets/custom_app_text_field.dart'; // Import du widget CustomAppTextField
|
|
||||||
import '../../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
|
|
||||||
class ParentRegisterStep1Screen extends StatefulWidget {
|
|
||||||
const ParentRegisterStep1Screen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ParentRegisterStep1Screen> createState() => _ParentRegisterStep1ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ParentRegisterStep1ScreenState extends State<ParentRegisterStep1Screen> {
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
late UserRegistrationData _registrationData;
|
|
||||||
|
|
||||||
// Contrôleurs pour les champs (restauration CP et Ville)
|
|
||||||
final _lastNameController = TextEditingController();
|
|
||||||
final _firstNameController = TextEditingController();
|
|
||||||
final _phoneController = TextEditingController();
|
|
||||||
final _emailController = TextEditingController();
|
|
||||||
final _addressController = TextEditingController();
|
|
||||||
final _postalCodeController = TextEditingController();
|
|
||||||
final _cityController = TextEditingController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_registrationData = UserRegistrationData();
|
|
||||||
_generateAndFillData();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _generateAndFillData() {
|
|
||||||
final String genFirstName = DataGenerator.firstName();
|
|
||||||
final String genLastName = DataGenerator.lastName();
|
|
||||||
|
|
||||||
// Utilisation des méthodes publiques de DataGenerator
|
|
||||||
_addressController.text = DataGenerator.address();
|
|
||||||
_postalCodeController.text = DataGenerator.postalCode();
|
|
||||||
_cityController.text = DataGenerator.city();
|
|
||||||
|
|
||||||
_firstNameController.text = genFirstName;
|
|
||||||
_lastNameController.text = genLastName;
|
|
||||||
_phoneController.text = DataGenerator.phone();
|
|
||||||
_emailController.text = DataGenerator.email(genFirstName, genLastName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_lastNameController.dispose();
|
|
||||||
_firstNameController.dispose();
|
|
||||||
_phoneController.dispose();
|
|
||||||
_emailController.dispose();
|
|
||||||
_addressController.dispose();
|
|
||||||
_postalCodeController.dispose();
|
|
||||||
_cityController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
// Fond papier
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/images/paper2.png',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
repeat: ImageRepeat.repeat,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Contenu centré
|
|
||||||
Center(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
// Indicateur d'étape
|
|
||||||
Text(
|
|
||||||
'Étape 1/6',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
// Texte d'instruction
|
|
||||||
Text(
|
|
||||||
'Informations du Parent Principal',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
|
|
||||||
// Carte jaune contenant le formulaire
|
|
||||||
Container(
|
|
||||||
width: screenSize.width * 0.6,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50),
|
|
||||||
constraints: const BoxConstraints(minHeight: 570),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(CardColorHorizontal.peach.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _lastNameController, labelText: 'Nom', hintText: 'Votre nom de famille', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _firstNameController, labelText: 'Prénom', hintText: 'Votre prénom', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _phoneController, labelText: 'Téléphone', keyboardType: TextInputType.phone, hintText: 'Votre numéro de téléphone', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _emailController, labelText: 'Email', keyboardType: TextInputType.emailAddress, hintText: 'Votre adresse e-mail', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _addressController,
|
|
||||||
labelText: 'Adresse (N° et Rue)',
|
|
||||||
hintText: 'Numéro et nom de votre rue',
|
|
||||||
style: CustomAppTextFieldStyle.beige,
|
|
||||||
fieldWidth: double.infinity,
|
|
||||||
labelFontSize: 22.0,
|
|
||||||
inputFontSize: 20.0,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 1, child: CustomAppTextField(controller: _postalCodeController, labelText: 'Code Postal', keyboardType: TextInputType.number, hintText: 'Code postal', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(flex: 4, child: CustomAppTextField(controller: _cityController, labelText: 'Ville', hintText: 'Votre ville', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Chevron de navigation gauche (Retour)
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20, // Centré verticalement
|
|
||||||
left: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Transform(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
transform: Matrix4.rotationY(math.pi), // Inverse horizontalement
|
|
||||||
child: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
),
|
|
||||||
onPressed: () => Navigator.pop(context), // Retour à l'écran de choix
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Chevron de navigation droit (Suivant)
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20, // Centré verticalement
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: () {
|
|
||||||
if (_formKey.currentState?.validate() ?? false) {
|
|
||||||
_registrationData.updateParent1(
|
|
||||||
ParentData(
|
|
||||||
firstName: _firstNameController.text,
|
|
||||||
lastName: _lastNameController.text,
|
|
||||||
address: _addressController.text,
|
|
||||||
postalCode: _postalCodeController.text,
|
|
||||||
city: _cityController.text,
|
|
||||||
phone: _phoneController.text,
|
|
||||||
email: _emailController.text,
|
|
||||||
password: '', // Pas de mot de passe à cette étape
|
|
||||||
)
|
|
||||||
);
|
|
||||||
Navigator.pushNamed(context, '/parent-register/step2', arguments: _registrationData);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,244 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
|
||||||
import '../../../models/parent_user_registration_data.dart'; // Import du modèle
|
|
||||||
import '../../../utils/data_generator.dart'; // Import du générateur
|
|
||||||
import '../../../widgets/custom_app_text_field.dart'; // Import du widget
|
|
||||||
import '../../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
|
|
||||||
class ParentRegisterStep2Screen extends StatefulWidget {
|
|
||||||
final UserRegistrationData registrationData; // Accepte les données de l'étape 1
|
|
||||||
|
|
||||||
const ParentRegisterStep2Screen({super.key, required this.registrationData});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ParentRegisterStep2Screen> createState() => _ParentRegisterStep2ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ParentRegisterStep2ScreenState extends State<ParentRegisterStep2Screen> {
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
late UserRegistrationData _registrationData; // Copie locale pour modification
|
|
||||||
|
|
||||||
bool _addParent2 = true; // Pour le test, on ajoute toujours le parent 2
|
|
||||||
bool _sameAddressAsParent1 = false; // Peut être généré aléatoirement aussi
|
|
||||||
|
|
||||||
// Contrôleurs pour les champs du parent 2
|
|
||||||
final _lastNameController = TextEditingController();
|
|
||||||
final _firstNameController = TextEditingController();
|
|
||||||
final _phoneController = TextEditingController();
|
|
||||||
final _emailController = TextEditingController();
|
|
||||||
final _addressController = TextEditingController();
|
|
||||||
final _postalCodeController = TextEditingController();
|
|
||||||
final _cityController = TextEditingController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_registrationData = widget.registrationData; // Récupère les données de l'étape 1
|
|
||||||
if (_addParent2) {
|
|
||||||
_generateAndFillParent2Data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _generateAndFillParent2Data() {
|
|
||||||
final String genFirstName = DataGenerator.firstName();
|
|
||||||
final String genLastName = DataGenerator.lastName();
|
|
||||||
_firstNameController.text = genFirstName;
|
|
||||||
_lastNameController.text = genLastName;
|
|
||||||
_phoneController.text = DataGenerator.phone();
|
|
||||||
_emailController.text = DataGenerator.email(genFirstName, genLastName);
|
|
||||||
|
|
||||||
_sameAddressAsParent1 = DataGenerator.boolean();
|
|
||||||
if (!_sameAddressAsParent1) {
|
|
||||||
_addressController.text = DataGenerator.address();
|
|
||||||
_postalCodeController.text = DataGenerator.postalCode();
|
|
||||||
_cityController.text = DataGenerator.city();
|
|
||||||
} else {
|
|
||||||
_addressController.clear();
|
|
||||||
_postalCodeController.clear();
|
|
||||||
_cityController.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_lastNameController.dispose();
|
|
||||||
_firstNameController.dispose();
|
|
||||||
_phoneController.dispose();
|
|
||||||
_emailController.dispose();
|
|
||||||
_addressController.dispose();
|
|
||||||
_postalCodeController.dispose();
|
|
||||||
_cityController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _parent2FieldsEnabled => _addParent2;
|
|
||||||
bool get _addressFieldsEnabled => _addParent2 && !_sameAddressAsParent1;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text('Étape 2/6', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text(
|
|
||||||
'Informations du Deuxième Parent (Optionnel)',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Container(
|
|
||||||
width: screenSize.width * 0.6,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 50),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(image: AssetImage(CardColorHorizontal.blue.path), fit: BoxFit.fill),
|
|
||||||
),
|
|
||||||
child: Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 12,
|
|
||||||
child: Row(children: [
|
|
||||||
const Icon(Icons.person_add_alt_1, size: 20), const SizedBox(width: 8),
|
|
||||||
Flexible(child: Text('Ajouter Parent 2 ?', style: GoogleFonts.merienda(fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis)),
|
|
||||||
const Spacer(),
|
|
||||||
Switch(value: _addParent2, onChanged: (val) => setState(() {
|
|
||||||
_addParent2 = val ?? false;
|
|
||||||
if (_addParent2) _generateAndFillParent2Data(); else _clearParent2Fields();
|
|
||||||
}), activeColor: Theme.of(context).primaryColor),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(
|
|
||||||
flex: 12,
|
|
||||||
child: Row(children: [
|
|
||||||
Icon(Icons.home_work_outlined, size: 20, color: _addParent2 ? null : Colors.grey),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Flexible(child: Text('Même Adresse ?', style: GoogleFonts.merienda(color: _addParent2 ? null : Colors.grey), overflow: TextOverflow.ellipsis)),
|
|
||||||
const Spacer(),
|
|
||||||
Switch(value: _sameAddressAsParent1, onChanged: _addParent2 ? (val) => setState(() {
|
|
||||||
_sameAddressAsParent1 = val ?? false;
|
|
||||||
if (_sameAddressAsParent1) {
|
|
||||||
_addressController.text = _registrationData.parent1.address;
|
|
||||||
_postalCodeController.text = _registrationData.parent1.postalCode;
|
|
||||||
_cityController.text = _registrationData.parent1.city;
|
|
||||||
} else {
|
|
||||||
_addressController.text = DataGenerator.address();
|
|
||||||
_postalCodeController.text = DataGenerator.postalCode();
|
|
||||||
_cityController.text = DataGenerator.city();
|
|
||||||
}
|
|
||||||
}) : null, activeColor: Theme.of(context).primaryColor),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _lastNameController, labelText: 'Nom', hintText: 'Nom du parent 2', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _firstNameController, labelText: 'Prénom', hintText: 'Prénom du parent 2', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _phoneController, labelText: 'Téléphone', keyboardType: TextInputType.phone, hintText: 'Son téléphone', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _emailController, labelText: 'Email', keyboardType: TextInputType.emailAddress, hintText: 'Son email', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
CustomAppTextField(controller: _addressController, labelText: 'Adresse (N° et Rue)', hintText: 'Son numéro et nom de rue', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 1, child: CustomAppTextField(controller: _postalCodeController, labelText: 'Code Postal', keyboardType: TextInputType.number, hintText: 'Son code postal', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(flex: 4, child: CustomAppTextField(controller: _cityController, labelText: 'Ville', hintText: 'Sa ville', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, labelFontSize: 22.0, inputFontSize: 20.0)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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: () => Navigator.pop(context),
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: () {
|
|
||||||
if (!_addParent2 || (_formKey.currentState?.validate() ?? false)) {
|
|
||||||
if (_addParent2) {
|
|
||||||
_registrationData.updateParent2(
|
|
||||||
ParentData(
|
|
||||||
firstName: _firstNameController.text,
|
|
||||||
lastName: _lastNameController.text,
|
|
||||||
address: _sameAddressAsParent1 ? _registrationData.parent1.address : _addressController.text,
|
|
||||||
postalCode: _sameAddressAsParent1 ? _registrationData.parent1.postalCode : _postalCodeController.text,
|
|
||||||
city: _sameAddressAsParent1 ? _registrationData.parent1.city : _cityController.text,
|
|
||||||
phone: _phoneController.text,
|
|
||||||
email: _emailController.text,
|
|
||||||
password: '', // Pas de mot de passe à cette étape
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_registrationData.updateParent2(null);
|
|
||||||
}
|
|
||||||
Navigator.pushNamed(context, '/parent-register/step3', arguments: _registrationData);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _clearParent2Fields() {
|
|
||||||
_formKey.currentState?.reset();
|
|
||||||
_lastNameController.clear();
|
|
||||||
_firstNameController.clear();
|
|
||||||
_phoneController.clear();
|
|
||||||
_emailController.clear();
|
|
||||||
_addressController.clear();
|
|
||||||
_postalCodeController.clear();
|
|
||||||
_cityController.clear();
|
|
||||||
_sameAddressAsParent1 = false;
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,487 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
|
||||||
import 'package:flutter/gestures.dart'; // Pour PointerDeviceKind
|
|
||||||
import '../../../widgets/hover_relief_widget.dart'; // Import du nouveau widget
|
|
||||||
import 'package:image_picker/image_picker.dart';
|
|
||||||
// import 'package:image_cropper/image_cropper.dart'; // Supprimé
|
|
||||||
import 'dart:io' show File, Platform; // Ajout de Platform
|
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb; // Import pour kIsWeb
|
|
||||||
import '../../../widgets/custom_app_text_field.dart'; // Import du nouveau widget TextField
|
|
||||||
import '../../../widgets/app_custom_checkbox.dart'; // Import du nouveau widget Checkbox
|
|
||||||
import '../../../models/parent_user_registration_data.dart'; // Import du modèle de données
|
|
||||||
import '../../../utils/data_generator.dart'; // Import du générateur
|
|
||||||
import '../../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
|
|
||||||
// La classe _ChildFormData est supprimée car on utilise ChildData du modèle
|
|
||||||
|
|
||||||
class ParentRegisterStep3Screen extends StatefulWidget {
|
|
||||||
final UserRegistrationData registrationData; // Accepte les données
|
|
||||||
|
|
||||||
const ParentRegisterStep3Screen({super.key, required this.registrationData});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ParentRegisterStep3Screen> createState() => _ParentRegisterStep3ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
|
|
||||||
late UserRegistrationData _registrationData; // Stocke l'état complet
|
|
||||||
final ScrollController _scrollController = ScrollController(); // Pour le défilement horizontal
|
|
||||||
bool _isScrollable = false;
|
|
||||||
bool _showLeftFade = false;
|
|
||||||
bool _showRightFade = false;
|
|
||||||
static const double _fadeExtent = 0.05; // Pourcentage de fondu
|
|
||||||
|
|
||||||
// Liste ordonnée des couleurs de cartes pour les enfants
|
|
||||||
static const List<CardColorVertical> _childCardColors = [
|
|
||||||
CardColorVertical.lavender, // Premier enfant toujours lavande
|
|
||||||
CardColorVertical.pink,
|
|
||||||
CardColorVertical.peach,
|
|
||||||
CardColorVertical.lime,
|
|
||||||
CardColorVertical.red,
|
|
||||||
CardColorVertical.green,
|
|
||||||
CardColorVertical.blue,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Garder une trace des couleurs déjà utilisées
|
|
||||||
final Set<CardColorVertical> _usedColors = {};
|
|
||||||
|
|
||||||
// Utilisation de GlobalKey pour les cartes enfants si validation complexe future
|
|
||||||
// Map<int, GlobalKey<FormState>> _childFormKeys = {};
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_registrationData = widget.registrationData;
|
|
||||||
// Initialiser les couleurs utilisées avec les enfants existants
|
|
||||||
for (var child in _registrationData.children) {
|
|
||||||
_usedColors.add(child.cardColor);
|
|
||||||
}
|
|
||||||
// S'il n'y a pas d'enfant, en ajouter un automatiquement avec des données générées
|
|
||||||
if (_registrationData.children.isEmpty) {
|
|
||||||
_addChild();
|
|
||||||
}
|
|
||||||
_scrollController.addListener(_scrollListener);
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_scrollController.removeListener(_scrollListener);
|
|
||||||
_scrollController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _scrollListener() {
|
|
||||||
if (!_scrollController.hasClients) return;
|
|
||||||
final position = _scrollController.position;
|
|
||||||
final newIsScrollable = position.maxScrollExtent > 0.0;
|
|
||||||
final newShowLeftFade = newIsScrollable && position.pixels > (position.viewportDimension * _fadeExtent / 2);
|
|
||||||
final newShowRightFade = newIsScrollable && position.pixels < (position.maxScrollExtent - (position.viewportDimension * _fadeExtent / 2));
|
|
||||||
if (newIsScrollable != _isScrollable || newShowLeftFade != _showLeftFade || newShowRightFade != _showRightFade) {
|
|
||||||
setState(() {
|
|
||||||
_isScrollable = newIsScrollable;
|
|
||||||
_showLeftFade = newShowLeftFade;
|
|
||||||
_showRightFade = newShowRightFade;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addChild() {
|
|
||||||
setState(() {
|
|
||||||
bool isUnborn = DataGenerator.boolean();
|
|
||||||
|
|
||||||
// Trouver la première couleur non utilisée
|
|
||||||
CardColorVertical cardColor = _childCardColors.firstWhere(
|
|
||||||
(color) => !_usedColors.contains(color),
|
|
||||||
orElse: () => _childCardColors[0], // Fallback sur la première couleur si toutes sont utilisées
|
|
||||||
);
|
|
||||||
|
|
||||||
final newChild = ChildData(
|
|
||||||
lastName: _registrationData.parent1.lastName,
|
|
||||||
firstName: DataGenerator.firstName(),
|
|
||||||
dob: DataGenerator.dob(isUnborn: isUnborn),
|
|
||||||
isUnbornChild: isUnborn,
|
|
||||||
photoConsent: DataGenerator.boolean(),
|
|
||||||
multipleBirth: DataGenerator.boolean(),
|
|
||||||
cardColor: cardColor,
|
|
||||||
);
|
|
||||||
_registrationData.addChild(newChild);
|
|
||||||
_usedColors.add(cardColor);
|
|
||||||
});
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
_scrollListener();
|
|
||||||
if (_scrollController.hasClients && _scrollController.position.maxScrollExtent > 0.0) {
|
|
||||||
_scrollController.animateTo(_scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _removeChild(int index) {
|
|
||||||
if (_registrationData.children.length > 1 && index >= 0 && index < _registrationData.children.length) {
|
|
||||||
setState(() {
|
|
||||||
// Ne pas retirer la couleur de _usedColors pour éviter sa réutilisation
|
|
||||||
_registrationData.children.removeAt(index);
|
|
||||||
});
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollListener());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _pickImage(int childIndex) async {
|
|
||||||
final ImagePicker picker = ImagePicker();
|
|
||||||
try {
|
|
||||||
final XFile? pickedFile = await picker.pickImage(
|
|
||||||
source: ImageSource.gallery, imageQuality: 70, maxWidth: 1024, maxHeight: 1024);
|
|
||||||
if (pickedFile != null) {
|
|
||||||
setState(() {
|
|
||||||
if (childIndex < _registrationData.children.length) {
|
|
||||||
_registrationData.children[childIndex].imageFile = File(pickedFile.path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) { print("Erreur image: $e"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _selectDate(BuildContext context, int childIndex) async {
|
|
||||||
final ChildData currentChild = _registrationData.children[childIndex];
|
|
||||||
final DateTime now = DateTime.now();
|
|
||||||
DateTime initialDatePickerDate = now;
|
|
||||||
DateTime firstDatePickerDate = DateTime(1980); DateTime lastDatePickerDate = now;
|
|
||||||
|
|
||||||
if (currentChild.isUnbornChild) {
|
|
||||||
firstDatePickerDate = now; lastDatePickerDate = now.add(const Duration(days: 300));
|
|
||||||
if (currentChild.dob.isNotEmpty) {
|
|
||||||
try {
|
|
||||||
List<String> parts = currentChild.dob.split('/');
|
|
||||||
DateTime? parsedDate = DateTime.tryParse("${parts[2]}-${parts[1].padLeft(2, '0')}-${parts[0].padLeft(2, '0')}");
|
|
||||||
if (parsedDate != null && !parsedDate.isBefore(firstDatePickerDate) && !parsedDate.isAfter(lastDatePickerDate)) {
|
|
||||||
initialDatePickerDate = parsedDate;
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (currentChild.dob.isNotEmpty) {
|
|
||||||
try {
|
|
||||||
List<String> parts = currentChild.dob.split('/');
|
|
||||||
DateTime? parsedDate = DateTime.tryParse("${parts[2]}-${parts[1].padLeft(2, '0')}-${parts[0].padLeft(2, '0')}");
|
|
||||||
if (parsedDate != null && !parsedDate.isBefore(firstDatePickerDate) && !parsedDate.isAfter(lastDatePickerDate)) {
|
|
||||||
initialDatePickerDate = parsedDate;
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final DateTime? picked = await showDatePicker(
|
|
||||||
context: context, initialDate: initialDatePickerDate, firstDate: firstDatePickerDate,
|
|
||||||
lastDate: lastDatePickerDate, locale: const Locale('fr', 'FR'),
|
|
||||||
);
|
|
||||||
if (picked != null) {
|
|
||||||
setState(() {
|
|
||||||
currentChild.dob = "${picked.day.toString().padLeft(2, '0')}/${picked.month.toString().padLeft(2, '0')}/${picked.year}";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
return Scaffold(
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text('Étape 3/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text(
|
|
||||||
'Informations Enfants',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 150.0),
|
|
||||||
child: SizedBox(
|
|
||||||
height: 684.0,
|
|
||||||
child: ShaderMask(
|
|
||||||
shaderCallback: (Rect bounds) {
|
|
||||||
final Color leftFade = (_isScrollable && _showLeftFade) ? Colors.transparent : Colors.black;
|
|
||||||
final Color rightFade = (_isScrollable && _showRightFade) ? Colors.transparent : Colors.black;
|
|
||||||
if (!_isScrollable) { return LinearGradient(colors: const <Color>[Colors.black, Colors.black, Colors.black, Colors.black], stops: const [0.0, _fadeExtent, 1.0 - _fadeExtent, 1.0],).createShader(bounds); }
|
|
||||||
return LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: <Color>[ leftFade, Colors.black, Colors.black, rightFade ], stops: const [0.0, _fadeExtent, 1.0 - _fadeExtent, 1.0], ).createShader(bounds);
|
|
||||||
},
|
|
||||||
blendMode: BlendMode.dstIn,
|
|
||||||
child: Scrollbar(
|
|
||||||
controller: _scrollController,
|
|
||||||
thumbVisibility: true,
|
|
||||||
child: ListView.builder(
|
|
||||||
controller: _scrollController,
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
|
||||||
itemCount: _registrationData.children.length + 1,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index < _registrationData.children.length) {
|
|
||||||
// Carte Enfant
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 20.0),
|
|
||||||
child: _ChildCardWidget(
|
|
||||||
key: ValueKey(_registrationData.children[index].hashCode), // Utiliser une clé basée sur les données
|
|
||||||
childData: _registrationData.children[index],
|
|
||||||
childIndex: index,
|
|
||||||
onPickImage: () => _pickImage(index),
|
|
||||||
onDateSelect: () => _selectDate(context, index),
|
|
||||||
onFirstNameChanged: (value) => setState(() => _registrationData.children[index].firstName = value),
|
|
||||||
onLastNameChanged: (value) => setState(() => _registrationData.children[index].lastName = value),
|
|
||||||
onTogglePhotoConsent: (newValue) => setState(() => _registrationData.children[index].photoConsent = newValue),
|
|
||||||
onToggleMultipleBirth: (newValue) => setState(() => _registrationData.children[index].multipleBirth = newValue),
|
|
||||||
onToggleIsUnborn: (newValue) => setState(() {
|
|
||||||
_registrationData.children[index].isUnbornChild = newValue;
|
|
||||||
// Générer une nouvelle date si on change le statut
|
|
||||||
_registrationData.children[index].dob = DataGenerator.dob(isUnborn: newValue);
|
|
||||||
}),
|
|
||||||
onRemove: () => _removeChild(index),
|
|
||||||
canBeRemoved: _registrationData.children.length > 1,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Bouton Ajouter
|
|
||||||
return Center(
|
|
||||||
child: HoverReliefWidget(
|
|
||||||
onPressed: _addChild,
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
child: Image.asset('assets/images/plus.png', height: 80, width: 80),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Chevrons de navigation
|
|
||||||
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: () => Navigator.pop(context),
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: () {
|
|
||||||
// TODO: Validation (si nécessaire)
|
|
||||||
Navigator.pushNamed(context, '/parent-register/step4', arguments: _registrationData);
|
|
||||||
},
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Widget pour la carte enfant (adapté pour prendre ChildData et des callbacks)
|
|
||||||
class _ChildCardWidget extends StatefulWidget { // Transformé en StatefulWidget pour gérer les contrôleurs internes
|
|
||||||
final ChildData childData;
|
|
||||||
final int childIndex;
|
|
||||||
final VoidCallback onPickImage;
|
|
||||||
final VoidCallback onDateSelect;
|
|
||||||
final ValueChanged<String> onFirstNameChanged;
|
|
||||||
final ValueChanged<String> onLastNameChanged;
|
|
||||||
final ValueChanged<bool> onTogglePhotoConsent;
|
|
||||||
final ValueChanged<bool> onToggleMultipleBirth;
|
|
||||||
final ValueChanged<bool> onToggleIsUnborn;
|
|
||||||
final VoidCallback onRemove;
|
|
||||||
final bool canBeRemoved;
|
|
||||||
|
|
||||||
const _ChildCardWidget({
|
|
||||||
required Key key,
|
|
||||||
required this.childData,
|
|
||||||
required this.childIndex,
|
|
||||||
required this.onPickImage,
|
|
||||||
required this.onDateSelect,
|
|
||||||
required this.onFirstNameChanged,
|
|
||||||
required this.onLastNameChanged,
|
|
||||||
required this.onTogglePhotoConsent,
|
|
||||||
required this.onToggleMultipleBirth,
|
|
||||||
required this.onToggleIsUnborn,
|
|
||||||
required this.onRemove,
|
|
||||||
required this.canBeRemoved,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_ChildCardWidget> createState() => _ChildCardWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ChildCardWidgetState extends State<_ChildCardWidget> {
|
|
||||||
late TextEditingController _firstNameController;
|
|
||||||
late TextEditingController _lastNameController;
|
|
||||||
late TextEditingController _dobController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// Initialiser les contrôleurs avec les données du widget
|
|
||||||
_firstNameController = TextEditingController(text: widget.childData.firstName);
|
|
||||||
_lastNameController = TextEditingController(text: widget.childData.lastName);
|
|
||||||
_dobController = TextEditingController(text: widget.childData.dob);
|
|
||||||
|
|
||||||
// Ajouter des listeners pour mettre à jour les données sources via les callbacks
|
|
||||||
_firstNameController.addListener(() => widget.onFirstNameChanged(_firstNameController.text));
|
|
||||||
_lastNameController.addListener(() => widget.onLastNameChanged(_lastNameController.text));
|
|
||||||
// Pour dob, la mise à jour se fait via _selectDate, pas besoin de listener ici
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(covariant _ChildCardWidget oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
// Mettre à jour les contrôleurs si les données externes changent
|
|
||||||
// (peut arriver si on recharge l'état global)
|
|
||||||
if (widget.childData.firstName != _firstNameController.text) {
|
|
||||||
_firstNameController.text = widget.childData.firstName;
|
|
||||||
}
|
|
||||||
if (widget.childData.lastName != _lastNameController.text) {
|
|
||||||
_lastNameController.text = widget.childData.lastName;
|
|
||||||
}
|
|
||||||
if (widget.childData.dob != _dobController.text) {
|
|
||||||
_dobController.text = widget.childData.dob;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_firstNameController.dispose();
|
|
||||||
_lastNameController.dispose();
|
|
||||||
_dobController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final File? currentChildImage = widget.childData.imageFile;
|
|
||||||
// Utiliser la couleur de la carte de childData pour l'ombre si besoin, ou directement pour le fond
|
|
||||||
final Color baseCardColorForShadow = widget.childData.cardColor == CardColorVertical.lavender
|
|
||||||
? Colors.purple.shade200
|
|
||||||
: (widget.childData.cardColor == CardColorVertical.pink ? Colors.pink.shade200 : Colors.grey.shade200); // Placeholder pour autres couleurs
|
|
||||||
final Color initialPhotoShadow = baseCardColorForShadow.withAlpha(90);
|
|
||||||
final Color hoverPhotoShadow = baseCardColorForShadow.withAlpha(130);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: 345.0 * 1.1, // 379.5
|
|
||||||
height: 570.0 * 1.2, // 684.0
|
|
||||||
padding: const EdgeInsets.all(22.0 * 1.1), // 24.2
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(image: AssetImage(widget.childData.cardColor.path), fit: BoxFit.cover),
|
|
||||||
borderRadius: BorderRadius.circular(20 * 1.1), // 22
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
HoverReliefWidget(
|
|
||||||
onPressed: widget.onPickImage,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
initialShadowColor: initialPhotoShadow,
|
|
||||||
hoverShadowColor: hoverPhotoShadow,
|
|
||||||
child: SizedBox(
|
|
||||||
height: 200.0,
|
|
||||||
width: 200.0,
|
|
||||||
child: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(5.0 * 1.1), // 5.5
|
|
||||||
child: currentChildImage != null
|
|
||||||
? ClipRRect(borderRadius: BorderRadius.circular(10 * 1.1), child: kIsWeb ? Image.network(currentChildImage.path, fit: BoxFit.cover) : Image.file(currentChildImage, fit: BoxFit.cover))
|
|
||||||
: Image.asset('assets/images/photo.png', fit: BoxFit.contain),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12.0 * 1.1), // Augmenté pour plus d'espace après la photo
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text('Enfant à naître ?', style: GoogleFonts.merienda(fontSize: 16 * 1.1, fontWeight: FontWeight.w600)),
|
|
||||||
Switch(value: widget.childData.isUnbornChild, onChanged: widget.onToggleIsUnborn, activeColor: Theme.of(context).primaryColor),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 9.0 * 1.1), // 9.9
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _firstNameController,
|
|
||||||
labelText: 'Prénom',
|
|
||||||
hintText: 'Facultatif si à naître',
|
|
||||||
isRequired: !widget.childData.isUnbornChild,
|
|
||||||
fieldHeight: 55.0 * 1.1, // 60.5
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6.0 * 1.1), // 6.6
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _lastNameController,
|
|
||||||
labelText: 'Nom',
|
|
||||||
hintText: 'Nom de l\'enfant',
|
|
||||||
enabled: true,
|
|
||||||
fieldHeight: 55.0 * 1.1, // 60.5
|
|
||||||
),
|
|
||||||
const SizedBox(height: 9.0 * 1.1), // 9.9
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _dobController,
|
|
||||||
labelText: widget.childData.isUnbornChild ? 'Date prévisionnelle de naissance' : 'Date de naissance',
|
|
||||||
hintText: 'JJ/MM/AAAA',
|
|
||||||
readOnly: true,
|
|
||||||
onTap: widget.onDateSelect,
|
|
||||||
suffixIcon: Icons.calendar_today,
|
|
||||||
fieldHeight: 55.0 * 1.1, // 60.5
|
|
||||||
),
|
|
||||||
const SizedBox(height: 11.0 * 1.1), // 12.1
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
AppCustomCheckbox(
|
|
||||||
label: 'Consentement photo',
|
|
||||||
value: widget.childData.photoConsent,
|
|
||||||
onChanged: widget.onTogglePhotoConsent,
|
|
||||||
checkboxSize: 22.0 * 1.1, // 24.2
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6.0 * 1.1), // 6.6
|
|
||||||
AppCustomCheckbox(
|
|
||||||
label: 'Naissance multiple',
|
|
||||||
value: widget.childData.multipleBirth,
|
|
||||||
onChanged: widget.onToggleMultipleBirth,
|
|
||||||
checkboxSize: 22.0 * 1.1, // 24.2
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (widget.canBeRemoved)
|
|
||||||
Positioned(
|
|
||||||
top: -5, right: -5,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: widget.onRemove,
|
|
||||||
customBorder: const CircleBorder(),
|
|
||||||
child: Image.asset(
|
|
||||||
'images/red_cross2.png',
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,217 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/custom_decorated_text_field.dart'; // Import du nouveau widget
|
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
|
||||||
import 'package:p_tits_pas/widgets/app_custom_checkbox.dart'; // Import de la checkbox personnalisée
|
|
||||||
// import 'package:p_tits_pas/models/placeholder_registration_data.dart'; // Remplacé
|
|
||||||
import '../../../models/parent_user_registration_data.dart'; // Import du vrai modèle
|
|
||||||
import '../../../utils/data_generator.dart'; // Import du générateur
|
|
||||||
import '../../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
|
|
||||||
class ParentRegisterStep4Screen extends StatefulWidget {
|
|
||||||
final UserRegistrationData registrationData; // Accepte les données
|
|
||||||
|
|
||||||
const ParentRegisterStep4Screen({super.key, required this.registrationData});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ParentRegisterStep4Screen> createState() => _ParentRegisterStep4ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ParentRegisterStep4ScreenState extends State<ParentRegisterStep4Screen> {
|
|
||||||
late UserRegistrationData _registrationData; // État local
|
|
||||||
final _motivationController = TextEditingController();
|
|
||||||
bool _cguAccepted = true; // Pour le test, CGU acceptées par défaut
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_registrationData = widget.registrationData;
|
|
||||||
_motivationController.text = DataGenerator.motivation(); // Générer la motivation
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_motivationController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showCGUModal() {
|
|
||||||
// Un long texte Lorem Ipsum pour simuler les CGU
|
|
||||||
const String loremIpsumText = '''
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.
|
|
||||||
|
|
||||||
Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna.
|
|
||||||
|
|
||||||
Aliquam convallis sollicitudin purus. Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet.
|
|
||||||
|
|
||||||
Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.
|
|
||||||
|
|
||||||
Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna. Etiam et felis dolor.
|
|
||||||
|
|
||||||
Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
|
|
||||||
|
|
||||||
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
|
|
||||||
''';
|
|
||||||
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false, // L'utilisateur doit utiliser le bouton
|
|
||||||
builder: (BuildContext dialogContext) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
'Conditions Générales d\'Utilisation',
|
|
||||||
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
content: SizedBox(
|
|
||||||
width: MediaQuery.of(dialogContext).size.width * 0.7, // 70% de la largeur de l'écran
|
|
||||||
height: MediaQuery.of(dialogContext).size.height * 0.6, // 60% de la hauteur de l'écran
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Text(
|
|
||||||
loremIpsumText,
|
|
||||||
style: GoogleFonts.merienda(fontSize: 13),
|
|
||||||
textAlign: TextAlign.justify,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actionsPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
|
|
||||||
actionsAlignment: MainAxisAlignment.center,
|
|
||||||
actions: <Widget>[
|
|
||||||
ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(dialogContext).primaryColor,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Valider et Accepter',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(dialogContext).pop(); // Ferme la modale
|
|
||||||
setState(() {
|
|
||||||
_cguAccepted = true; // Met à jour l'état
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
final cardWidth = screenSize.width * 0.6; // Largeur de la carte (60% de l'écran)
|
|
||||||
final double imageAspectRatio = 2.0; // Ratio corrigé (1024/512 = 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(
|
|
||||||
'Étape 4/5',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Text(
|
|
||||||
'Motivation de votre demande',
|
|
||||||
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(CardColorHorizontal.green.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(40.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: CustomDecoratedTextField(
|
|
||||||
controller: _motivationController,
|
|
||||||
hintText: 'Écrivez ici pour motiver votre demande...',
|
|
||||||
fieldHeight: cardHeight * 0.6,
|
|
||||||
maxLines: 10,
|
|
||||||
expandDynamically: true,
|
|
||||||
fontSize: 18.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (!_cguAccepted) {
|
|
||||||
_showCGUModal();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: AppCustomCheckbox(
|
|
||||||
label: 'J\'accepte les conditions générales d\'utilisation',
|
|
||||||
value: _cguAccepted,
|
|
||||||
onChanged: (newValue) {
|
|
||||||
if (!_cguAccepted) {
|
|
||||||
_showCGUModal();
|
|
||||||
} else {
|
|
||||||
setState(() => _cguAccepted = false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Chevrons de navigation
|
|
||||||
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: () => Navigator.pop(context),
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: _cguAccepted
|
|
||||||
? () {
|
|
||||||
_registrationData.updateMotivation(_motivationController.text);
|
|
||||||
_registrationData.acceptCGU();
|
|
||||||
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context,
|
|
||||||
'/parent-register/step5',
|
|
||||||
arguments: _registrationData
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,465 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'package:p_tits_pas/widgets/Summary.dart';
|
|
||||||
import '../../../models/parent_user_registration_data.dart'; // Utilisation du vrai modèle
|
|
||||||
import '../../../widgets/image_button.dart'; // Import du ImageButton
|
|
||||||
import '../../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
|
||||||
import '../../../widgets/custom_decorated_text_field.dart'; // Import du CustomDecoratedTextField
|
|
||||||
|
|
||||||
// Nouvelle méthode helper pour afficher un champ de type "lecture seule" stylisé
|
|
||||||
Widget _buildDisplayFieldValue(BuildContext context, String label, String value, {bool multiLine = false, double fieldHeight = 50.0, double labelFontSize = 18.0}) {
|
|
||||||
const FontWeight labelFontWeight = FontWeight.w600;
|
|
||||||
|
|
||||||
// Ne pas afficher le label si labelFontSize est 0 ou si label est vide
|
|
||||||
bool showLabel = label.isNotEmpty && labelFontSize > 0;
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (showLabel)
|
|
||||||
Text(label, style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: labelFontWeight)),
|
|
||||||
if (showLabel)
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
// Utiliser Expanded si multiLine et pas de hauteur fixe, sinon Container
|
|
||||||
multiLine && fieldHeight == null
|
|
||||||
? Expanded(
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: const DecorationImage(
|
|
||||||
image: AssetImage('assets/images/input_field_bg.png'),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: SingleChildScrollView( // Pour le défilement si le texte dépasse
|
|
||||||
child: Text(
|
|
||||||
value.isNotEmpty ? value : '-',
|
|
||||||
style: GoogleFonts.merienda(fontSize: labelFontSize > 0 ? labelFontSize : 18.0), // Garder une taille de texte par défaut si label caché
|
|
||||||
maxLines: null, // Permettre un nombre illimité de lignes
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: multiLine ? null : fieldHeight,
|
|
||||||
constraints: multiLine ? BoxConstraints(minHeight: fieldHeight) : null,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: const DecorationImage(
|
|
||||||
image: AssetImage('assets/images/input_field_bg.png'),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
value.isNotEmpty ? value : '-',
|
|
||||||
style: GoogleFonts.merienda(fontSize: labelFontSize > 0 ? labelFontSize : 18.0),
|
|
||||||
maxLines: multiLine ? null : 1,
|
|
||||||
overflow: multiLine ? TextOverflow.visible : TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParentRegisterStep5Screen extends StatelessWidget {
|
|
||||||
final UserRegistrationData registrationData;
|
|
||||||
|
|
||||||
const ParentRegisterStep5Screen({super.key, required this.registrationData});
|
|
||||||
|
|
||||||
// Méthode pour construire la carte Parent 1
|
|
||||||
Widget _buildParent1Card(BuildContext context, ParentData data) {
|
|
||||||
const double verticalSpacing = 28.0; // Espacement vertical augmenté
|
|
||||||
const double labelFontSize = 22.0; // Taille de label augmentée
|
|
||||||
|
|
||||||
List<Widget> details = [
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Nom:", data.lastName, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Prénom:", data.firstName, labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: verticalSpacing),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Téléphone:", data.phone, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Email:", data.email, multiLine: true, labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: verticalSpacing),
|
|
||||||
_buildDisplayFieldValue(context, "Adresse:", "${data.address}\n${data.postalCode} ${data.city}".trim(), multiLine: true, fieldHeight: 80, labelFontSize: labelFontSize),
|
|
||||||
];
|
|
||||||
return _SummaryCard(
|
|
||||||
backgroundImagePath: CardColorHorizontal.peach.path,
|
|
||||||
title: 'Parent Principal',
|
|
||||||
content: details,
|
|
||||||
onEdit: () => Navigator.of(context).pushNamed('/parent-register/step1', arguments: registrationData),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthode pour construire la carte Parent 2
|
|
||||||
Widget _buildParent2Card(BuildContext context, ParentData data) {
|
|
||||||
const double verticalSpacing = 28.0;
|
|
||||||
const double labelFontSize = 22.0;
|
|
||||||
List<Widget> details = [
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Nom:", data.lastName, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Prénom:", data.firstName, labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: verticalSpacing),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Téléphone:", data.phone, labelFontSize: labelFontSize)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(child: _buildDisplayFieldValue(context, "Email:", data.email, multiLine: true, labelFontSize: labelFontSize)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: verticalSpacing),
|
|
||||||
_buildDisplayFieldValue(context, "Adresse:", "${data.address}\n${data.postalCode} ${data.city}".trim(), multiLine: true, fieldHeight: 80, labelFontSize: labelFontSize),
|
|
||||||
];
|
|
||||||
return SummaryCard(
|
|
||||||
backgroundImagePath: CardColorHorizontal.blue.path,
|
|
||||||
title: 'Deuxième Parent',
|
|
||||||
content: details,
|
|
||||||
onEdit: () => Navigator.of(context).pushNamed('/parent-register/step2', arguments: registrationData),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthode pour construire les cartes Enfants
|
|
||||||
List<Widget> _buildChildrenCards(BuildContext context, List<ChildData> children) {
|
|
||||||
return children.asMap().entries.map((entry) {
|
|
||||||
int index = entry.key;
|
|
||||||
ChildData child = entry.value;
|
|
||||||
|
|
||||||
CardColorHorizontal cardColorHorizontal = CardColorHorizontal.values.firstWhere(
|
|
||||||
(e) => e.name == child.cardColor.name,
|
|
||||||
orElse: () => CardColorHorizontal.lavender,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 20.0),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: 2.0,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 25.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(cardColorHorizontal.path),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Titre centré dans la carte
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
'Enfant ${index + 1}' + (child.isUnbornChild ? ' (à naître)' : ''),
|
|
||||||
style: GoogleFonts.merienda(fontSize: 28, fontWeight: FontWeight.w600),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.edit, color: Colors.black54, size: 28),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pushNamed(
|
|
||||||
'/parent-register/step3',
|
|
||||||
arguments: registrationData,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
tooltip: 'Modifier',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 18),
|
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
// IMAGE SANS CADRE BLANC, PREND LA HAUTEUR
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Center(
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(18),
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 1,
|
|
||||||
child: (child.imageFile != null)
|
|
||||||
? (kIsWeb
|
|
||||||
? Image.network(child.imageFile!.path, fit: BoxFit.cover)
|
|
||||||
: Image.file(child.imageFile!, fit: BoxFit.cover))
|
|
||||||
: Image.asset('assets/images/photo.png', fit: BoxFit.contain),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 32),
|
|
||||||
// INFOS À DROITE (2/3)
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
_buildDisplayFieldValue(context, 'Prénom :', child.firstName, labelFontSize: 22.0),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
_buildDisplayFieldValue(context, 'Nom :', child.lastName, labelFontSize: 22.0),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
_buildDisplayFieldValue(context, child.isUnbornChild ? 'Date de naissance :' : 'Date de naissance :', child.dob, labelFontSize: 22.0),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 18),
|
|
||||||
// Ligne des consentements
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Checkbox(
|
|
||||||
value: child.photoConsent,
|
|
||||||
onChanged: null,
|
|
||||||
),
|
|
||||||
Text('Consentement photo', style: GoogleFonts.merienda(fontSize: 16)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(width: 32),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Checkbox(
|
|
||||||
value: child.multipleBirth,
|
|
||||||
onChanged: null,
|
|
||||||
),
|
|
||||||
Text('Naissance multiple', style: GoogleFonts.merienda(fontSize: 16)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthode pour construire la carte Motivation
|
|
||||||
Widget _buildMotivationCard(BuildContext context, String motivation) {
|
|
||||||
return _SummaryCard(
|
|
||||||
backgroundImagePath: CardColorHorizontal.green.path,
|
|
||||||
title: 'Votre Motivation',
|
|
||||||
content: [
|
|
||||||
Expanded(
|
|
||||||
child: CustomDecoratedTextField(
|
|
||||||
controller: TextEditingController(text: motivation),
|
|
||||||
hintText: 'Aucune motivation renseignée.',
|
|
||||||
fieldHeight: 200,
|
|
||||||
maxLines: 10,
|
|
||||||
expandDynamically: true,
|
|
||||||
readOnly: true,
|
|
||||||
fontSize: 18.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onEdit: () => Navigator.of(context).pushNamed('/parent-register/step4', arguments: registrationData),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper pour afficher une ligne de détail (police et agencement amélioré)
|
|
||||||
Widget _buildDetailRow(String label, String value) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"$label: ",
|
|
||||||
style: GoogleFonts.merienda(fontSize: 18, fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
value.isNotEmpty ? value : '-',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 18),
|
|
||||||
softWrap: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
final cardWidth = screenSize.width / 2.0; // Largeur de la carte (50% de l'écran)
|
|
||||||
final double imageAspectRatio = 2.0; // Ratio corrigé (1024/512 = 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.repeatY),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40.0), // Padding horizontal supprimé ici
|
|
||||||
child: Padding( // Ajout du Padding horizontal externe
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: screenSize.width / 4.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text('Étape 5/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Text('Récapitulatif de votre demande', style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), textAlign: TextAlign.center),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
|
|
||||||
_buildParent1Card(context, registrationData.parent1),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
if (registrationData.parent2 != null) ...[
|
|
||||||
_buildParent2Card(context, registrationData.parent2!),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
],
|
|
||||||
..._buildChildrenCards(context, registrationData.children),
|
|
||||||
_buildMotivationCard(context, registrationData.motivationText),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
ImageButton(
|
|
||||||
bg: 'assets/images/btn_green.png',
|
|
||||||
text: 'Soumettre ma demande',
|
|
||||||
textColor: const Color(0xFF2D6A4F),
|
|
||||||
width: 350,
|
|
||||||
height: 50,
|
|
||||||
fontSize: 18,
|
|
||||||
onPressed: () {
|
|
||||||
print("Données finales: ${registrationData.parent1.firstName}, Enfant(s): ${registrationData.children.length}");
|
|
||||||
_showConfirmationModal(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
left: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Transform.flip(flipX: true, child: Image.asset('assets/images/chevron_right.png', height: 40)),
|
|
||||||
onPressed: () => Navigator.pop(context), // Retour à l'étape 4
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showConfirmationModal(BuildContext context) {
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (BuildContext dialogContext) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
'Demande enregistrée',
|
|
||||||
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
content: Text(
|
|
||||||
'Votre dossier a bien été pris en compte. Un gestionnaire le validera bientôt.',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 14),
|
|
||||||
),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(dialogContext).pop(); // Ferme la modale
|
|
||||||
// TODO: Naviguer vers l'écran de connexion ou tableau de bord
|
|
||||||
Navigator.of(context).pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Widget générique _SummaryCard (ajusté)
|
|
||||||
class _SummaryCard extends StatelessWidget {
|
|
||||||
final String backgroundImagePath;
|
|
||||||
final String title;
|
|
||||||
final List<Widget> content;
|
|
||||||
final VoidCallback onEdit;
|
|
||||||
|
|
||||||
const _SummaryCard({
|
|
||||||
super.key,
|
|
||||||
required this.backgroundImagePath,
|
|
||||||
required this.title,
|
|
||||||
required this.content,
|
|
||||||
required this.onEdit,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AspectRatio(
|
|
||||||
aspectRatio: 2.0,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 25.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(backgroundImagePath),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
title,
|
|
||||||
style: GoogleFonts.merienda(fontSize: 28, fontWeight: FontWeight.w600),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.edit, color: Colors.black54, size: 28),
|
|
||||||
onPressed: onEdit,
|
|
||||||
tooltip: 'Modifier',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 18),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
children: content,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,250 +1,65 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
|
||||||
import '../../models/user_registration_data.dart'; // Import du modèle de données
|
|
||||||
import '../../utils/data_generator.dart'; // Import du générateur de données
|
|
||||||
import '../../widgets/custom_app_text_field.dart'; // Import du widget CustomAppTextField
|
|
||||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart'; // Importer Provider
|
|
||||||
|
|
||||||
class ParentRegisterStep1Screen extends StatefulWidget {
|
import '../../models/user_registration_data.dart';
|
||||||
|
import '../../utils/data_generator.dart';
|
||||||
|
import '../../widgets/personal_info_form_screen.dart';
|
||||||
|
import '../../models/card_assets.dart';
|
||||||
|
|
||||||
|
class ParentRegisterStep1Screen extends StatelessWidget {
|
||||||
const ParentRegisterStep1Screen({super.key});
|
const ParentRegisterStep1Screen({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
State<ParentRegisterStep1Screen> createState() => _ParentRegisterStep1ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ParentRegisterStep1ScreenState extends State<ParentRegisterStep1Screen> {
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
// late UserRegistrationData _registrationData; // Supprimé, on utilisera le Provider
|
|
||||||
|
|
||||||
// Contrôleurs pour les champs
|
|
||||||
final _lastNameController = TextEditingController();
|
|
||||||
final _firstNameController = TextEditingController();
|
|
||||||
final _phoneController = TextEditingController();
|
|
||||||
final _emailController = TextEditingController();
|
|
||||||
final _passwordController = TextEditingController();
|
|
||||||
final _confirmPasswordController = TextEditingController();
|
|
||||||
final _addressController = TextEditingController();
|
|
||||||
final _postalCodeController = TextEditingController();
|
|
||||||
final _cityController = TextEditingController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// Récupérer les données existantes du Provider pour pré-remplir si l'utilisateur revient
|
|
||||||
final registrationDataFromProvider = Provider.of<UserRegistrationData>(context, listen: false);
|
|
||||||
_firstNameController.text = registrationDataFromProvider.parent1.firstName;
|
|
||||||
_lastNameController.text = registrationDataFromProvider.parent1.lastName;
|
|
||||||
_phoneController.text = registrationDataFromProvider.parent1.phone;
|
|
||||||
_emailController.text = registrationDataFromProvider.parent1.email;
|
|
||||||
_passwordController.text = registrationDataFromProvider.parent1.password;
|
|
||||||
_confirmPasswordController.text = registrationDataFromProvider.parent1.password; // Ou laisser vide pour reconfirmation
|
|
||||||
_addressController.text = registrationDataFromProvider.parent1.address;
|
|
||||||
_postalCodeController.text = registrationDataFromProvider.parent1.postalCode;
|
|
||||||
_cityController.text = registrationDataFromProvider.parent1.city;
|
|
||||||
|
|
||||||
// Si les champs sont vides (première visite), générer des données
|
|
||||||
if (registrationDataFromProvider.parent1.firstName.isEmpty) {
|
|
||||||
_generateAndFillData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _generateAndFillData() {
|
|
||||||
final String genFirstName = DataGenerator.firstName();
|
|
||||||
final String genLastName = DataGenerator.lastName();
|
|
||||||
|
|
||||||
_addressController.text = DataGenerator.address();
|
|
||||||
_postalCodeController.text = DataGenerator.postalCode();
|
|
||||||
_cityController.text = DataGenerator.city();
|
|
||||||
|
|
||||||
_firstNameController.text = genFirstName;
|
|
||||||
_lastNameController.text = genLastName;
|
|
||||||
_phoneController.text = DataGenerator.phone();
|
|
||||||
_emailController.text = DataGenerator.email(genFirstName, genLastName);
|
|
||||||
_passwordController.text = DataGenerator.password();
|
|
||||||
_confirmPasswordController.text = _passwordController.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_lastNameController.dispose();
|
|
||||||
_firstNameController.dispose();
|
|
||||||
_phoneController.dispose();
|
|
||||||
_emailController.dispose();
|
|
||||||
_passwordController.dispose();
|
|
||||||
_confirmPasswordController.dispose();
|
|
||||||
_addressController.dispose();
|
|
||||||
_postalCodeController.dispose();
|
|
||||||
_cityController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _submitForm() {
|
|
||||||
if (_formKey.currentState?.validate() ?? false) {
|
|
||||||
final registrationData = Provider.of<UserRegistrationData>(context, listen: false);
|
|
||||||
registrationData.updateParent1(
|
|
||||||
ParentData(
|
|
||||||
firstName: _firstNameController.text,
|
|
||||||
lastName: _lastNameController.text,
|
|
||||||
address: _addressController.text,
|
|
||||||
postalCode: _postalCodeController.text,
|
|
||||||
city: _cityController.text,
|
|
||||||
phone: _phoneController.text,
|
|
||||||
email: _emailController.text,
|
|
||||||
password: _passwordController.text, // Sauvegarder le mot de passe
|
|
||||||
)
|
|
||||||
);
|
|
||||||
context.go('/parent-register-step2');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenSize = MediaQuery.of(context).size;
|
final registrationData = Provider.of<UserRegistrationData>(context, listen: false);
|
||||||
|
final parent1 = registrationData.parent1;
|
||||||
|
|
||||||
return Scaffold(
|
// Générer des données de test si vide
|
||||||
body: Stack(
|
PersonalInfoData initialData;
|
||||||
children: [
|
if (parent1.firstName.isEmpty) {
|
||||||
// Fond papier
|
final genFirstName = DataGenerator.firstName();
|
||||||
Positioned.fill(
|
final genLastName = DataGenerator.lastName();
|
||||||
child: Image.asset(
|
initialData = PersonalInfoData(
|
||||||
'assets/images/paper2.png',
|
firstName: genFirstName,
|
||||||
fit: BoxFit.cover,
|
lastName: genLastName,
|
||||||
repeat: ImageRepeat.repeat,
|
phone: DataGenerator.phone(),
|
||||||
),
|
email: DataGenerator.email(genFirstName, genLastName),
|
||||||
),
|
address: DataGenerator.address(),
|
||||||
|
postalCode: DataGenerator.postalCode(),
|
||||||
|
city: DataGenerator.city(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
initialData = PersonalInfoData(
|
||||||
|
firstName: parent1.firstName,
|
||||||
|
lastName: parent1.lastName,
|
||||||
|
phone: parent1.phone,
|
||||||
|
email: parent1.email,
|
||||||
|
address: parent1.address,
|
||||||
|
postalCode: parent1.postalCode,
|
||||||
|
city: parent1.city,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Contenu centré
|
return PersonalInfoFormScreen(
|
||||||
Center(
|
stepText: 'Étape 1/5',
|
||||||
child: SingleChildScrollView(
|
title: 'Informations du Parent Principal',
|
||||||
child: Column(
|
cardColor: CardColorHorizontal.peach,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
initialData: initialData,
|
||||||
children: [
|
previousRoute: '/register-choice',
|
||||||
// Indicateur d'étape (à rendre dynamique)
|
onSubmit: (data, {hasSecondPerson, sameAddress}) {
|
||||||
Text(
|
registrationData.updateParent1(ParentData(
|
||||||
'Étape 1/5',
|
firstName: data.firstName,
|
||||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
lastName: data.lastName,
|
||||||
),
|
phone: data.phone,
|
||||||
const SizedBox(height: 10),
|
email: data.email,
|
||||||
// Texte d'instruction
|
address: data.address,
|
||||||
Text(
|
postalCode: data.postalCode,
|
||||||
'Informations du Parent Principal',
|
city: data.city,
|
||||||
style: GoogleFonts.merienda(
|
password: '',
|
||||||
fontSize: 24,
|
));
|
||||||
fontWeight: FontWeight.bold,
|
context.go('/parent-register-step2');
|
||||||
color: Colors.black87,
|
},
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
|
|
||||||
// Carte jaune contenant le formulaire
|
|
||||||
Container(
|
|
||||||
width: screenSize.width * 0.6,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50),
|
|
||||||
constraints: const BoxConstraints(minHeight: 570),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(CardColorHorizontal.peach.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _lastNameController, labelText: 'Nom', hintText: 'Votre nom de famille', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _firstNameController, labelText: 'Prénom', hintText: 'Votre prénom', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _phoneController, labelText: 'Téléphone', keyboardType: TextInputType.phone, hintText: 'Votre numéro de téléphone', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _emailController, labelText: 'Email', keyboardType: TextInputType.emailAddress, hintText: 'Votre adresse e-mail', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _passwordController, labelText: 'Mot de passe', obscureText: true, hintText: 'Créez votre mot de passe', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) return 'Mot de passe requis';
|
|
||||||
if (value.length < 6) return '6 caractères minimum';
|
|
||||||
return null;
|
|
||||||
})),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _confirmPasswordController, labelText: 'Confirmation', obscureText: true, hintText: 'Confirmez le mot de passe', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) return 'Confirmation requise';
|
|
||||||
if (value != _passwordController.text) return 'Ne correspond pas';
|
|
||||||
return null;
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _addressController,
|
|
||||||
labelText: 'Adresse (N° et Rue)',
|
|
||||||
hintText: 'Numéro et nom de votre rue',
|
|
||||||
style: CustomAppTextFieldStyle.beige,
|
|
||||||
fieldWidth: double.infinity,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 1, child: CustomAppTextField(controller: _postalCodeController, labelText: 'Code Postal', keyboardType: TextInputType.number, hintText: 'Code postal', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(flex: 4, child: CustomAppTextField(controller: _cityController, labelText: 'Ville', hintText: 'Votre ville', style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Chevron de navigation gauche (Retour)
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20, // Centré verticalement
|
|
||||||
left: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Transform(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
transform: Matrix4.rotationY(math.pi), // Inverse horizontalement
|
|
||||||
child: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
if (context.canPop()) {
|
|
||||||
context.pop();
|
|
||||||
} else {
|
|
||||||
context.go('/register-choice');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Chevron de navigation droit (Suivant)
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20, // Centré verticalement
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: _submitForm, // Appel de la fonction de soumission
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,290 +1,90 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
|
||||||
import '../../models/user_registration_data.dart'; // Import du modèle
|
|
||||||
import '../../utils/data_generator.dart'; // Import du générateur
|
|
||||||
import '../../widgets/custom_app_text_field.dart'; // Import du widget
|
|
||||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:go_router/go_router.dart'; // Importer GoRouter
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class ParentRegisterStep2Screen extends StatefulWidget {
|
import '../../models/user_registration_data.dart';
|
||||||
// final UserRegistrationData registrationData; // Supprimé
|
import '../../utils/data_generator.dart';
|
||||||
|
import '../../widgets/personal_info_form_screen.dart';
|
||||||
|
import '../../models/card_assets.dart';
|
||||||
|
|
||||||
const ParentRegisterStep2Screen({super.key /*, required this.registrationData */}); // Modifié
|
class ParentRegisterStep2Screen extends StatelessWidget {
|
||||||
|
const ParentRegisterStep2Screen({super.key});
|
||||||
@override
|
|
||||||
_ParentRegisterStep2ScreenState createState() =>
|
|
||||||
_ParentRegisterStep2ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ParentRegisterStep2ScreenState extends State<ParentRegisterStep2Screen> {
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
// late UserRegistrationData _registrationData; // Supprimé
|
|
||||||
|
|
||||||
bool _addParent2 = true; // Pour le test, on ajoute toujours le parent 2
|
|
||||||
bool _sameAddressAsParent1 = false; // Peut être généré aléatoirement aussi
|
|
||||||
|
|
||||||
// Contrôleurs pour les champs du parent 2 (restauration CP et Ville)
|
|
||||||
final _lastNameController = TextEditingController();
|
|
||||||
final _firstNameController = TextEditingController();
|
|
||||||
final _phoneController = TextEditingController();
|
|
||||||
final _emailController = TextEditingController();
|
|
||||||
final _passwordController = TextEditingController();
|
|
||||||
final _confirmPasswordController = TextEditingController();
|
|
||||||
final _addressController = TextEditingController(); // Rue seule
|
|
||||||
final _postalCodeController = TextEditingController(); // Restauré
|
|
||||||
final _cityController = TextEditingController(); // Restauré
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// On ne récupère plus _registrationData ici
|
|
||||||
// Mais on peut récupérer les données initiales pour les contrôleurs si nécessaire
|
|
||||||
final initialData = Provider.of<UserRegistrationData>(context, listen: false);
|
|
||||||
_addParent2 = initialData.parent2 != null;
|
|
||||||
if (_addParent2) {
|
|
||||||
_fillParent2Data(initialData.parent2!, initialData.parent1);
|
|
||||||
} else {
|
|
||||||
_generateAndFillParent2Data(initialData.parent1); // Ou générer si pas de données
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modifié pour prendre les données parent1
|
|
||||||
void _generateAndFillParent2Data(ParentData parent1Data) {
|
|
||||||
final String genFirstName = DataGenerator.firstName();
|
|
||||||
final String genLastName = DataGenerator.lastName();
|
|
||||||
_firstNameController.text = genFirstName;
|
|
||||||
_lastNameController.text = genLastName;
|
|
||||||
_phoneController.text = DataGenerator.phone();
|
|
||||||
_emailController.text = DataGenerator.email(genFirstName, genLastName);
|
|
||||||
_passwordController.text = DataGenerator.password();
|
|
||||||
_confirmPasswordController.text = _passwordController.text;
|
|
||||||
|
|
||||||
_sameAddressAsParent1 = DataGenerator.boolean();
|
|
||||||
if (!_sameAddressAsParent1) {
|
|
||||||
// Générer adresse, CP, Ville séparément
|
|
||||||
_addressController.text = DataGenerator.address();
|
|
||||||
_postalCodeController.text = DataGenerator.postalCode();
|
|
||||||
_cityController.text = DataGenerator.city();
|
|
||||||
} else {
|
|
||||||
// Vider les champs si même adresse (seront désactivés)
|
|
||||||
_addressController.text = parent1Data.address;
|
|
||||||
_postalCodeController.text = parent1Data.postalCode;
|
|
||||||
_cityController.text = parent1Data.city;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nouvelle fonction pour remplir depuis les données existantes
|
|
||||||
void _fillParent2Data(ParentData parent2Data, ParentData parent1Data) {
|
|
||||||
_firstNameController.text = parent2Data.firstName;
|
|
||||||
_lastNameController.text = parent2Data.lastName;
|
|
||||||
_phoneController.text = parent2Data.phone;
|
|
||||||
_emailController.text = parent2Data.email;
|
|
||||||
_passwordController.text = parent2Data.password; // Attention à la sécurité
|
|
||||||
_confirmPasswordController.text = parent2Data.password;
|
|
||||||
|
|
||||||
_sameAddressAsParent1 = (parent2Data.address == parent1Data.address &&
|
|
||||||
parent2Data.postalCode == parent1Data.postalCode &&
|
|
||||||
parent2Data.city == parent1Data.city);
|
|
||||||
|
|
||||||
_addressController.text = parent2Data.address;
|
|
||||||
_postalCodeController.text = parent2Data.postalCode;
|
|
||||||
_cityController.text = parent2Data.city;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_lastNameController.dispose();
|
|
||||||
_firstNameController.dispose();
|
|
||||||
_phoneController.dispose();
|
|
||||||
_emailController.dispose();
|
|
||||||
_passwordController.dispose();
|
|
||||||
_confirmPasswordController.dispose();
|
|
||||||
_addressController.dispose();
|
|
||||||
_postalCodeController.dispose();
|
|
||||||
_cityController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _parent2FieldsEnabled => _addParent2;
|
|
||||||
bool get _addressFieldsEnabled => _addParent2 && !_sameAddressAsParent1;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final registrationData = Provider.of<UserRegistrationData>(context, listen: false);
|
final registrationData = Provider.of<UserRegistrationData>(context, listen: false);
|
||||||
final parent1Data = registrationData.parent1;
|
final parent1 = registrationData.parent1;
|
||||||
final screenSize = MediaQuery.of(context).size;
|
final parent2 = registrationData.parent2;
|
||||||
|
|
||||||
return Scaffold(
|
bool hasParent2 = parent2 != null;
|
||||||
body: Stack(
|
bool sameAddress = false;
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
// Générer des données de test si vide
|
||||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
PersonalInfoData initialData;
|
||||||
),
|
if (parent2 == null || parent2.firstName.isEmpty) {
|
||||||
Center(
|
final genFirstName = DataGenerator.firstName();
|
||||||
child: SingleChildScrollView(
|
final genLastName = DataGenerator.lastName();
|
||||||
child: Column(
|
sameAddress = DataGenerator.boolean();
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
initialData = PersonalInfoData(
|
||||||
Text('Étape 2/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
firstName: genFirstName,
|
||||||
const SizedBox(height: 10),
|
lastName: genLastName,
|
||||||
Text(
|
phone: DataGenerator.phone(),
|
||||||
'Informations du Deuxième Parent (Optionnel)',
|
email: DataGenerator.email(genFirstName, genLastName),
|
||||||
style: GoogleFonts.merienda(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87),
|
address: sameAddress ? parent1.address : DataGenerator.address(),
|
||||||
textAlign: TextAlign.center,
|
postalCode: sameAddress ? parent1.postalCode : DataGenerator.postalCode(),
|
||||||
),
|
city: sameAddress ? parent1.city : DataGenerator.city(),
|
||||||
const SizedBox(height: 30),
|
);
|
||||||
Container(
|
} else {
|
||||||
width: screenSize.width * 0.6,
|
sameAddress = (parent2.address == parent1.address &&
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 50),
|
parent2.postalCode == parent1.postalCode &&
|
||||||
decoration: BoxDecoration(
|
parent2.city == parent1.city);
|
||||||
image: DecorationImage(image: AssetImage(CardColorHorizontal.blue.path), fit: BoxFit.fill),
|
initialData = PersonalInfoData(
|
||||||
),
|
firstName: parent2.firstName,
|
||||||
child: Form(
|
lastName: parent2.lastName,
|
||||||
key: _formKey,
|
phone: parent2.phone,
|
||||||
child: SingleChildScrollView(
|
email: parent2.email,
|
||||||
child: Column(
|
address: parent2.address,
|
||||||
mainAxisSize: MainAxisSize.min,
|
postalCode: parent2.postalCode,
|
||||||
children: [
|
city: parent2.city,
|
||||||
Row(
|
);
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
}
|
||||||
children: [
|
|
||||||
Expanded(
|
// Adresse de référence pour "même adresse"
|
||||||
flex: 12,
|
final referenceAddress = PersonalInfoData(
|
||||||
child: Row(children: [
|
address: parent1.address,
|
||||||
const Icon(Icons.person_add_alt_1, size: 20), const SizedBox(width: 8),
|
postalCode: parent1.postalCode,
|
||||||
Flexible(child: Text('Ajouter Parent 2 ?', style: GoogleFonts.merienda(fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis)),
|
city: parent1.city,
|
||||||
const Spacer(),
|
);
|
||||||
Switch(value: _addParent2, onChanged: (val) => setState(() {
|
|
||||||
_addParent2 = val ?? false;
|
return PersonalInfoFormScreen(
|
||||||
if (_addParent2) _generateAndFillParent2Data(parent1Data); else _clearParent2Fields();
|
stepText: 'Étape 2/5',
|
||||||
}), activeColor: Theme.of(context).primaryColor),
|
title: 'Deuxième Parent',
|
||||||
]),
|
cardColor: CardColorHorizontal.blue,
|
||||||
),
|
initialData: initialData,
|
||||||
Expanded(flex: 1, child: const SizedBox()),
|
previousRoute: '/parent-register-step1',
|
||||||
Expanded(
|
showSecondPersonToggle: true,
|
||||||
flex: 12,
|
initialHasSecondPerson: hasParent2,
|
||||||
child: Row(children: [
|
showSameAddressCheckbox: true,
|
||||||
Icon(Icons.home_work_outlined, size: 20, color: _addParent2 ? null : Colors.grey),
|
initialSameAddress: sameAddress,
|
||||||
const SizedBox(width: 8),
|
referenceAddressData: referenceAddress,
|
||||||
Flexible(child: Text('Même Adresse ?', style: GoogleFonts.merienda(color: _addParent2 ? null : Colors.grey), overflow: TextOverflow.ellipsis)),
|
onSubmit: (data, {hasSecondPerson, sameAddress}) {
|
||||||
const Spacer(),
|
if (hasSecondPerson == true) {
|
||||||
Switch(value: _sameAddressAsParent1, onChanged: _addParent2 ? (val) => setState(() {
|
registrationData.updateParent2(ParentData(
|
||||||
_sameAddressAsParent1 = val ?? false;
|
firstName: data.firstName,
|
||||||
if (_sameAddressAsParent1) {
|
lastName: data.lastName,
|
||||||
_addressController.text = parent1Data.address;
|
phone: data.phone,
|
||||||
_postalCodeController.text = parent1Data.postalCode;
|
email: data.email,
|
||||||
_cityController.text = parent1Data.city;
|
address: data.address,
|
||||||
} else {
|
postalCode: data.postalCode,
|
||||||
_addressController.text = DataGenerator.address();
|
city: data.city,
|
||||||
_postalCodeController.text = DataGenerator.postalCode();
|
password: '',
|
||||||
_cityController.text = DataGenerator.city();
|
));
|
||||||
}
|
} else {
|
||||||
}) : null, activeColor: Theme.of(context).primaryColor),
|
registrationData.updateParent2(null);
|
||||||
]),
|
}
|
||||||
),
|
context.go('/parent-register-step3');
|
||||||
]),
|
},
|
||||||
const SizedBox(height: 25),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _lastNameController, labelText: 'Nom', hintText: 'Nom du parent 2', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _firstNameController, labelText: 'Prénom', hintText: 'Prénom du parent 2', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _phoneController, labelText: 'Téléphone', keyboardType: TextInputType.phone, hintText: 'Son téléphone', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _emailController, labelText: 'Email', keyboardType: TextInputType.emailAddress, hintText: 'Son email', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _passwordController, labelText: 'Mot de passe', obscureText: true, hintText: 'Son mot de passe', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, validator: _addParent2 ? (v) => (v == null || v.isEmpty ? 'Requis' : (v.length < 6 ? '6 car. min' : null)) : null)),
|
|
||||||
Expanded(flex: 1, child: const SizedBox()), // Espace de 4%
|
|
||||||
Expanded(flex: 12, child: CustomAppTextField(controller: _confirmPasswordController, labelText: 'Confirmation', obscureText: true, hintText: 'Confirmer mot de passe', enabled: _parent2FieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity, validator: _addParent2 ? (v) => (v == null || v.isEmpty ? 'Requis' : (v != _passwordController.text ? 'Différent' : null)) : null)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
CustomAppTextField(controller: _addressController, labelText: 'Adresse (N° et Rue)', hintText: 'Son numéro et nom de rue', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 1, child: CustomAppTextField(controller: _postalCodeController, labelText: 'Code Postal', keyboardType: TextInputType.number, hintText: 'Son code postal', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Expanded(flex: 4, child: CustomAppTextField(controller: _cityController, labelText: 'Ville', hintText: 'Sa ville', enabled: _addressFieldsEnabled, style: CustomAppTextFieldStyle.beige, fieldWidth: double.infinity)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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('/parent-register-step1');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: () {
|
|
||||||
if (!_addParent2 || (_formKey.currentState?.validate() ?? false)) {
|
|
||||||
if (_addParent2) {
|
|
||||||
registrationData.updateParent2(
|
|
||||||
ParentData(
|
|
||||||
firstName: _firstNameController.text,
|
|
||||||
lastName: _lastNameController.text,
|
|
||||||
address: _sameAddressAsParent1 ? parent1Data.address : _addressController.text,
|
|
||||||
postalCode: _sameAddressAsParent1 ? parent1Data.postalCode : _postalCodeController.text,
|
|
||||||
city: _sameAddressAsParent1 ? parent1Data.city : _cityController.text,
|
|
||||||
phone: _phoneController.text,
|
|
||||||
email: _emailController.text,
|
|
||||||
password: _passwordController.text,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
registrationData.updateParent2(null);
|
|
||||||
}
|
|
||||||
context.go('/parent-register-step3');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearParent2Fields() {
|
|
||||||
_formKey.currentState?.reset();
|
|
||||||
_lastNameController.clear(); _firstNameController.clear(); _phoneController.clear();
|
|
||||||
_emailController.clear(); _passwordController.clear(); _confirmPasswordController.clear();
|
|
||||||
_addressController.clear();
|
|
||||||
_postalCodeController.clear();
|
|
||||||
_cityController.clear();
|
|
||||||
_sameAddressAsParent1 = false;
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,22 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
import 'dart:math' as math; // Pour la rotation du chevron
|
||||||
import 'package:flutter/gestures.dart'; // Pour PointerDeviceKind
|
|
||||||
import '../../widgets/hover_relief_widget.dart'; // Import du nouveau widget
|
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
// import 'package:image_cropper/image_cropper.dart'; // Supprimé
|
import 'dart:io' show File;
|
||||||
import 'dart:io' show File, Platform; // Ajout de Platform
|
import '../../widgets/hover_relief_widget.dart';
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb; // Import pour kIsWeb
|
import '../../widgets/child_card_widget.dart';
|
||||||
import '../../widgets/custom_app_text_field.dart'; // Import du nouveau widget TextField
|
import '../../models/user_registration_data.dart';
|
||||||
import '../../widgets/app_custom_checkbox.dart'; // Import du nouveau widget Checkbox
|
import '../../utils/data_generator.dart';
|
||||||
import '../../models/user_registration_data.dart'; // Import du modèle de données
|
import '../../models/card_assets.dart';
|
||||||
import '../../utils/data_generator.dart'; // Import du générateur
|
import 'package:provider/provider.dart';
|
||||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
import 'package:provider/provider.dart'; // Assurer l'import
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
// La classe _ChildFormData est supprimée car on utilise ChildData du modèle
|
|
||||||
|
|
||||||
class ParentRegisterStep3Screen extends StatefulWidget {
|
class ParentRegisterStep3Screen extends StatefulWidget {
|
||||||
// final UserRegistrationData registrationData; // Supprimé
|
// final UserRegistrationData registrationData; // Supprimé
|
||||||
|
|
||||||
@ -253,7 +247,7 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
|
|||||||
// Carte Enfant
|
// Carte Enfant
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 20.0),
|
padding: const EdgeInsets.only(right: 20.0),
|
||||||
child: _ChildCardWidget(
|
child: ChildCardWidget(
|
||||||
key: ValueKey(registrationData.children[index].hashCode), // Utiliser une clé basée sur les données
|
key: ValueKey(registrationData.children[index].hashCode), // Utiliser une clé basée sur les données
|
||||||
childData: registrationData.children[index],
|
childData: registrationData.children[index],
|
||||||
childIndex: index,
|
childIndex: index,
|
||||||
@ -345,195 +339,3 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget pour la carte enfant (adapté pour prendre ChildData et des callbacks)
|
|
||||||
class _ChildCardWidget extends StatefulWidget { // Transformé en StatefulWidget pour gérer les contrôleurs internes
|
|
||||||
final ChildData childData;
|
|
||||||
final int childIndex;
|
|
||||||
final VoidCallback onPickImage;
|
|
||||||
final VoidCallback onDateSelect;
|
|
||||||
final ValueChanged<String> onFirstNameChanged;
|
|
||||||
final ValueChanged<String> onLastNameChanged;
|
|
||||||
final ValueChanged<bool> onTogglePhotoConsent;
|
|
||||||
final ValueChanged<bool> onToggleMultipleBirth;
|
|
||||||
final ValueChanged<bool> onToggleIsUnborn;
|
|
||||||
final VoidCallback onRemove;
|
|
||||||
final bool canBeRemoved;
|
|
||||||
|
|
||||||
const _ChildCardWidget({
|
|
||||||
required Key key,
|
|
||||||
required this.childData,
|
|
||||||
required this.childIndex,
|
|
||||||
required this.onPickImage,
|
|
||||||
required this.onDateSelect,
|
|
||||||
required this.onFirstNameChanged,
|
|
||||||
required this.onLastNameChanged,
|
|
||||||
required this.onTogglePhotoConsent,
|
|
||||||
required this.onToggleMultipleBirth,
|
|
||||||
required this.onToggleIsUnborn,
|
|
||||||
required this.onRemove,
|
|
||||||
required this.canBeRemoved,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_ChildCardWidget> createState() => _ChildCardWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ChildCardWidgetState extends State<_ChildCardWidget> {
|
|
||||||
late TextEditingController _firstNameController;
|
|
||||||
late TextEditingController _lastNameController;
|
|
||||||
late TextEditingController _dobController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// Initialiser les contrôleurs avec les données du widget
|
|
||||||
_firstNameController = TextEditingController(text: widget.childData.firstName);
|
|
||||||
_lastNameController = TextEditingController(text: widget.childData.lastName);
|
|
||||||
_dobController = TextEditingController(text: widget.childData.dob);
|
|
||||||
|
|
||||||
// Ajouter des listeners pour mettre à jour les données sources via les callbacks
|
|
||||||
_firstNameController.addListener(() => widget.onFirstNameChanged(_firstNameController.text));
|
|
||||||
_lastNameController.addListener(() => widget.onLastNameChanged(_lastNameController.text));
|
|
||||||
// Pour dob, la mise à jour se fait via _selectDate, pas besoin de listener ici
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(covariant _ChildCardWidget oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
// Mettre à jour les contrôleurs si les données externes changent
|
|
||||||
// (peut arriver si on recharge l'état global)
|
|
||||||
if (widget.childData.firstName != _firstNameController.text) {
|
|
||||||
_firstNameController.text = widget.childData.firstName;
|
|
||||||
}
|
|
||||||
if (widget.childData.lastName != _lastNameController.text) {
|
|
||||||
_lastNameController.text = widget.childData.lastName;
|
|
||||||
}
|
|
||||||
if (widget.childData.dob != _dobController.text) {
|
|
||||||
_dobController.text = widget.childData.dob;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_firstNameController.dispose();
|
|
||||||
_lastNameController.dispose();
|
|
||||||
_dobController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final File? currentChildImage = widget.childData.imageFile;
|
|
||||||
// Utiliser la couleur de la carte de childData pour l'ombre si besoin, ou directement pour le fond
|
|
||||||
final Color baseCardColorForShadow = widget.childData.cardColor == CardColorVertical.lavender
|
|
||||||
? Colors.purple.shade200
|
|
||||||
: (widget.childData.cardColor == CardColorVertical.pink ? Colors.pink.shade200 : Colors.grey.shade200); // Placeholder pour autres couleurs
|
|
||||||
final Color initialPhotoShadow = baseCardColorForShadow.withAlpha(90);
|
|
||||||
final Color hoverPhotoShadow = baseCardColorForShadow.withAlpha(130);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: 345.0 * 1.1, // 379.5
|
|
||||||
height: 570.0 * 1.2, // 684.0
|
|
||||||
padding: const EdgeInsets.all(22.0 * 1.1), // 24.2
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(image: AssetImage(widget.childData.cardColor.path), fit: BoxFit.cover),
|
|
||||||
borderRadius: BorderRadius.circular(20 * 1.1), // 22
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
HoverReliefWidget(
|
|
||||||
onPressed: widget.onPickImage,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
initialShadowColor: initialPhotoShadow,
|
|
||||||
hoverShadowColor: hoverPhotoShadow,
|
|
||||||
child: SizedBox(
|
|
||||||
height: 200.0,
|
|
||||||
width: 200.0,
|
|
||||||
child: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(5.0 * 1.1), // 5.5
|
|
||||||
child: currentChildImage != null
|
|
||||||
? ClipRRect(borderRadius: BorderRadius.circular(10 * 1.1), child: kIsWeb ? Image.network(currentChildImage.path, fit: BoxFit.cover) : Image.file(currentChildImage, fit: BoxFit.cover))
|
|
||||||
: Image.asset('assets/images/photo.png', fit: BoxFit.contain),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12.0 * 1.1), // Augmenté pour plus d'espace après la photo
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text('Enfant à naître ?', style: GoogleFonts.merienda(fontSize: 16 * 1.1, fontWeight: FontWeight.w600)),
|
|
||||||
Switch(value: widget.childData.isUnbornChild, onChanged: widget.onToggleIsUnborn, activeColor: Theme.of(context).primaryColor),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 9.0 * 1.1), // 9.9
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _firstNameController,
|
|
||||||
labelText: 'Prénom',
|
|
||||||
hintText: 'Facultatif si à naître',
|
|
||||||
isRequired: !widget.childData.isUnbornChild,
|
|
||||||
fieldHeight: 55.0 * 1.1, // 60.5
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6.0 * 1.1), // 6.6
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _lastNameController,
|
|
||||||
labelText: 'Nom',
|
|
||||||
hintText: 'Nom de l\'enfant',
|
|
||||||
enabled: true,
|
|
||||||
fieldHeight: 55.0 * 1.1, // 60.5
|
|
||||||
),
|
|
||||||
const SizedBox(height: 9.0 * 1.1), // 9.9
|
|
||||||
CustomAppTextField(
|
|
||||||
controller: _dobController,
|
|
||||||
labelText: widget.childData.isUnbornChild ? 'Date prévisionnelle de naissance' : 'Date de naissance',
|
|
||||||
hintText: 'JJ/MM/AAAA',
|
|
||||||
readOnly: true,
|
|
||||||
onTap: widget.onDateSelect,
|
|
||||||
suffixIcon: Icons.calendar_today,
|
|
||||||
fieldHeight: 55.0 * 1.1, // 60.5
|
|
||||||
),
|
|
||||||
const SizedBox(height: 11.0 * 1.1), // 12.1
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
AppCustomCheckbox(
|
|
||||||
label: 'Consentement photo',
|
|
||||||
value: widget.childData.photoConsent,
|
|
||||||
onChanged: widget.onTogglePhotoConsent,
|
|
||||||
checkboxSize: 22.0 * 1.1, // 24.2
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6.0 * 1.1), // 6.6
|
|
||||||
AppCustomCheckbox(
|
|
||||||
label: 'Naissance multiple',
|
|
||||||
value: widget.childData.multipleBirth,
|
|
||||||
onChanged: widget.onToggleMultipleBirth,
|
|
||||||
checkboxSize: 22.0 * 1.1, // 24.2
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (widget.canBeRemoved)
|
|
||||||
Positioned(
|
|
||||||
top: -5, right: -5,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: widget.onRemove,
|
|
||||||
customBorder: const CircleBorder(),
|
|
||||||
child: Image.asset(
|
|
||||||
'images/red_cross2.png',
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,247 +1,42 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:p_tits_pas/widgets/custom_decorated_text_field.dart'; // Import du nouveau widget
|
|
||||||
import 'dart:math' as math; // Pour la rotation du chevron
|
|
||||||
import 'package:p_tits_pas/widgets/app_custom_checkbox.dart'; // Import de la checkbox personnalisée
|
|
||||||
// import 'package:p_tits_pas/models/placeholder_registration_data.dart'; // Remplacé
|
|
||||||
import '../../models/user_registration_data.dart'; // Import du vrai modèle
|
|
||||||
import '../../utils/data_generator.dart'; // Import du générateur
|
|
||||||
import '../../models/card_assets.dart'; // Import des enums de cartes
|
|
||||||
import 'package:provider/provider.dart'; // Assurer l'import
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class ParentRegisterStep4Screen extends StatefulWidget {
|
import '../../models/user_registration_data.dart';
|
||||||
// final UserRegistrationData registrationData; // Supprimé
|
import '../../widgets/presentation_form_screen.dart';
|
||||||
|
import '../../models/card_assets.dart';
|
||||||
|
import '../../utils/data_generator.dart';
|
||||||
|
|
||||||
const ParentRegisterStep4Screen({super.key /*, required this.registrationData */}); // Modifié
|
class ParentRegisterStep4Screen extends StatelessWidget {
|
||||||
|
const ParentRegisterStep4Screen({super.key});
|
||||||
@override
|
|
||||||
_ParentRegisterStep4ScreenState createState() =>
|
|
||||||
_ParentRegisterStep4ScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ParentRegisterStep4ScreenState extends State<ParentRegisterStep4Screen> {
|
|
||||||
// late UserRegistrationData _registrationData; // Supprimé
|
|
||||||
final _motivationController = TextEditingController();
|
|
||||||
bool _cguAccepted = true; // Pour le test, CGU acceptées par défaut
|
|
||||||
final _bankNameController = TextEditingController();
|
|
||||||
final _ibanController = TextEditingController();
|
|
||||||
final _bicController = TextEditingController();
|
|
||||||
final _attestationController = TextEditingController();
|
|
||||||
bool _consentQuotientFamilial = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final registrationData = Provider.of<UserRegistrationData>(context, listen: false);
|
|
||||||
// _registrationData = registrationData; // Supprimé
|
|
||||||
_motivationController.text = registrationData.motivationText.isNotEmpty ? registrationData.motivationText : DataGenerator.motivation();
|
|
||||||
_bankNameController.text = registrationData.bankDetails?.bankName ?? '';
|
|
||||||
_ibanController.text = registrationData.bankDetails?.iban ?? '';
|
|
||||||
_bicController.text = registrationData.bankDetails?.bic ?? '';
|
|
||||||
_attestationController.text = registrationData.attestationCafNumber;
|
|
||||||
_consentQuotientFamilial = registrationData.consentQuotientFamilial;
|
|
||||||
_cguAccepted = registrationData.cguAccepted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_motivationController.dispose();
|
|
||||||
_bankNameController.dispose();
|
|
||||||
_ibanController.dispose();
|
|
||||||
_bicController.dispose();
|
|
||||||
_attestationController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showCGUModal() {
|
|
||||||
// Un long texte Lorem Ipsum pour simuler les CGU
|
|
||||||
const String loremIpsumText = '''
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.
|
|
||||||
|
|
||||||
Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna.
|
|
||||||
|
|
||||||
Aliquam convallis sollicitudin purus. Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet.
|
|
||||||
|
|
||||||
Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit.
|
|
||||||
|
|
||||||
Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna. Etiam et felis dolor.
|
|
||||||
|
|
||||||
Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
|
|
||||||
|
|
||||||
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
|
|
||||||
''';
|
|
||||||
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false, // L'utilisateur doit utiliser le bouton
|
|
||||||
builder: (BuildContext dialogContext) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
'Conditions Générales d\'Utilisation',
|
|
||||||
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
content: SizedBox(
|
|
||||||
width: MediaQuery.of(dialogContext).size.width * 0.7, // 70% de la largeur de l'écran
|
|
||||||
height: MediaQuery.of(dialogContext).size.height * 0.6, // 60% de la hauteur de l'écran
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Text(
|
|
||||||
loremIpsumText,
|
|
||||||
style: GoogleFonts.merienda(fontSize: 13),
|
|
||||||
textAlign: TextAlign.justify,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actionsPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
|
|
||||||
actionsAlignment: MainAxisAlignment.center,
|
|
||||||
actions: <Widget>[
|
|
||||||
ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(dialogContext).primaryColor,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Valider et Accepter',
|
|
||||||
style: GoogleFonts.merienda(fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(dialogContext).pop(); // Ferme la modale
|
|
||||||
setState(() {
|
|
||||||
_cguAccepted = true; // Met à jour l'état
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final registrationData = Provider.of<UserRegistrationData>(context, listen: false); // listen:false car on met à jour
|
final registrationData = Provider.of<UserRegistrationData>(context, listen: false);
|
||||||
final screenSize = MediaQuery.of(context).size;
|
|
||||||
final cardWidth = screenSize.width * 0.6; // Largeur de la carte (60% de l'écran)
|
|
||||||
final double imageAspectRatio = 2.0; // Ratio corrigé (1024/512 = 2.0)
|
|
||||||
final cardHeight = cardWidth / imageAspectRatio;
|
|
||||||
|
|
||||||
return Scaffold(
|
// Générer un texte de test si vide
|
||||||
body: Stack(
|
String initialText = registrationData.motivationText;
|
||||||
children: [
|
bool initialCgu = registrationData.cguAccepted;
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
if (initialText.isEmpty) {
|
||||||
),
|
initialText = DataGenerator.motivation();
|
||||||
Center(
|
initialCgu = true;
|
||||||
child: SingleChildScrollView(
|
}
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 50.0),
|
|
||||||
child: Column(
|
return PresentationFormScreen(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
stepText: 'Étape 4/5',
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
title: 'Motivation de votre demande',
|
||||||
children: [
|
cardColor: CardColorHorizontal.green,
|
||||||
Text(
|
textFieldHint: 'Écrivez ici pour motiver votre demande...',
|
||||||
'Étape 4/5',
|
initialText: initialText,
|
||||||
style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54),
|
initialCguAccepted: initialCgu,
|
||||||
),
|
previousRoute: '/parent-register-step3',
|
||||||
const SizedBox(height: 20),
|
onSubmit: (text, cguAccepted) {
|
||||||
Text(
|
registrationData.updateMotivation(text);
|
||||||
'Motivation de votre demande',
|
registrationData.acceptCGU(cguAccepted);
|
||||||
style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87),
|
// Les infos financières peuvent être gérées ailleurs si nécessaire
|
||||||
textAlign: TextAlign.center,
|
context.go('/parent-register-step5');
|
||||||
),
|
},
|
||||||
const SizedBox(height: 30),
|
|
||||||
Container(
|
|
||||||
width: cardWidth,
|
|
||||||
height: cardHeight,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: AssetImage(CardColorHorizontal.green.path),
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(40.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: CustomDecoratedTextField(
|
|
||||||
controller: _motivationController,
|
|
||||||
hintText: 'Écrivez ici pour motiver votre demande...',
|
|
||||||
fieldHeight: cardHeight * 0.6,
|
|
||||||
maxLines: 10,
|
|
||||||
expandDynamically: true,
|
|
||||||
fontSize: 18.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (!_cguAccepted) {
|
|
||||||
_showCGUModal();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: AppCustomCheckbox(
|
|
||||||
label: 'J\'accepte les conditions générales d\'utilisation',
|
|
||||||
value: _cguAccepted,
|
|
||||||
onChanged: (newValue) {
|
|
||||||
if (!_cguAccepted) {
|
|
||||||
_showCGUModal();
|
|
||||||
} else {
|
|
||||||
setState(() => _cguAccepted = false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Chevrons de navigation
|
|
||||||
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('/parent-register-step3');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Retour',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: screenSize.height / 2 - 20,
|
|
||||||
right: 40,
|
|
||||||
child: IconButton(
|
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
|
||||||
onPressed: _cguAccepted
|
|
||||||
? () {
|
|
||||||
registrationData.updateMotivation(_motivationController.text);
|
|
||||||
registrationData.acceptCGU(_cguAccepted);
|
|
||||||
registrationData.updateFinancialInfo(
|
|
||||||
bankDetails: BankDetails(
|
|
||||||
bankName: _bankNameController.text,
|
|
||||||
iban: _ibanController.text,
|
|
||||||
bic: _bicController.text,
|
|
||||||
),
|
|
||||||
attestationCafNumber: _attestationController.text,
|
|
||||||
consentQuotientFamilial: _consentQuotientFamilial,
|
|
||||||
);
|
|
||||||
context.go('/parent-register-step5');
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
tooltip: 'Suivant',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ class RegisterChoiceScreen extends StatelessWidget {
|
|||||||
iconPath: 'assets/images/icon_assmat.png',
|
iconPath: 'assets/images/icon_assmat.png',
|
||||||
label: 'Assistante Maternelle',
|
label: 'Assistante Maternelle',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.go('/nanny-register-step1');
|
context.go('/am-register-step1');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
202
frontend/lib/widgets/child_card_widget.dart
Normal file
202
frontend/lib/widgets/child_card_widget.dart
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'dart:io' show File;
|
||||||
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||||
|
import '../models/user_registration_data.dart';
|
||||||
|
import '../models/card_assets.dart';
|
||||||
|
import 'custom_app_text_field.dart';
|
||||||
|
import 'app_custom_checkbox.dart';
|
||||||
|
import 'hover_relief_widget.dart';
|
||||||
|
|
||||||
|
/// Widget pour afficher et éditer une carte enfant
|
||||||
|
/// Utilisé dans le workflow d'inscription des parents
|
||||||
|
class ChildCardWidget extends StatefulWidget {
|
||||||
|
final ChildData childData;
|
||||||
|
final int childIndex;
|
||||||
|
final VoidCallback onPickImage;
|
||||||
|
final VoidCallback onDateSelect;
|
||||||
|
final ValueChanged<String> onFirstNameChanged;
|
||||||
|
final ValueChanged<String> onLastNameChanged;
|
||||||
|
final ValueChanged<bool> onTogglePhotoConsent;
|
||||||
|
final ValueChanged<bool> onToggleMultipleBirth;
|
||||||
|
final ValueChanged<bool> onToggleIsUnborn;
|
||||||
|
final VoidCallback onRemove;
|
||||||
|
final bool canBeRemoved;
|
||||||
|
|
||||||
|
const ChildCardWidget({
|
||||||
|
required Key key,
|
||||||
|
required this.childData,
|
||||||
|
required this.childIndex,
|
||||||
|
required this.onPickImage,
|
||||||
|
required this.onDateSelect,
|
||||||
|
required this.onFirstNameChanged,
|
||||||
|
required this.onLastNameChanged,
|
||||||
|
required this.onTogglePhotoConsent,
|
||||||
|
required this.onToggleMultipleBirth,
|
||||||
|
required this.onToggleIsUnborn,
|
||||||
|
required this.onRemove,
|
||||||
|
required this.canBeRemoved,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChildCardWidget> createState() => _ChildCardWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChildCardWidgetState extends State<ChildCardWidget> {
|
||||||
|
late TextEditingController _firstNameController;
|
||||||
|
late TextEditingController _lastNameController;
|
||||||
|
late TextEditingController _dobController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
// Initialiser les contrôleurs avec les données du widget
|
||||||
|
_firstNameController = TextEditingController(text: widget.childData.firstName);
|
||||||
|
_lastNameController = TextEditingController(text: widget.childData.lastName);
|
||||||
|
_dobController = TextEditingController(text: widget.childData.dob);
|
||||||
|
|
||||||
|
// Ajouter des listeners pour mettre à jour les données sources via les callbacks
|
||||||
|
_firstNameController.addListener(() => widget.onFirstNameChanged(_firstNameController.text));
|
||||||
|
_lastNameController.addListener(() => widget.onLastNameChanged(_lastNameController.text));
|
||||||
|
// Pour dob, la mise à jour se fait via _selectDate, pas besoin de listener ici
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant ChildCardWidget oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
// Mettre à jour les contrôleurs si les données externes changent
|
||||||
|
// (peut arriver si on recharge l'état global)
|
||||||
|
if (widget.childData.firstName != _firstNameController.text) {
|
||||||
|
_firstNameController.text = widget.childData.firstName;
|
||||||
|
}
|
||||||
|
if (widget.childData.lastName != _lastNameController.text) {
|
||||||
|
_lastNameController.text = widget.childData.lastName;
|
||||||
|
}
|
||||||
|
if (widget.childData.dob != _dobController.text) {
|
||||||
|
_dobController.text = widget.childData.dob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_firstNameController.dispose();
|
||||||
|
_lastNameController.dispose();
|
||||||
|
_dobController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final File? currentChildImage = widget.childData.imageFile;
|
||||||
|
// Utiliser la couleur de la carte de childData pour l'ombre si besoin, ou directement pour le fond
|
||||||
|
final Color baseCardColorForShadow = widget.childData.cardColor == CardColorVertical.lavender
|
||||||
|
? Colors.purple.shade200
|
||||||
|
: (widget.childData.cardColor == CardColorVertical.pink ? Colors.pink.shade200 : Colors.grey.shade200); // Placeholder pour autres couleurs
|
||||||
|
final Color initialPhotoShadow = baseCardColorForShadow.withAlpha(90);
|
||||||
|
final Color hoverPhotoShadow = baseCardColorForShadow.withAlpha(130);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
width: 345.0 * 1.1, // 379.5
|
||||||
|
height: 570.0 * 1.2, // 684.0
|
||||||
|
padding: const EdgeInsets.all(22.0 * 1.1), // 24.2
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(image: AssetImage(widget.childData.cardColor.path), fit: BoxFit.cover),
|
||||||
|
borderRadius: BorderRadius.circular(20 * 1.1), // 22
|
||||||
|
),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
HoverReliefWidget(
|
||||||
|
onPressed: widget.onPickImage,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
initialShadowColor: initialPhotoShadow,
|
||||||
|
hoverShadowColor: hoverPhotoShadow,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 200.0,
|
||||||
|
width: 200.0,
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(5.0 * 1.1), // 5.5
|
||||||
|
child: currentChildImage != null
|
||||||
|
? ClipRRect(borderRadius: BorderRadius.circular(10 * 1.1), child: kIsWeb ? Image.network(currentChildImage.path, fit: BoxFit.cover) : Image.file(currentChildImage, fit: BoxFit.cover))
|
||||||
|
: Image.asset('assets/images/photo.png', fit: BoxFit.contain),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12.0 * 1.1), // Augmenté pour plus d'espace après la photo
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Enfant à naître ?', style: GoogleFonts.merienda(fontSize: 16 * 1.1, fontWeight: FontWeight.w600)),
|
||||||
|
Switch(value: widget.childData.isUnbornChild, onChanged: widget.onToggleIsUnborn, activeColor: Theme.of(context).primaryColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 9.0 * 1.1), // 9.9
|
||||||
|
CustomAppTextField(
|
||||||
|
controller: _firstNameController,
|
||||||
|
labelText: 'Prénom',
|
||||||
|
hintText: 'Facultatif si à naître',
|
||||||
|
isRequired: !widget.childData.isUnbornChild,
|
||||||
|
fieldHeight: 55.0 * 1.1, // 60.5
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6.0 * 1.1), // 6.6
|
||||||
|
CustomAppTextField(
|
||||||
|
controller: _lastNameController,
|
||||||
|
labelText: 'Nom',
|
||||||
|
hintText: 'Nom de l\'enfant',
|
||||||
|
enabled: true,
|
||||||
|
fieldHeight: 55.0 * 1.1, // 60.5
|
||||||
|
),
|
||||||
|
const SizedBox(height: 9.0 * 1.1), // 9.9
|
||||||
|
CustomAppTextField(
|
||||||
|
controller: _dobController,
|
||||||
|
labelText: widget.childData.isUnbornChild ? 'Date prévisionnelle de naissance' : 'Date de naissance',
|
||||||
|
hintText: 'JJ/MM/AAAA',
|
||||||
|
readOnly: true,
|
||||||
|
onTap: widget.onDateSelect,
|
||||||
|
suffixIcon: Icons.calendar_today,
|
||||||
|
fieldHeight: 55.0 * 1.1, // 60.5
|
||||||
|
),
|
||||||
|
const SizedBox(height: 11.0 * 1.1), // 12.1
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
AppCustomCheckbox(
|
||||||
|
label: 'Consentement photo',
|
||||||
|
value: widget.childData.photoConsent,
|
||||||
|
onChanged: widget.onTogglePhotoConsent,
|
||||||
|
checkboxSize: 22.0 * 1.1, // 24.2
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6.0 * 1.1), // 6.6
|
||||||
|
AppCustomCheckbox(
|
||||||
|
label: 'Naissance multiple',
|
||||||
|
value: widget.childData.multipleBirth,
|
||||||
|
onChanged: widget.onToggleMultipleBirth,
|
||||||
|
checkboxSize: 22.0 * 1.1, // 24.2
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (widget.canBeRemoved)
|
||||||
|
Positioned(
|
||||||
|
top: -5, right: -5,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: widget.onRemove,
|
||||||
|
customBorder: const CircleBorder(),
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/red_cross2.png',
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
405
frontend/lib/widgets/personal_info_form_screen.dart
Normal file
405
frontend/lib/widgets/personal_info_form_screen.dart
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
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_app_text_field.dart';
|
||||||
|
import 'app_custom_checkbox.dart';
|
||||||
|
import '../models/card_assets.dart';
|
||||||
|
|
||||||
|
/// Modèle de données pour le formulaire
|
||||||
|
class PersonalInfoData {
|
||||||
|
String firstName;
|
||||||
|
String lastName;
|
||||||
|
String phone;
|
||||||
|
String email;
|
||||||
|
String address;
|
||||||
|
String postalCode;
|
||||||
|
String city;
|
||||||
|
|
||||||
|
PersonalInfoData({
|
||||||
|
this.firstName = '',
|
||||||
|
this.lastName = '',
|
||||||
|
this.phone = '',
|
||||||
|
this.email = '',
|
||||||
|
this.address = '',
|
||||||
|
this.postalCode = '',
|
||||||
|
this.city = '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Widget générique pour les formulaires d'informations personnelles
|
||||||
|
class PersonalInfoFormScreen extends StatefulWidget {
|
||||||
|
final String stepText; // Ex: "Étape 1/5"
|
||||||
|
final String title; // Ex: "Informations du Parent Principal"
|
||||||
|
final CardColorHorizontal cardColor;
|
||||||
|
final PersonalInfoData initialData;
|
||||||
|
final Function(PersonalInfoData data, {bool? hasSecondPerson, bool? sameAddress}) onSubmit;
|
||||||
|
final String previousRoute;
|
||||||
|
|
||||||
|
// Options spécifiques pour Parent 2
|
||||||
|
final bool showSecondPersonToggle; // Afficher "Il y a un 2ème parent"
|
||||||
|
final bool? initialHasSecondPerson;
|
||||||
|
final bool showSameAddressCheckbox; // Afficher "Même adresse que parent 1"
|
||||||
|
final bool? initialSameAddress;
|
||||||
|
final PersonalInfoData? referenceAddressData; // Pour pré-remplir si "même adresse"
|
||||||
|
|
||||||
|
const PersonalInfoFormScreen({
|
||||||
|
super.key,
|
||||||
|
required this.stepText,
|
||||||
|
required this.title,
|
||||||
|
required this.cardColor,
|
||||||
|
required this.initialData,
|
||||||
|
required this.onSubmit,
|
||||||
|
required this.previousRoute,
|
||||||
|
this.showSecondPersonToggle = false,
|
||||||
|
this.initialHasSecondPerson,
|
||||||
|
this.showSameAddressCheckbox = false,
|
||||||
|
this.initialSameAddress,
|
||||||
|
this.referenceAddressData,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PersonalInfoFormScreen> createState() => _PersonalInfoFormScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PersonalInfoFormScreenState extends State<PersonalInfoFormScreen> {
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
late TextEditingController _lastNameController;
|
||||||
|
late TextEditingController _firstNameController;
|
||||||
|
late TextEditingController _phoneController;
|
||||||
|
late TextEditingController _emailController;
|
||||||
|
late TextEditingController _addressController;
|
||||||
|
late TextEditingController _postalCodeController;
|
||||||
|
late TextEditingController _cityController;
|
||||||
|
|
||||||
|
bool _hasSecondPerson = false;
|
||||||
|
bool _sameAddress = false;
|
||||||
|
bool _fieldsEnabled = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_lastNameController = TextEditingController(text: widget.initialData.lastName);
|
||||||
|
_firstNameController = TextEditingController(text: widget.initialData.firstName);
|
||||||
|
_phoneController = TextEditingController(text: widget.initialData.phone);
|
||||||
|
_emailController = TextEditingController(text: widget.initialData.email);
|
||||||
|
_addressController = TextEditingController(text: widget.initialData.address);
|
||||||
|
_postalCodeController = TextEditingController(text: widget.initialData.postalCode);
|
||||||
|
_cityController = TextEditingController(text: widget.initialData.city);
|
||||||
|
|
||||||
|
if (widget.showSecondPersonToggle) {
|
||||||
|
_hasSecondPerson = widget.initialHasSecondPerson ?? true;
|
||||||
|
_fieldsEnabled = _hasSecondPerson;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.showSameAddressCheckbox) {
|
||||||
|
_sameAddress = widget.initialSameAddress ?? false;
|
||||||
|
_updateAddressFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_lastNameController.dispose();
|
||||||
|
_firstNameController.dispose();
|
||||||
|
_phoneController.dispose();
|
||||||
|
_emailController.dispose();
|
||||||
|
_addressController.dispose();
|
||||||
|
_postalCodeController.dispose();
|
||||||
|
_cityController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateAddressFields() {
|
||||||
|
if (_sameAddress && widget.referenceAddressData != null) {
|
||||||
|
_addressController.text = widget.referenceAddressData!.address;
|
||||||
|
_postalCodeController.text = widget.referenceAddressData!.postalCode;
|
||||||
|
_cityController.text = widget.referenceAddressData!.city;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleSubmit() {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
final data = PersonalInfoData(
|
||||||
|
firstName: _firstNameController.text,
|
||||||
|
lastName: _lastNameController.text,
|
||||||
|
phone: _phoneController.text,
|
||||||
|
email: _emailController.text,
|
||||||
|
address: _addressController.text,
|
||||||
|
postalCode: _postalCodeController.text,
|
||||||
|
city: _cityController.text,
|
||||||
|
);
|
||||||
|
|
||||||
|
widget.onSubmit(
|
||||||
|
data,
|
||||||
|
hasSecondPerson: widget.showSecondPersonToggle ? _hasSecondPerson : null,
|
||||||
|
sameAddress: widget.showSameAddressCheckbox ? _sameAddress : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final screenSize = MediaQuery.of(context).size;
|
||||||
|
|
||||||
|
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),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(widget.stepText, style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
style: GoogleFonts.merienda(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Container(
|
||||||
|
width: screenSize.width * 0.6,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50),
|
||||||
|
constraints: const BoxConstraints(minHeight: 570),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(widget.cardColor.path),
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// Toggles "Ajouter Parent 2" et "Même Adresse" (uniquement pour Parent 2)
|
||||||
|
if (widget.showSecondPersonToggle) ...[
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 12,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.person_add_alt_1, size: 20),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
'Ajouter Parent 2 ?',
|
||||||
|
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Switch(
|
||||||
|
value: _hasSecondPerson,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_hasSecondPerson = value;
|
||||||
|
_fieldsEnabled = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
activeColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Expanded(flex: 1, child: SizedBox()),
|
||||||
|
if (widget.showSameAddressCheckbox)
|
||||||
|
Expanded(
|
||||||
|
flex: 12,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.home_work_outlined,
|
||||||
|
size: 20,
|
||||||
|
color: _fieldsEnabled ? null : Colors.grey,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
'Même Adresse ?',
|
||||||
|
style: GoogleFonts.merienda(
|
||||||
|
color: _fieldsEnabled ? null : Colors.grey,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Switch(
|
||||||
|
value: _sameAddress,
|
||||||
|
onChanged: _fieldsEnabled ? (value) {
|
||||||
|
setState(() {
|
||||||
|
_sameAddress = value ?? false;
|
||||||
|
_updateAddressFields();
|
||||||
|
});
|
||||||
|
} : null,
|
||||||
|
activeColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
],
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 12,
|
||||||
|
child: CustomAppTextField(
|
||||||
|
controller: _lastNameController,
|
||||||
|
labelText: 'Nom',
|
||||||
|
hintText: 'Votre nom de famille',
|
||||||
|
style: CustomAppTextFieldStyle.beige,
|
||||||
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
|
enabled: _fieldsEnabled,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Expanded(flex: 1, child: SizedBox()),
|
||||||
|
Expanded(
|
||||||
|
flex: 12,
|
||||||
|
child: CustomAppTextField(
|
||||||
|
controller: _firstNameController,
|
||||||
|
labelText: 'Prénom',
|
||||||
|
hintText: 'Votre prénom',
|
||||||
|
style: CustomAppTextFieldStyle.beige,
|
||||||
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
|
enabled: _fieldsEnabled,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 12,
|
||||||
|
child: CustomAppTextField(
|
||||||
|
controller: _phoneController,
|
||||||
|
labelText: 'Téléphone',
|
||||||
|
keyboardType: TextInputType.phone,
|
||||||
|
hintText: 'Votre numéro de téléphone',
|
||||||
|
style: CustomAppTextFieldStyle.beige,
|
||||||
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
|
enabled: _fieldsEnabled,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Expanded(flex: 1, child: SizedBox()),
|
||||||
|
Expanded(
|
||||||
|
flex: 12,
|
||||||
|
child: CustomAppTextField(
|
||||||
|
controller: _emailController,
|
||||||
|
labelText: 'Email',
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
hintText: 'Votre adresse e-mail',
|
||||||
|
style: CustomAppTextFieldStyle.beige,
|
||||||
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
|
enabled: _fieldsEnabled,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
CustomAppTextField(
|
||||||
|
controller: _addressController,
|
||||||
|
labelText: 'Adresse (N° et Rue)',
|
||||||
|
hintText: 'Numéro et nom de votre rue',
|
||||||
|
style: CustomAppTextFieldStyle.beige,
|
||||||
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
|
enabled: _fieldsEnabled && !_sameAddress,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: CustomAppTextField(
|
||||||
|
controller: _postalCodeController,
|
||||||
|
labelText: 'Code Postal',
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
hintText: 'Code postal',
|
||||||
|
style: CustomAppTextFieldStyle.beige,
|
||||||
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
|
enabled: _fieldsEnabled && !_sameAddress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Expanded(flex: 1, child: SizedBox()),
|
||||||
|
Expanded(
|
||||||
|
flex: 4,
|
||||||
|
child: CustomAppTextField(
|
||||||
|
controller: _cityController,
|
||||||
|
labelText: 'Ville',
|
||||||
|
hintText: 'Votre ville',
|
||||||
|
style: CustomAppTextFieldStyle.beige,
|
||||||
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
|
enabled: _fieldsEnabled && !_sameAddress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Chevrons
|
||||||
|
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',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: screenSize.height / 2 - 20,
|
||||||
|
right: 40,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||||
|
onPressed: _handleSubmit,
|
||||||
|
tooltip: 'Suivant',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
169
frontend/lib/widgets/presentation_form_screen.dart
Normal file
169
frontend/lib/widgets/presentation_form_screen.dart
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
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',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,62 +1,99 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'dart:io'; // Pour FileImage si _pickPhoto utilise un File
|
import 'dart:io';
|
||||||
|
import '../models/card_assets.dart';
|
||||||
|
import 'custom_app_text_field.dart';
|
||||||
|
import 'app_custom_checkbox.dart';
|
||||||
|
import 'hover_relief_widget.dart';
|
||||||
|
|
||||||
import '../../../models/nanny_registration_data.dart';
|
/// Données pour le formulaire d'informations professionnelles
|
||||||
import '../../../widgets/custom_app_text_field.dart';
|
class ProfessionalInfoData {
|
||||||
import '../../../widgets/app_custom_checkbox.dart'; // Import de la checkbox
|
final String? photoPath;
|
||||||
import '../../../widgets/hover_relief_widget.dart'; // Import du HoverReliefWidget
|
final File? photoFile;
|
||||||
import '../../../models/card_assets.dart';
|
final bool photoConsent;
|
||||||
// import '../../../utils/data_generator.dart'; // Plus besoin pour l'initialisation directe ici
|
final DateTime? dateOfBirth;
|
||||||
|
final String birthCity;
|
||||||
|
final String birthCountry;
|
||||||
|
final String nir;
|
||||||
|
final String agrementNumber;
|
||||||
|
final int? capacity;
|
||||||
|
|
||||||
class NannyRegisterStep2Screen extends StatefulWidget {
|
ProfessionalInfoData({
|
||||||
const NannyRegisterStep2Screen({super.key});
|
this.photoPath,
|
||||||
|
this.photoFile,
|
||||||
@override
|
this.photoConsent = false,
|
||||||
State<NannyRegisterStep2Screen> createState() => _NannyRegisterStep2ScreenState();
|
this.dateOfBirth,
|
||||||
|
this.birthCity = '',
|
||||||
|
this.birthCountry = '',
|
||||||
|
this.nir = '',
|
||||||
|
this.agrementNumber = '',
|
||||||
|
this.capacity,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
/// Widget générique pour le formulaire d'informations professionnelles
|
||||||
|
/// Utilisé pour l'inscription des Assistantes Maternelles
|
||||||
|
class ProfessionalInfoFormScreen extends StatefulWidget {
|
||||||
|
final String stepText;
|
||||||
|
final String title;
|
||||||
|
final CardColorHorizontal cardColor;
|
||||||
|
final ProfessionalInfoData? initialData;
|
||||||
|
final String previousRoute;
|
||||||
|
final Function(ProfessionalInfoData) onSubmit;
|
||||||
|
final Future<void> Function()? onPickPhoto;
|
||||||
|
|
||||||
|
const ProfessionalInfoFormScreen({
|
||||||
|
super.key,
|
||||||
|
required this.stepText,
|
||||||
|
required this.title,
|
||||||
|
required this.cardColor,
|
||||||
|
this.initialData,
|
||||||
|
required this.previousRoute,
|
||||||
|
required this.onSubmit,
|
||||||
|
this.onPickPhoto,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProfessionalInfoFormScreen> createState() => _ProfessionalInfoFormScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
final _dateOfBirthController = TextEditingController();
|
final _dateOfBirthController = TextEditingController();
|
||||||
// final _placeOfBirthController = TextEditingController(); // Remplacé
|
final _birthCityController = TextEditingController();
|
||||||
final _birthCityController = TextEditingController(); // Nouveau
|
final _birthCountryController = TextEditingController();
|
||||||
final _birthCountryController = TextEditingController(); // Nouveau
|
|
||||||
final _nirController = TextEditingController();
|
final _nirController = TextEditingController();
|
||||||
final _agrementController = TextEditingController();
|
final _agrementController = TextEditingController();
|
||||||
final _capacityController = TextEditingController();
|
final _capacityController = TextEditingController();
|
||||||
|
|
||||||
DateTime? _selectedDate;
|
DateTime? _selectedDate;
|
||||||
String? _photoPathFramework; // Pour stocker le chemin de la photo (Asset ou File path)
|
String? _photoPathFramework;
|
||||||
File? _photoFile; // Pour stocker le fichier image si sélectionné localement
|
File? _photoFile;
|
||||||
bool _photoConsent = false;
|
bool _photoConsent = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final data = Provider.of<NannyRegistrationData>(context, listen: false);
|
|
||||||
_selectedDate = data.dateOfBirth;
|
final data = widget.initialData;
|
||||||
_dateOfBirthController.text = data.dateOfBirth != null ? DateFormat('dd/MM/yyyy').format(data.dateOfBirth!) : '';
|
if (data != null) {
|
||||||
_birthCityController.text = data.birthCity;
|
_selectedDate = data.dateOfBirth;
|
||||||
_birthCountryController.text = data.birthCountry;
|
_dateOfBirthController.text = data.dateOfBirth != null
|
||||||
_nirController.text = data.nir;
|
? DateFormat('dd/MM/yyyy').format(data.dateOfBirth!)
|
||||||
_agrementController.text = data.agrementNumber;
|
: '';
|
||||||
_capacityController.text = data.capacity?.toString() ?? '';
|
_birthCityController.text = data.birthCity;
|
||||||
// Gérer la photo existante (pourrait être un path d'asset ou un path de fichier)
|
_birthCountryController.text = data.birthCountry;
|
||||||
if (data.photoPath != null) {
|
_nirController.text = data.nir;
|
||||||
if (data.photoPath!.startsWith('assets/')) {
|
_agrementController.text = data.agrementNumber;
|
||||||
_photoPathFramework = data.photoPath;
|
_capacityController.text = data.capacity?.toString() ?? '';
|
||||||
_photoFile = null;
|
_photoPathFramework = data.photoPath;
|
||||||
} else {
|
_photoFile = data.photoFile;
|
||||||
_photoFile = File(data.photoPath!);
|
_photoConsent = data.photoConsent;
|
||||||
_photoPathFramework = data.photoPath; // ou _photoFile.path
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_photoConsent = data.photoConsent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -73,9 +110,9 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
Future<void> _selectDate(BuildContext context) async {
|
Future<void> _selectDate(BuildContext context) async {
|
||||||
final DateTime? picked = await showDatePicker(
|
final DateTime? picked = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: _selectedDate ?? DateTime.now().subtract(const Duration(days: 365 * 25)), // Default à 25 ans si null
|
initialDate: _selectedDate ?? DateTime.now().subtract(const Duration(days: 365 * 25)),
|
||||||
firstDate: DateTime(1920, 1),
|
firstDate: DateTime(1920, 1),
|
||||||
lastDate: DateTime.now().subtract(const Duration(days: 365 * 18)), // Assurer un âge minimum de 18 ans
|
lastDate: DateTime.now().subtract(const Duration(days: 365 * 18)),
|
||||||
locale: const Locale('fr', 'FR'),
|
locale: const Locale('fr', 'FR'),
|
||||||
);
|
);
|
||||||
if (picked != null && picked != _selectedDate) {
|
if (picked != null && picked != _selectedDate) {
|
||||||
@ -87,22 +124,15 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pickPhoto() async {
|
Future<void> _pickPhoto() async {
|
||||||
// TODO: Remplacer par la vraie logique ImagePicker
|
if (widget.onPickPhoto != null) {
|
||||||
// final imagePicker = ImagePicker();
|
await widget.onPickPhoto!();
|
||||||
// final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);
|
} else {
|
||||||
// if (pickedFile != null) {
|
// Comportement par défaut : utiliser un asset de test
|
||||||
// setState(() {
|
setState(() {
|
||||||
// _photoFile = File(pickedFile.path);
|
_photoPathFramework = 'assets/images/icon_assmat.png';
|
||||||
// _photoPathFramework = pickedFile.path; // pour la sauvegarde
|
_photoFile = null;
|
||||||
// });
|
});
|
||||||
// } else {
|
}
|
||||||
// // Simuler la sélection d'un asset pour test si aucun fichier n'est choisi
|
|
||||||
setState(() {
|
|
||||||
_photoPathFramework = 'assets/images/icon_assmat.png'; // Simule une photo asset
|
|
||||||
_photoFile = null; // Assurez-vous que _photoFile est null si c'est un asset
|
|
||||||
});
|
|
||||||
// }
|
|
||||||
print("Photo sélectionnée: $_photoPathFramework");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _submitForm() {
|
void _submitForm() {
|
||||||
@ -114,25 +144,25 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Provider.of<NannyRegistrationData>(context, listen: false)
|
final data = ProfessionalInfoData(
|
||||||
.updateProfessionalInfo(
|
photoPath: _photoPathFramework,
|
||||||
photoPath: _photoPathFramework, // Sauvegarder le chemin (asset ou fichier)
|
photoFile: _photoFile,
|
||||||
photoConsent: _photoConsent,
|
photoConsent: _photoConsent,
|
||||||
dateOfBirth: _selectedDate,
|
dateOfBirth: _selectedDate,
|
||||||
birthCity: _birthCityController.text,
|
birthCity: _birthCityController.text,
|
||||||
birthCountry: _birthCountryController.text,
|
birthCountry: _birthCountryController.text,
|
||||||
nir: _nirController.text,
|
nir: _nirController.text,
|
||||||
agrementNumber: _agrementController.text,
|
agrementNumber: _agrementController.text,
|
||||||
capacity: int.tryParse(_capacityController.text)
|
capacity: int.tryParse(_capacityController.text),
|
||||||
);
|
);
|
||||||
context.go('/nanny-register-step3');
|
|
||||||
|
widget.onSubmit(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenSize = MediaQuery.of(context).size;
|
final screenSize = MediaQuery.of(context).size;
|
||||||
const cardColor = CardColorHorizontal.green; // Couleur de la carte
|
|
||||||
final Color baseCardColorForShadow = Colors.green.shade300;
|
final Color baseCardColorForShadow = Colors.green.shade300;
|
||||||
final Color initialPhotoShadow = baseCardColorForShadow.withAlpha(90);
|
final Color initialPhotoShadow = baseCardColorForShadow.withAlpha(90);
|
||||||
final Color hoverPhotoShadow = baseCardColorForShadow.withAlpha(130);
|
final Color hoverPhotoShadow = baseCardColorForShadow.withAlpha(130);
|
||||||
@ -156,36 +186,38 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text('Étape 2/4', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
Text(widget.stepText, style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
style: GoogleFonts.merienda(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
Container(
|
Container(
|
||||||
width: screenSize.width * 0.7, // Largeur de la carte
|
width: screenSize.width * 0.6,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 50),
|
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50),
|
||||||
constraints: const BoxConstraints(minHeight: 650), // Hauteur minimale ajustée
|
constraints: const BoxConstraints(minHeight: 650),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(image: AssetImage(cardColor.path), fit: BoxFit.fill),
|
image: DecorationImage(image: AssetImage(widget.cardColor.path), fit: BoxFit.fill),
|
||||||
),
|
),
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
'Vos informations professionnelles',
|
|
||||||
style: GoogleFonts.merienda(
|
|
||||||
fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87, // Couleur du titre ajustée
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// Colonne Gauche: Photo et Checkbox
|
// Colonne Gauche: Photo et Checkbox
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 300, // Largeur fixe pour la colonne photo (200 * 1.5)
|
width: 300,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center, // Centrer les éléments horizontalement
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
HoverReliefWidget(
|
HoverReliefWidget(
|
||||||
onPressed: _pickPhoto,
|
onPressed: _pickPhoto,
|
||||||
@ -193,8 +225,8 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
initialShadowColor: initialPhotoShadow,
|
initialShadowColor: initialPhotoShadow,
|
||||||
hoverShadowColor: hoverPhotoShadow,
|
hoverShadowColor: hoverPhotoShadow,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 270, // (180 * 1.5)
|
height: 270,
|
||||||
width: 270, // (180 * 1.5)
|
width: 270,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
@ -208,16 +240,16 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10), // Espace réduit
|
const SizedBox(height: 10),
|
||||||
AppCustomCheckbox(
|
AppCustomCheckbox(
|
||||||
label: 'J\'accepte l\'utilisation de ma photo.',
|
label: 'J\'accepte l\'utilisation\nde ma photo.',
|
||||||
value: _photoConsent,
|
value: _photoConsent,
|
||||||
onChanged: (val) => setState(() => _photoConsent = val ?? false),
|
onChanged: (val) => setState(() => _photoConsent = val ?? false),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 30), // Augmenter l'espace entre les colonnes
|
const SizedBox(width: 30),
|
||||||
// Colonne Droite: Champs de naissance
|
// Colonne Droite: Champs de naissance
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -227,25 +259,31 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
labelText: 'Ville de naissance',
|
labelText: 'Ville de naissance',
|
||||||
hintText: 'Votre ville de naissance',
|
hintText: 'Votre ville de naissance',
|
||||||
fieldWidth: double.infinity,
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
validator: (v) => v!.isEmpty ? 'Ville requise' : null,
|
validator: (v) => v!.isEmpty ? 'Ville requise' : null,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 32),
|
||||||
CustomAppTextField(
|
CustomAppTextField(
|
||||||
controller: _birthCountryController,
|
controller: _birthCountryController,
|
||||||
labelText: 'Pays de naissance',
|
labelText: 'Pays de naissance',
|
||||||
hintText: 'Votre pays de naissance',
|
hintText: 'Votre pays de naissance',
|
||||||
fieldWidth: double.infinity,
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
validator: (v) => v!.isEmpty ? 'Pays requis' : null,
|
validator: (v) => v!.isEmpty ? 'Pays requis' : null,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 32),
|
||||||
CustomAppTextField(
|
CustomAppTextField(
|
||||||
controller: _dateOfBirthController,
|
controller: _dateOfBirthController,
|
||||||
labelText: 'Date de naissance',
|
labelText: 'Date de naissance',
|
||||||
hintText: 'JJ/MM/AAAA',
|
hintText: 'JJ/MM/AAAA',
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
onTap: () => _selectDate(context),
|
onTap: () => _selectDate(context),
|
||||||
suffixIcon: Icons.calendar_today, // Assurez-vous que CustomAppTextField gère suffixIcon
|
suffixIcon: Icons.calendar_today,
|
||||||
fieldWidth: double.infinity,
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
validator: (v) => _selectedDate == null ? 'Date requise' : null,
|
validator: (v) => _selectedDate == null ? 'Date requise' : null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -253,22 +291,23 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 32),
|
||||||
CustomAppTextField(
|
CustomAppTextField(
|
||||||
controller: _nirController,
|
controller: _nirController,
|
||||||
labelText: 'N° Sécurité Sociale (NIR)',
|
labelText: 'N° Sécurité Sociale (NIR)',
|
||||||
hintText: 'Votre NIR à 13 chiffres',
|
hintText: 'Votre NIR à 13 chiffres',
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
fieldWidth: double.infinity,
|
fieldWidth: double.infinity,
|
||||||
validator: (v) { // Validation plus précise du NIR
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
|
validator: (v) {
|
||||||
if (v == null || v.isEmpty) return 'NIR requis';
|
if (v == null || v.isEmpty) return 'NIR requis';
|
||||||
if (v.length != 13) return 'Le NIR doit contenir 13 chiffres';
|
if (v.length != 13) return 'Le NIR doit contenir 13 chiffres';
|
||||||
if (!RegExp(r'^[1-3]').hasMatch(v[0])) return 'Le NIR doit commencer par 1, 2 ou 3';
|
if (!RegExp(r'^[1-3]').hasMatch(v[0])) return 'Le NIR doit commencer par 1, 2 ou 3';
|
||||||
// D'autres validations plus complexes (clé de contrôle) peuvent être ajoutées
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 32),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -277,6 +316,8 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
labelText: 'N° d\'agrément',
|
labelText: 'N° d\'agrément',
|
||||||
hintText: 'Votre numéro d\'agrément',
|
hintText: 'Votre numéro d\'agrément',
|
||||||
fieldWidth: double.infinity,
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
validator: (v) => v!.isEmpty ? 'Agrément requis' : null,
|
validator: (v) => v!.isEmpty ? 'Agrément requis' : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -288,6 +329,8 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
hintText: 'Ex: 3',
|
hintText: 'Ex: 3',
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
fieldWidth: double.infinity,
|
fieldWidth: double.infinity,
|
||||||
|
labelFontSize: 22.0,
|
||||||
|
inputFontSize: 20.0,
|
||||||
validator: (v) {
|
validator: (v) {
|
||||||
if (v == null || v.isEmpty) return 'Capacité requise';
|
if (v == null || v.isEmpty) return 'Capacité requise';
|
||||||
final n = int.tryParse(v);
|
final n = int.tryParse(v);
|
||||||
@ -311,12 +354,16 @@ class _NannyRegisterStep2ScreenState extends State<NannyRegisterStep2Screen> {
|
|||||||
top: screenSize.height / 2 - 20,
|
top: screenSize.height / 2 - 20,
|
||||||
left: 40,
|
left: 40,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: Transform(alignment: Alignment.center, transform: Matrix4.rotationY(math.pi), child: Image.asset('assets/images/chevron_right.png', height: 40)),
|
icon: Transform(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
transform: Matrix4.rotationY(math.pi),
|
||||||
|
child: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||||
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (context.canPop()) {
|
if (context.canPop()) {
|
||||||
context.pop();
|
context.pop();
|
||||||
} else {
|
} else {
|
||||||
context.go('/nanny-register-step1');
|
context.go(widget.previousRoute);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tooltip: 'Précédent',
|
tooltip: 'Précédent',
|
||||||
248
frontend/lib/widgets/summary_screen.dart
Normal file
248
frontend/lib/widgets/summary_screen.dart
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
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 '../models/card_assets.dart';
|
||||||
|
import 'image_button.dart';
|
||||||
|
|
||||||
|
/// Widget générique pour afficher un écran de récapitulatif
|
||||||
|
/// Utilisé pour le récapitulatif d'inscription Parents et AM
|
||||||
|
class SummaryScreen extends StatelessWidget {
|
||||||
|
final String stepText;
|
||||||
|
final String title;
|
||||||
|
final List<Widget> summaryCards;
|
||||||
|
final String previousRoute;
|
||||||
|
final VoidCallback onSubmit;
|
||||||
|
final String submitButtonText;
|
||||||
|
|
||||||
|
const SummaryScreen({
|
||||||
|
super.key,
|
||||||
|
required this.stepText,
|
||||||
|
required this.title,
|
||||||
|
required this.summaryCards,
|
||||||
|
required this.previousRoute,
|
||||||
|
required this.onSubmit,
|
||||||
|
this.submitButtonText = 'Soumettre ma demande',
|
||||||
|
});
|
||||||
|
|
||||||
|
void _showConfirmationModal(BuildContext context) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||||
|
backgroundColor: const Color(0xFFF4F1DE),
|
||||||
|
title: Text('Demande envoyée !', style: GoogleFonts.merienda(fontSize: 20, fontWeight: FontWeight.bold, color: const Color(0xFF2D6A4F)), textAlign: TextAlign.center),
|
||||||
|
content: Text('Votre demande a bien été enregistrée. Nous reviendrons vers vous prochainement.', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black87), textAlign: TextAlign.center),
|
||||||
|
actions: [
|
||||||
|
Center(
|
||||||
|
child: ImageButton(
|
||||||
|
bg: 'assets/images/btn_green.png',
|
||||||
|
text: 'OK',
|
||||||
|
textColor: const Color(0xFF2D6A4F),
|
||||||
|
width: 150,
|
||||||
|
height: 40,
|
||||||
|
fontSize: 16,
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(ctx).pop();
|
||||||
|
context.go('/');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final screenSize = MediaQuery.of(context).size;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeatY),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 40.0),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: screenSize.width / 4.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(stepText, style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text(title, style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), textAlign: TextAlign.center),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
// Cartes de récapitulatif passées en paramètre
|
||||||
|
...summaryCards.map((card) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 20),
|
||||||
|
child: card,
|
||||||
|
)),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
ImageButton(
|
||||||
|
bg: 'assets/images/btn_green.png',
|
||||||
|
text: submitButtonText,
|
||||||
|
textColor: const Color(0xFF2D6A4F),
|
||||||
|
width: 350,
|
||||||
|
height: 50,
|
||||||
|
fontSize: 18,
|
||||||
|
onPressed: () {
|
||||||
|
onSubmit();
|
||||||
|
_showConfirmationModal(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 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(previousRoute);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: 'Retour',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper widget pour créer une carte de récapitulatif avec un titre et un contenu
|
||||||
|
class SummaryCard extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String backgroundImagePath;
|
||||||
|
final List<Widget> content;
|
||||||
|
final VoidCallback? onEdit;
|
||||||
|
|
||||||
|
const SummaryCard({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.backgroundImagePath,
|
||||||
|
required this.content,
|
||||||
|
this.onEdit,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: 2.0,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 25.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(backgroundImagePath),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: GoogleFonts.merienda(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: content,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (onEdit != null) ...[
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Image.asset('assets/images/input_field_bg.png', height: 35),
|
||||||
|
tooltip: 'Modifier',
|
||||||
|
onPressed: onEdit,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fonction helper pour afficher un champ de type "lecture seule" stylisé
|
||||||
|
Widget buildDisplayFieldValue(
|
||||||
|
BuildContext context,
|
||||||
|
String label,
|
||||||
|
String value, {
|
||||||
|
bool multiLine = false,
|
||||||
|
double fieldHeight = 50.0,
|
||||||
|
double labelFontSize = 18.0,
|
||||||
|
}) {
|
||||||
|
const FontWeight labelFontWeight = FontWeight.w600;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(label, style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: labelFontWeight)),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: multiLine ? null : fieldHeight,
|
||||||
|
constraints: multiLine ? const BoxConstraints(minHeight: 50.0) : null,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage('assets/images/input_field_bg.png'),
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
value.isNotEmpty ? value : '-',
|
||||||
|
style: GoogleFonts.merienda(fontSize: labelFontSize),
|
||||||
|
maxLines: multiLine ? null : 1,
|
||||||
|
overflow: multiLine ? TextOverflow.visible : TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user