fix(am): ordre Tab formulaire pro et chevrons accessibles
- Ordre de focus explicite (NumericFocusOrder) pour l’inscription AM étape 2 : chaîne des champs jusqu’aux chevrons, Pays → NIR, places → Suivant (10) puis Précédent (11). - FocusTraversalGroup avec OrderedTraversalPolicy sur le Scaffold pour respecter cet ordre (desktop / web). - Champ pays : TextInputAction.next, onFieldSubmitted et onEditingComplete vers le NIR ; CustomAppTextField expose onEditingComplete. - NirTextField : focusNode et focusTraversalOrder optionnels. Made-with: Cursor
This commit is contained in:
parent
d550e57cb8
commit
a9ce4a6756
@ -33,6 +33,8 @@ class CustomAppTextField extends StatefulWidget {
|
|||||||
final Iterable<String>? autofillHints;
|
final Iterable<String>? autofillHints;
|
||||||
final TextInputAction? textInputAction;
|
final TextInputAction? textInputAction;
|
||||||
final ValueChanged<String>? onFieldSubmitted;
|
final ValueChanged<String>? onFieldSubmitted;
|
||||||
|
/// Souvent appelé par la touche « suivant » du clavier (flèche), là où [onFieldSubmitted] ne l’est pas.
|
||||||
|
final VoidCallback? onEditingComplete;
|
||||||
final List<TextInputFormatter>? inputFormatters;
|
final List<TextInputFormatter>? inputFormatters;
|
||||||
final bool autocorrect;
|
final bool autocorrect;
|
||||||
final bool enableSuggestions;
|
final bool enableSuggestions;
|
||||||
@ -62,6 +64,7 @@ class CustomAppTextField extends StatefulWidget {
|
|||||||
this.autofillHints,
|
this.autofillHints,
|
||||||
this.textInputAction,
|
this.textInputAction,
|
||||||
this.onFieldSubmitted,
|
this.onFieldSubmitted,
|
||||||
|
this.onEditingComplete,
|
||||||
this.inputFormatters,
|
this.inputFormatters,
|
||||||
this.autocorrect = true,
|
this.autocorrect = true,
|
||||||
this.enableSuggestions = true,
|
this.enableSuggestions = true,
|
||||||
@ -144,6 +147,7 @@ class _CustomAppTextFieldState extends State<CustomAppTextField> {
|
|||||||
autofillHints: widget.autofillHints,
|
autofillHints: widget.autofillHints,
|
||||||
textInputAction: widget.textInputAction,
|
textInputAction: widget.textInputAction,
|
||||||
onFieldSubmitted: widget.onFieldSubmitted,
|
onFieldSubmitted: widget.onFieldSubmitted,
|
||||||
|
onEditingComplete: widget.onEditingComplete,
|
||||||
enabled: widget.enabled,
|
enabled: widget.enabled,
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
|
|||||||
@ -8,6 +8,9 @@ import 'custom_app_text_field.dart';
|
|||||||
/// La valeur envoyée au [controller] est formatée ; utiliser [normalizeNir](controller.text) à la soumission.
|
/// La valeur envoyée au [controller] est formatée ; utiliser [normalizeNir](controller.text) à la soumission.
|
||||||
class NirTextField extends StatelessWidget {
|
class NirTextField extends StatelessWidget {
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
|
final FocusNode? focusNode;
|
||||||
|
/// Si non null, impose l’ordre Tab parmi les descendants (ex. après « Pays de naissance »).
|
||||||
|
final double? focusTraversalOrder;
|
||||||
final String labelText;
|
final String labelText;
|
||||||
final String hintText;
|
final String hintText;
|
||||||
final String? Function(String?)? validator;
|
final String? Function(String?)? validator;
|
||||||
@ -23,6 +26,8 @@ class NirTextField extends StatelessWidget {
|
|||||||
const NirTextField({
|
const NirTextField({
|
||||||
super.key,
|
super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
|
this.focusNode,
|
||||||
|
this.focusTraversalOrder,
|
||||||
this.labelText = 'N° Sécurité Sociale (NIR)',
|
this.labelText = 'N° Sécurité Sociale (NIR)',
|
||||||
this.hintText = '13 chiffres + clé',
|
this.hintText = '13 chiffres + clé',
|
||||||
this.validator,
|
this.validator,
|
||||||
@ -38,8 +43,9 @@ class NirTextField extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CustomAppTextField(
|
final field = CustomAppTextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
focusNode: focusNode,
|
||||||
labelText: labelText,
|
labelText: labelText,
|
||||||
hintText: hintText,
|
hintText: hintText,
|
||||||
fieldWidth: fieldWidth,
|
fieldWidth: fieldWidth,
|
||||||
@ -54,5 +60,13 @@ class NirTextField extends StatelessWidget {
|
|||||||
style: style,
|
style: style,
|
||||||
labelFieldSpacing: labelFieldSpacing,
|
labelFieldSpacing: labelFieldSpacing,
|
||||||
);
|
);
|
||||||
|
final o = focusTraversalOrder;
|
||||||
|
if (o != null) {
|
||||||
|
return FocusTraversalOrder(
|
||||||
|
order: NumericFocusOrder(o),
|
||||||
|
child: field,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,7 +89,20 @@ class ProfessionalInfoFormScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen> {
|
class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
/// Ordre Tab explicite : sans cela, la zone photo (InkWell) peut s’insérer entre pays et NIR.
|
||||||
|
static const double _kTabPhotoPick = 1;
|
||||||
|
static const double _kTabDateBirth = 2;
|
||||||
|
static const double _kTabBirthCity = 3;
|
||||||
|
static const double _kTabBirthCountry = 4;
|
||||||
|
static const double _kTabNir = 5;
|
||||||
|
static const double _kTabAgrement = 6;
|
||||||
|
static const double _kTabAgreementDate = 7;
|
||||||
|
static const double _kTabCapacity = 8;
|
||||||
|
static const double _kTabPlaces = 9;
|
||||||
|
static const double _kTabChevronNext = 10;
|
||||||
|
static const double _kTabChevronPrev = 11;
|
||||||
|
|
||||||
final _dateOfBirthController = TextEditingController();
|
final _dateOfBirthController = TextEditingController();
|
||||||
final _birthCityController = TextEditingController();
|
final _birthCityController = TextEditingController();
|
||||||
final _birthCountryController = TextEditingController();
|
final _birthCountryController = TextEditingController();
|
||||||
@ -101,7 +114,8 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
|
|
||||||
FocusNode? _birthCityFocus;
|
FocusNode? _birthCityFocus;
|
||||||
FocusNode? _birthCountryFocus;
|
FocusNode? _birthCountryFocus;
|
||||||
|
FocusNode? _nirFocus;
|
||||||
|
|
||||||
DateTime? _selectedDate;
|
DateTime? _selectedDate;
|
||||||
DateTime? _selectedAgreementDate;
|
DateTime? _selectedAgreementDate;
|
||||||
String? _photoPathFramework;
|
String? _photoPathFramework;
|
||||||
@ -148,6 +162,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
if (widget.mode == DisplayMode.editable) {
|
if (widget.mode == DisplayMode.editable) {
|
||||||
_birthCityFocus = FocusNode();
|
_birthCityFocus = FocusNode();
|
||||||
_birthCountryFocus = FocusNode();
|
_birthCountryFocus = FocusNode();
|
||||||
|
_nirFocus = FocusNode();
|
||||||
_birthCityFocus!.addListener(_onBirthCityFocusChange);
|
_birthCityFocus!.addListener(_onBirthCityFocusChange);
|
||||||
_birthCountryFocus!.addListener(_onBirthCountryFocusChange);
|
_birthCountryFocus!.addListener(_onBirthCountryFocusChange);
|
||||||
}
|
}
|
||||||
@ -178,6 +193,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
_birthCountryFocus?.removeListener(_onBirthCountryFocusChange);
|
_birthCountryFocus?.removeListener(_onBirthCountryFocusChange);
|
||||||
_birthCityFocus?.dispose();
|
_birthCityFocus?.dispose();
|
||||||
_birthCountryFocus?.dispose();
|
_birthCountryFocus?.dispose();
|
||||||
|
_nirFocus?.dispose();
|
||||||
_dateOfBirthController.dispose();
|
_dateOfBirthController.dispose();
|
||||||
_birthCityController.dispose();
|
_birthCityController.dispose();
|
||||||
_birthCountryController.dispose();
|
_birthCountryController.dispose();
|
||||||
@ -337,82 +353,89 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
return _buildCard(context, config, screenSize);
|
return _buildCard(context, config, screenSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return FocusTraversalGroup(
|
||||||
body: Stack(
|
policy: OrderedTraversalPolicy(),
|
||||||
children: [
|
child: Scaffold(
|
||||||
Positioned.fill(
|
body: Stack(
|
||||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
children: [
|
||||||
),
|
Positioned.fill(
|
||||||
Center(
|
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
||||||
child: SingleChildScrollView(
|
),
|
||||||
padding: EdgeInsets.symmetric(vertical: config.isMobile ? 40.0 : 28.0),
|
Center(
|
||||||
child: Column(
|
child: SingleChildScrollView(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
padding: EdgeInsets.symmetric(vertical: config.isMobile ? 40.0 : 28.0),
|
||||||
children: [
|
child: Column(
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
widget.stepText,
|
children: [
|
||||||
style: GoogleFonts.merienda(
|
Text(
|
||||||
fontSize: config.isMobile ? 13 : 16,
|
widget.stepText,
|
||||||
color: Colors.black54,
|
style: GoogleFonts.merienda(
|
||||||
|
fontSize: config.isMobile ? 13 : 16,
|
||||||
|
color: Colors.black54,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: config.isMobile ? 6 : 10),
|
||||||
SizedBox(height: config.isMobile ? 6 : 10),
|
Text(
|
||||||
Text(
|
widget.title,
|
||||||
widget.title,
|
style: GoogleFonts.merienda(
|
||||||
style: GoogleFonts.merienda(
|
fontSize: config.isMobile ? 18 : 24,
|
||||||
fontSize: config.isMobile ? 18 : 24,
|
fontWeight: FontWeight.bold,
|
||||||
fontWeight: FontWeight.bold,
|
color: Colors.black87,
|
||||||
color: Colors.black87,
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
SizedBox(height: config.isMobile ? 16 : 20),
|
||||||
),
|
_buildCard(context, config, screenSize),
|
||||||
SizedBox(height: config.isMobile ? 16 : 20),
|
|
||||||
_buildCard(context, config, screenSize),
|
// Boutons mobile sous la carte
|
||||||
|
if (config.isMobile) ...[
|
||||||
// Boutons mobile sous la carte
|
const SizedBox(height: 20),
|
||||||
if (config.isMobile) ...[
|
_buildMobileButtons(context, config, screenSize),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 10),
|
||||||
_buildMobileButtons(context, config, screenSize),
|
],
|
||||||
const SizedBox(height: 10),
|
|
||||||
],
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
// Chevrons desktop : visuellement sur les côtés ; ordre Tab 10–11 = après « Places disponibles ».
|
||||||
// Chevrons desktop uniquement
|
|
||||||
if (!config.isMobile) ...[
|
if (!config.isMobile) ...[
|
||||||
// Chevron Gauche (Retour)
|
|
||||||
Positioned(
|
Positioned(
|
||||||
top: screenSize.height / 2 - 20,
|
top: screenSize.height / 2 - 20,
|
||||||
left: 40,
|
left: 40,
|
||||||
child: IconButton(
|
child: FocusTraversalOrder(
|
||||||
icon: Transform(
|
order: const NumericFocusOrder(_kTabChevronPrev),
|
||||||
alignment: Alignment.center,
|
child: IconButton(
|
||||||
transform: Matrix4.rotationY(math.pi),
|
icon: Transform(
|
||||||
child: Image.asset('assets/images/chevron_right.png', height: 40),
|
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: 'Précédent',
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
if (context.canPop()) {
|
|
||||||
context.pop();
|
|
||||||
} else {
|
|
||||||
context.go(widget.previousRoute);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: 'Précédent',
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Chevron Droit (Suivant)
|
|
||||||
Positioned(
|
Positioned(
|
||||||
top: screenSize.height / 2 - 20,
|
top: screenSize.height / 2 - 20,
|
||||||
right: 40,
|
right: 40,
|
||||||
child: IconButton(
|
child: FocusTraversalOrder(
|
||||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
order: const NumericFocusOrder(_kTabChevronNext),
|
||||||
onPressed: _submitForm,
|
child: IconButton(
|
||||||
tooltip: 'Suivant',
|
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||||
|
onPressed: _submitForm,
|
||||||
|
tooltip: 'Suivant',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -766,6 +789,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
onTap: () => _selectDate(context),
|
onTap: () => _selectDate(context),
|
||||||
suffixIcon: Icons.calendar_today,
|
suffixIcon: Icons.calendar_today,
|
||||||
validator: (v) => _selectedDate == null ? 'Date requise' : null,
|
validator: (v) => _selectedDate == null ? 'Date requise' : null,
|
||||||
|
focusTraversalOrder: _kTabDateBirth,
|
||||||
),
|
),
|
||||||
SizedBox(height: verticalSpacing),
|
SizedBox(height: verticalSpacing),
|
||||||
_buildField(
|
_buildField(
|
||||||
@ -775,6 +799,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
hint: 'Ex. Ajaccio, Paris, Casablanca…',
|
hint: 'Ex. Ajaccio, Paris, Casablanca…',
|
||||||
validator: _validateBirthCity,
|
validator: _validateBirthCity,
|
||||||
focusNode: _birthCityFocus,
|
focusNode: _birthCityFocus,
|
||||||
|
focusTraversalOrder: _kTabBirthCity,
|
||||||
),
|
),
|
||||||
SizedBox(height: verticalSpacing),
|
SizedBox(height: verticalSpacing),
|
||||||
_buildField(
|
_buildField(
|
||||||
@ -784,6 +809,10 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
hint: 'Ex. France, Maroc…',
|
hint: 'Ex. France, Maroc…',
|
||||||
validator: _validateBirthCountry,
|
validator: _validateBirthCountry,
|
||||||
focusNode: _birthCountryFocus,
|
focusNode: _birthCountryFocus,
|
||||||
|
focusTraversalOrder: _kTabBirthCountry,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
onFieldSubmitted: (_) => _nirFocus?.requestFocus(),
|
||||||
|
onEditingComplete: () => _nirFocus?.requestFocus(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -793,6 +822,8 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
SizedBox(height: verticalSpacing),
|
SizedBox(height: verticalSpacing),
|
||||||
NirTextField(
|
NirTextField(
|
||||||
controller: _nirController,
|
controller: _nirController,
|
||||||
|
focusNode: _nirFocus,
|
||||||
|
focusTraversalOrder: _nirFocus != null ? _kTabNir : null,
|
||||||
fieldWidth: double.infinity,
|
fieldWidth: double.infinity,
|
||||||
fieldHeight: config.isMobile ? 45.0 : 53.0,
|
fieldHeight: config.isMobile ? 45.0 : 53.0,
|
||||||
labelFontSize: config.isMobile ? 15.0 : 20.0,
|
labelFontSize: config.isMobile ? 15.0 : 20.0,
|
||||||
@ -810,6 +841,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
controller: _agrementController,
|
controller: _agrementController,
|
||||||
hint: 'Votre numéro d\'agrément',
|
hint: 'Votre numéro d\'agrément',
|
||||||
validator: (v) => v!.isEmpty ? 'Agrément requis' : null,
|
validator: (v) => v!.isEmpty ? 'Agrément requis' : null,
|
||||||
|
focusTraversalOrder: _kTabAgrement,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 14),
|
const SizedBox(width: 14),
|
||||||
@ -824,6 +856,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
suffixIcon: Icons.calendar_today,
|
suffixIcon: Icons.calendar_today,
|
||||||
validator: (_) =>
|
validator: (_) =>
|
||||||
_selectedAgreementDate == null ? 'Date d\'obtention requise' : null,
|
_selectedAgreementDate == null ? 'Date d\'obtention requise' : null,
|
||||||
|
focusTraversalOrder: _kTabAgreementDate,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -850,6 +883,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
focusTraversalOrder: _kTabCapacity,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 14),
|
const SizedBox(width: 14),
|
||||||
@ -861,6 +895,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
hint: 'Entre 0 et la capacité',
|
hint: 'Entre 0 et la capacité',
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
validator: _validatePlacesAvailable,
|
validator: _validatePlacesAvailable,
|
||||||
|
focusTraversalOrder: _kTabPlaces,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -887,6 +922,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
onTap: () => _selectDate(context),
|
onTap: () => _selectDate(context),
|
||||||
suffixIcon: Icons.calendar_today,
|
suffixIcon: Icons.calendar_today,
|
||||||
validator: (v) => _selectedDate == null ? 'Date requise' : null,
|
validator: (v) => _selectedDate == null ? 'Date requise' : null,
|
||||||
|
focusTraversalOrder: _kTabDateBirth,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
@ -897,6 +933,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
hint: 'Ex. Ajaccio, Paris, Casablanca…',
|
hint: 'Ex. Ajaccio, Paris, Casablanca…',
|
||||||
validator: _validateBirthCity,
|
validator: _validateBirthCity,
|
||||||
focusNode: _birthCityFocus,
|
focusNode: _birthCityFocus,
|
||||||
|
focusTraversalOrder: _kTabBirthCity,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
@ -907,11 +944,17 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
hint: 'Ex. France, Maroc…',
|
hint: 'Ex. France, Maroc…',
|
||||||
validator: _validateBirthCountry,
|
validator: _validateBirthCountry,
|
||||||
focusNode: _birthCountryFocus,
|
focusNode: _birthCountryFocus,
|
||||||
|
focusTraversalOrder: _kTabBirthCountry,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
onFieldSubmitted: (_) => _nirFocus?.requestFocus(),
|
||||||
|
onEditingComplete: () => _nirFocus?.requestFocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
NirTextField(
|
NirTextField(
|
||||||
controller: _nirController,
|
controller: _nirController,
|
||||||
|
focusNode: _nirFocus,
|
||||||
|
focusTraversalOrder: _nirFocus != null ? _kTabNir : null,
|
||||||
fieldWidth: double.infinity,
|
fieldWidth: double.infinity,
|
||||||
fieldHeight: 45.0,
|
fieldHeight: 45.0,
|
||||||
labelFontSize: 15.0,
|
labelFontSize: 15.0,
|
||||||
@ -925,6 +968,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
controller: _agrementController,
|
controller: _agrementController,
|
||||||
hint: 'Votre numéro d\'agrément',
|
hint: 'Votre numéro d\'agrément',
|
||||||
validator: (v) => v!.isEmpty ? 'Agrément requis' : null,
|
validator: (v) => v!.isEmpty ? 'Agrément requis' : null,
|
||||||
|
focusTraversalOrder: _kTabAgrement,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_buildField(
|
_buildField(
|
||||||
@ -937,6 +981,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
suffixIcon: Icons.calendar_today,
|
suffixIcon: Icons.calendar_today,
|
||||||
validator: (_) =>
|
validator: (_) =>
|
||||||
_selectedAgreementDate == null ? 'Date d\'obtention requise' : null,
|
_selectedAgreementDate == null ? 'Date d\'obtention requise' : null,
|
||||||
|
focusTraversalOrder: _kTabAgreementDate,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_buildField(
|
_buildField(
|
||||||
@ -956,6 +1001,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
focusTraversalOrder: _kTabCapacity,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_buildField(
|
_buildField(
|
||||||
@ -965,6 +1011,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
hint: 'Entre 0 et la capacité',
|
hint: 'Entre 0 et la capacité',
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
validator: _validatePlacesAvailable,
|
validator: _validatePlacesAvailable,
|
||||||
|
focusTraversalOrder: _kTabPlaces,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -975,20 +1022,28 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
final photoSize = config.isMobile ? 200.0 : 270.0;
|
final photoSize = config.isMobile ? 200.0 : 270.0;
|
||||||
final scaleFactor = config.isMobile ? 0.9 : 1.1;
|
final scaleFactor = config.isMobile ? 0.9 : 1.1;
|
||||||
|
|
||||||
|
final slot = RegistrationPhotoSlot(
|
||||||
|
side: photoSize,
|
||||||
|
scaleFactor: scaleFactor,
|
||||||
|
imageBytes: _photoBytes,
|
||||||
|
imageFile: _photoFile,
|
||||||
|
imagePathOrAsset: _photoPathFramework,
|
||||||
|
onTapPick: config.isReadonly ? null : _pickPhoto,
|
||||||
|
onClear: config.isReadonly ? null : _clearRegistrationPhoto,
|
||||||
|
baseShadowColor: Colors.green.shade300,
|
||||||
|
isMobile: config.isMobile,
|
||||||
|
);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
RegistrationPhotoSlot(
|
if (config.isReadonly)
|
||||||
side: photoSize,
|
slot
|
||||||
scaleFactor: scaleFactor,
|
else
|
||||||
imageBytes: _photoBytes,
|
FocusTraversalOrder(
|
||||||
imageFile: _photoFile,
|
order: const NumericFocusOrder(_kTabPhotoPick),
|
||||||
imagePathOrAsset: _photoPathFramework,
|
child: slot,
|
||||||
onTapPick: config.isReadonly ? null : _pickPhoto,
|
),
|
||||||
onClear: config.isReadonly ? null : _clearRegistrationPhoto,
|
|
||||||
baseShadowColor: Colors.green.shade300,
|
|
||||||
isMobile: config.isMobile,
|
|
||||||
),
|
|
||||||
SizedBox(height: config.isMobile ? 10.0 : 6.0),
|
SizedBox(height: config.isMobile ? 10.0 : 6.0),
|
||||||
AppCustomCheckbox(
|
AppCustomCheckbox(
|
||||||
label: 'J\'accepte l\'utilisation\nde ma photo.',
|
label: 'J\'accepte l\'utilisation\nde ma photo.',
|
||||||
@ -1015,6 +1070,10 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
String? Function(String?)? validator,
|
String? Function(String?)? validator,
|
||||||
List<TextInputFormatter>? inputFormatters,
|
List<TextInputFormatter>? inputFormatters,
|
||||||
FocusNode? focusNode,
|
FocusNode? focusNode,
|
||||||
|
double? focusTraversalOrder,
|
||||||
|
TextInputAction? textInputAction,
|
||||||
|
ValueChanged<String>? onFieldSubmitted,
|
||||||
|
VoidCallback? onEditingComplete,
|
||||||
}) {
|
}) {
|
||||||
if (config.isReadonly) {
|
if (config.isReadonly) {
|
||||||
return FormFieldWrapper(
|
return FormFieldWrapper(
|
||||||
@ -1023,7 +1082,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
value: controller.text,
|
value: controller.text,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return CustomAppTextField(
|
final field = CustomAppTextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
labelText: label,
|
labelText: label,
|
||||||
@ -1039,7 +1098,18 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
|
|||||||
suffixIcon: suffixIcon,
|
suffixIcon: suffixIcon,
|
||||||
validator: validator,
|
validator: validator,
|
||||||
inputFormatters: inputFormatters,
|
inputFormatters: inputFormatters,
|
||||||
|
textInputAction: textInputAction,
|
||||||
|
onFieldSubmitted: onFieldSubmitted,
|
||||||
|
onEditingComplete: onEditingComplete,
|
||||||
);
|
);
|
||||||
|
final o = focusTraversalOrder;
|
||||||
|
if (o != null) {
|
||||||
|
return FocusTraversalOrder(
|
||||||
|
order: NumericFocusOrder(o),
|
||||||
|
child: field,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user