feat: Mise à jour des interfaces mobiles v3.2.3
- Amélioration des interfaces utilisateur sur mobile - Optimisation de la responsivité des composants Flutter - Mise à jour des widgets de chat et communication - Amélioration des formulaires et tableaux - Ajout de nouveaux composants pour l'administration - Optimisation des thèmes et styles visuels 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geosector_app/core/theme/app_theme.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:geosector_app/core/data/models/passage_model.dart';
|
||||
import 'package:geosector_app/core/repositories/passage_repository.dart';
|
||||
import 'package:geosector_app/core/repositories/user_repository.dart';
|
||||
@@ -184,8 +186,10 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
|
||||
// Initialiser la date de passage
|
||||
_passedAt = passage?.passedAt ?? DateTime.now();
|
||||
final String dateFormatted = '${_passedAt.day.toString().padLeft(2, '0')}/${_passedAt.month.toString().padLeft(2, '0')}/${_passedAt.year}';
|
||||
final String timeFormatted = '${_passedAt.hour.toString().padLeft(2, '0')}:${_passedAt.minute.toString().padLeft(2, '0')}';
|
||||
final String dateFormatted =
|
||||
'${_passedAt.day.toString().padLeft(2, '0')}/${_passedAt.month.toString().padLeft(2, '0')}/${_passedAt.year}';
|
||||
final String timeFormatted =
|
||||
'${_passedAt.hour.toString().padLeft(2, '0')}:${_passedAt.minute.toString().padLeft(2, '0')}';
|
||||
|
||||
debugPrint('Valeurs pour controllers:');
|
||||
debugPrint(' numero: "$numero"');
|
||||
@@ -258,12 +262,14 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
_montantController.text = '';
|
||||
_fkTypeReglement = 4; // Non renseigné
|
||||
}
|
||||
|
||||
|
||||
// Si c'est un nouveau passage et qu'on change de type, réinitialiser la date à maintenant
|
||||
if (widget.passage == null) {
|
||||
_passedAt = DateTime.now();
|
||||
_dateController.text = '${_passedAt.day.toString().padLeft(2, '0')}/${_passedAt.month.toString().padLeft(2, '0')}/${_passedAt.year}';
|
||||
_timeController.text = '${_passedAt.hour.toString().padLeft(2, '0')}:${_passedAt.minute.toString().padLeft(2, '0')}';
|
||||
_dateController.text =
|
||||
'${_passedAt.day.toString().padLeft(2, '0')}/${_passedAt.month.toString().padLeft(2, '0')}/${_passedAt.year}';
|
||||
_timeController.text =
|
||||
'${_passedAt.hour.toString().padLeft(2, '0')}:${_passedAt.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -366,7 +372,7 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
if (success && mounted) {
|
||||
Future.delayed(const Duration(milliseconds: 200), () {
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
widget.onSuccess?.call();
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
if (mounted) {
|
||||
@@ -420,7 +426,8 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: MediaQuery.of(context).size.width < 600 ? 1.8 : 2.5,
|
||||
childAspectRatio:
|
||||
MediaQuery.of(context).size.width < 600 ? 1.8 : 2.5,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
@@ -445,7 +452,7 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(typeData['couleur2'] as int? ?? 0xFF000000)
|
||||
.withOpacity(0.15),
|
||||
.withValues(alpha: 0.15),
|
||||
border: Border.all(
|
||||
color: Color(typeData['couleur2'] as int? ?? 0xFF000000),
|
||||
width: isSelected ? 3 : 2,
|
||||
@@ -456,7 +463,7 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
BoxShadow(
|
||||
color: Color(typeData['couleur2'] as int? ??
|
||||
0xFF000000)
|
||||
.withOpacity(0.2),
|
||||
.withValues(alpha: 0.2),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
)
|
||||
@@ -504,7 +511,6 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
Widget _buildPassageForm() {
|
||||
try {
|
||||
debugPrint('=== DEBUT _buildPassageForm ===');
|
||||
final theme = Theme.of(context);
|
||||
|
||||
debugPrint('Building Form...');
|
||||
return Form(
|
||||
@@ -549,7 +555,7 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
|
||||
// Section Adresse
|
||||
FormSection(
|
||||
title: 'Adresse',
|
||||
@@ -740,7 +746,9 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
|
||||
// Section Règlement et Remarque
|
||||
FormSection(
|
||||
title: (_selectedPassageType == 1 || _selectedPassageType == 5) ? 'Règlement et Note' : 'Note',
|
||||
title: (_selectedPassageType == 1 || _selectedPassageType == 5)
|
||||
? 'Règlement et Note'
|
||||
: 'Note',
|
||||
icon: Icons.note,
|
||||
children: [
|
||||
// Afficher montant et type de règlement seulement pour fkType 1 (Effectué) ou 5 (Lot)
|
||||
@@ -755,7 +763,8 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
showLabel: false,
|
||||
hintText: "0.00",
|
||||
textAlign: TextAlign.right,
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true),
|
||||
readOnly: widget.readOnly,
|
||||
validator: _validateMontant,
|
||||
prefixIcon: Icons.euro,
|
||||
@@ -764,7 +773,7 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: DropdownButtonFormField<int>(
|
||||
value: _fkTypeReglement,
|
||||
initialValue: _fkTypeReglement,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Type de règlement *",
|
||||
border: OutlineInputBorder(),
|
||||
@@ -792,7 +801,8 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
_fkTypeReglement = value!;
|
||||
});
|
||||
},
|
||||
validator: (_selectedPassageType == 1 || _selectedPassageType == 5)
|
||||
validator: (_selectedPassageType == 1 ||
|
||||
_selectedPassageType == 5)
|
||||
? (value) {
|
||||
if (value == null || value < 1 || value > 3) {
|
||||
return 'Type de règlement requis';
|
||||
@@ -837,9 +847,6 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Future<void> _selectDate() async {
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
@@ -856,7 +863,8 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
_passedAt.hour,
|
||||
_passedAt.minute,
|
||||
);
|
||||
_dateController.text = '${_passedAt.day.toString().padLeft(2, '0')}/${_passedAt.month.toString().padLeft(2, '0')}/${_passedAt.year}';
|
||||
_dateController.text =
|
||||
'${_passedAt.day.toString().padLeft(2, '0')}/${_passedAt.month.toString().padLeft(2, '0')}/${_passedAt.year}';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -875,160 +883,324 @@ class _PassageFormDialogState extends State<PassageFormDialog> {
|
||||
picked.hour,
|
||||
picked.minute,
|
||||
);
|
||||
_timeController.text = '${_passedAt.hour.toString().padLeft(2, '0')}:${_passedAt.minute.toString().padLeft(2, '0')}';
|
||||
_timeController.text =
|
||||
'${_passedAt.hour.toString().padLeft(2, '0')}:${_passedAt.minute.toString().padLeft(2, '0')}';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour détecter si on est sur mobile
|
||||
bool _isMobile(BuildContext context) {
|
||||
// Détecter si on est sur mobile natif ou web mobile (largeur < 600px)
|
||||
return Theme.of(context).platform == TargetPlatform.iOS ||
|
||||
Theme.of(context).platform == TargetPlatform.android ||
|
||||
(kIsWeb && MediaQuery.of(context).size.width < 600);
|
||||
}
|
||||
|
||||
// Méthode pour construire l'en-tête du formulaire
|
||||
Widget _buildHeader() {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedPassageType != null &&
|
||||
AppKeys.typesPassages.containsKey(_selectedPassageType)
|
||||
? Color(AppKeys.typesPassages[_selectedPassageType]!['couleur2']
|
||||
as int? ??
|
||||
0xFF000000)
|
||||
.withValues(alpha: 0.1)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
widget.passage == null ? Icons.add_circle : Icons.edit,
|
||||
color: _selectedPassageType != null &&
|
||||
AppKeys.typesPassages
|
||||
.containsKey(_selectedPassageType)
|
||||
? Color(AppKeys.typesPassages[_selectedPassageType]![
|
||||
'couleur2'] as int? ??
|
||||
0xFF000000)
|
||||
: theme.colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
widget.title,
|
||||
style: theme.textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _selectedPassageType != null &&
|
||||
AppKeys.typesPassages
|
||||
.containsKey(_selectedPassageType)
|
||||
? Color(AppKeys.typesPassages[_selectedPassageType]![
|
||||
'couleur2'] as int? ??
|
||||
0xFF000000)
|
||||
: theme.colorScheme.primary,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (_selectedPassageType != null &&
|
||||
AppKeys.typesPassages
|
||||
.containsKey(_selectedPassageType)) ...[
|
||||
const SizedBox(width: 12),
|
||||
Icon(
|
||||
AppKeys.typesPassages[_selectedPassageType]!['icon_data']
|
||||
as IconData? ??
|
||||
Icons.help,
|
||||
color: Color(
|
||||
AppKeys.typesPassages[_selectedPassageType]!['couleur2']
|
||||
as int? ??
|
||||
0xFF000000),
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
AppKeys.typesPassages[_selectedPassageType]!['titre']
|
||||
as String? ??
|
||||
'Inconnu',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(AppKeys.typesPassages[_selectedPassageType]![
|
||||
'couleur2'] as int? ??
|
||||
0xFF000000),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: _isSubmitting ? null : () {
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode pour construire le contenu principal
|
||||
Widget _buildContent() {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!_showForm) ...[
|
||||
() {
|
||||
debugPrint('Building passage type selection...');
|
||||
return _buildPassageTypeSelection();
|
||||
}(),
|
||||
] else ...[
|
||||
() {
|
||||
debugPrint('Building passage form...');
|
||||
return _buildPassageForm();
|
||||
}(),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode pour construire les boutons du footer
|
||||
Widget _buildFooterButtons() {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: _isSubmitting ? null : () {
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
},
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
if (!widget.readOnly && _showForm && _selectedPassageType != null)
|
||||
ElevatedButton.icon(
|
||||
onPressed: _isSubmitting ? null : _handleSubmit,
|
||||
icon: _isSubmitting
|
||||
? const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: Icon(widget.passage == null ? Icons.add : Icons.save),
|
||||
label: Text(_isSubmitting
|
||||
? 'Enregistrement...'
|
||||
: (widget.passage == null ? 'Créer' : 'Enregistrer')),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode pour construire le contenu du Dialog
|
||||
Widget _buildDialogContent() {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
_buildHeader(),
|
||||
const Divider(),
|
||||
|
||||
// Contenu
|
||||
Expanded(
|
||||
child: _buildContent(),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Footer
|
||||
_buildFooterButtons(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode pour construire l'AppBar mobile
|
||||
AppBar _buildMobileAppBar() {
|
||||
final theme = Theme.of(context);
|
||||
final typeColor = _selectedPassageType != null &&
|
||||
AppKeys.typesPassages.containsKey(_selectedPassageType)
|
||||
? Color(
|
||||
AppKeys.typesPassages[_selectedPassageType]!['couleur2'] as int? ??
|
||||
0xFF000000)
|
||||
: theme.colorScheme.primary;
|
||||
|
||||
return AppBar(
|
||||
backgroundColor: typeColor.withValues(alpha: 0.1),
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.close, color: typeColor),
|
||||
onPressed: _isSubmitting ? null : () {
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
},
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(
|
||||
widget.passage == null ? Icons.add_circle : Icons.edit,
|
||||
color: typeColor,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: typeColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: AppTheme.r(context, 18),
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: _selectedPassageType != null &&
|
||||
AppKeys.typesPassages.containsKey(_selectedPassageType)
|
||||
? [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
AppKeys.typesPassages[_selectedPassageType]!['icon_data']
|
||||
as IconData? ??
|
||||
Icons.help,
|
||||
color: typeColor,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
AppKeys.typesPassages[_selectedPassageType]!['titre']
|
||||
as String? ??
|
||||
'Inconnu',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: typeColor,
|
||||
fontSize: AppTheme.r(context, 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
try {
|
||||
debugPrint('=== DEBUT PassageFormDialog.build ===');
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
insetPadding: const EdgeInsets.all(24),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.6,
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 800,
|
||||
maxHeight: 900,
|
||||
),
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedPassageType != null && AppKeys.typesPassages.containsKey(_selectedPassageType)
|
||||
? Color(AppKeys.typesPassages[_selectedPassageType]!['couleur2'] as int? ?? 0xFF000000).withOpacity(0.1)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
widget.passage == null
|
||||
? Icons.add_circle
|
||||
: Icons.edit,
|
||||
color: _selectedPassageType != null && AppKeys.typesPassages.containsKey(_selectedPassageType)
|
||||
? Color(AppKeys.typesPassages[_selectedPassageType]!['couleur2'] as int? ?? 0xFF000000)
|
||||
: theme.colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
widget.title,
|
||||
style: theme.textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _selectedPassageType != null && AppKeys.typesPassages.containsKey(_selectedPassageType)
|
||||
? Color(AppKeys.typesPassages[_selectedPassageType]!['couleur2'] as int? ?? 0xFF000000)
|
||||
: theme.colorScheme.primary,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (_selectedPassageType != null && AppKeys.typesPassages.containsKey(_selectedPassageType)) ...[
|
||||
const SizedBox(width: 12),
|
||||
Icon(
|
||||
AppKeys.typesPassages[_selectedPassageType]!['icon_data'] as IconData? ?? Icons.help,
|
||||
color: Color(AppKeys.typesPassages[_selectedPassageType]!['couleur2'] as int? ?? 0xFF000000),
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
AppKeys.typesPassages[_selectedPassageType]!['titre'] as String? ?? 'Inconnu',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(AppKeys.typesPassages[_selectedPassageType]!['couleur2'] as int? ?? 0xFF000000),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: _isSubmitting
|
||||
? null
|
||||
: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
final isMobile = _isMobile(context);
|
||||
debugPrint('Platform mobile détectée: $isMobile');
|
||||
|
||||
// Contenu
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!_showForm) ...[
|
||||
() {
|
||||
debugPrint('Building passage type selection...');
|
||||
return _buildPassageTypeSelection();
|
||||
}(),
|
||||
] else ...[
|
||||
() {
|
||||
debugPrint('Building passage form...');
|
||||
return _buildPassageForm();
|
||||
}(),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Footer
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
if (isMobile) {
|
||||
// Mode plein écran pour mobile
|
||||
return Scaffold(
|
||||
appBar: _buildMobileAppBar(),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: _isSubmitting
|
||||
? null
|
||||
: () => Navigator.of(context).pop(),
|
||||
child: const Text('Annuler'),
|
||||
Expanded(
|
||||
child: _buildContent(),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
if (!widget.readOnly &&
|
||||
_showForm &&
|
||||
_selectedPassageType != null)
|
||||
ElevatedButton.icon(
|
||||
onPressed: _isSubmitting ? null : _handleSubmit,
|
||||
icon: _isSubmitting
|
||||
? const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: Icon(
|
||||
widget.passage == null ? Icons.add : Icons.save),
|
||||
label: Text(_isSubmitting
|
||||
? 'Enregistrement...'
|
||||
: (widget.passage == null ? 'Créer' : 'Enregistrer')),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
bottomNavigationBar: _showForm && _selectedPassageType != null
|
||||
? SafeArea(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.1),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, -2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: _buildFooterButtons(),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
} else {
|
||||
// Mode Dialog pour desktop/tablette
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
insetPadding: const EdgeInsets.all(24),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.6,
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 800,
|
||||
maxHeight: 900,
|
||||
),
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: _buildDialogContent(),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
debugPrint('=== ERREUR PassageFormDialog.build ===');
|
||||
debugPrint('Erreur: $e');
|
||||
|
||||
Reference in New Issue
Block a user