|
|
|
@ -2,6 +2,10 @@ import 'package:flutter/material.dart'; |
|
|
|
import 'package:http/http.dart' as http; |
|
|
|
import 'dart:convert'; |
|
|
|
|
|
|
|
import 'package:itrimobe/pages/tables.dart'; |
|
|
|
|
|
|
|
import '../layouts/main_layout.dart'; |
|
|
|
|
|
|
|
class CartPage extends StatefulWidget { |
|
|
|
final int tableId; |
|
|
|
final int personne; |
|
|
|
@ -119,30 +123,28 @@ class _CartPageState extends State<CartPage> { |
|
|
|
child: Text('Annuler', style: TextStyle(color: Colors.grey[600])), |
|
|
|
), |
|
|
|
ElevatedButton( |
|
|
|
onPressed: |
|
|
|
_isValidating |
|
|
|
? null |
|
|
|
: () { |
|
|
|
Navigator.of(context).pop(); |
|
|
|
_validateOrder(); |
|
|
|
}, |
|
|
|
onPressed: _isValidating |
|
|
|
? null |
|
|
|
: () { |
|
|
|
Navigator.of(context).pop(); |
|
|
|
_validateOrder(); |
|
|
|
}, |
|
|
|
style: ElevatedButton.styleFrom( |
|
|
|
backgroundColor: Colors.green[700], |
|
|
|
foregroundColor: Colors.white, |
|
|
|
), |
|
|
|
child: |
|
|
|
_isValidating |
|
|
|
? SizedBox( |
|
|
|
width: 16, |
|
|
|
height: 16, |
|
|
|
child: CircularProgressIndicator( |
|
|
|
strokeWidth: 2, |
|
|
|
valueColor: AlwaysStoppedAnimation<Color>( |
|
|
|
Colors.white, |
|
|
|
), |
|
|
|
child: _isValidating |
|
|
|
? SizedBox( |
|
|
|
width: 16, |
|
|
|
height: 16, |
|
|
|
child: CircularProgressIndicator( |
|
|
|
strokeWidth: 2, |
|
|
|
valueColor: AlwaysStoppedAnimation<Color>( |
|
|
|
Colors.white, |
|
|
|
), |
|
|
|
) |
|
|
|
: Text('Valider'), |
|
|
|
), |
|
|
|
) |
|
|
|
: Text('Valider'), |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
@ -163,16 +165,15 @@ class _CartPageState extends State<CartPage> { |
|
|
|
"reservation_id": 1, // Peut être null si pas de réservation |
|
|
|
"serveur": "Serveur par défaut", // Valeur par défaut comme demandé |
|
|
|
"commentaires": _getOrderComments(), |
|
|
|
"items": |
|
|
|
_cartItems |
|
|
|
.map( |
|
|
|
(item) => { |
|
|
|
"menu_id": item.id, |
|
|
|
"quantite": item.quantity, |
|
|
|
"commentaires": item.notes.isNotEmpty ? item.notes : null, |
|
|
|
}, |
|
|
|
) |
|
|
|
.toList(), |
|
|
|
"items": _cartItems |
|
|
|
.map( |
|
|
|
(item) => { |
|
|
|
"menu_id": item.id, |
|
|
|
"quantite": item.quantity, |
|
|
|
"commentaires": item.notes.isNotEmpty ? item.notes : null, |
|
|
|
}, |
|
|
|
) |
|
|
|
.toList(), |
|
|
|
}; |
|
|
|
|
|
|
|
// Appel API pour créer la commande |
|
|
|
@ -206,15 +207,15 @@ class _CartPageState extends State<CartPage> { |
|
|
|
|
|
|
|
String _getOrderComments() { |
|
|
|
// Concaténer toutes les notes des articles pour les commentaires généraux |
|
|
|
List<String> allNotes = |
|
|
|
_cartItems |
|
|
|
.where((item) => item.notes.isNotEmpty) |
|
|
|
.map((item) => '${item.nom}: ${item.notes}') |
|
|
|
.toList(); |
|
|
|
List<String> allNotes = _cartItems |
|
|
|
.where((item) => item.notes.isNotEmpty) |
|
|
|
.map((item) => '${item.nom}: ${item.notes}') |
|
|
|
.toList(); |
|
|
|
|
|
|
|
return allNotes.join('; '); |
|
|
|
} |
|
|
|
|
|
|
|
// FONCTION CORRIGÉE POUR LA NAVIGATION VERS LES TABLES |
|
|
|
void _showSuccessDialog() { |
|
|
|
showDialog( |
|
|
|
context: context, |
|
|
|
@ -234,14 +235,13 @@ class _CartPageState extends State<CartPage> { |
|
|
|
actions: [ |
|
|
|
ElevatedButton( |
|
|
|
onPressed: () { |
|
|
|
Navigator.of(context).pop(); // Fermer le dialog |
|
|
|
Navigator.of(context).pop(); // Retourner au menu |
|
|
|
Navigator.of(context).pop(); // Retourner aux tables |
|
|
|
Navigator.of(context).pushAndRemoveUntil( |
|
|
|
MaterialPageRoute( |
|
|
|
builder: (context) => MainLayout(child: TablesScreen()), |
|
|
|
), |
|
|
|
(route) => false, |
|
|
|
); |
|
|
|
}, |
|
|
|
style: ElevatedButton.styleFrom( |
|
|
|
backgroundColor: Colors.green[700], |
|
|
|
foregroundColor: Colors.white, |
|
|
|
), |
|
|
|
child: Text('OK'), |
|
|
|
), |
|
|
|
], |
|
|
|
@ -251,17 +251,17 @@ class _CartPageState extends State<CartPage> { |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> _updateTableStatus() async { |
|
|
|
try { |
|
|
|
final updateResponse = await http.put( |
|
|
|
Uri.parse('https://restaurant.careeracademy.mg/api/tables/${widget.tableId}'), |
|
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
|
body: json.encode({"status": "occupied"}), |
|
|
|
); |
|
|
|
} catch (e) { |
|
|
|
print("Erreur lors de la mise à jour du statut de la table: $e"); |
|
|
|
try { |
|
|
|
final updateResponse = await http.put( |
|
|
|
Uri.parse( |
|
|
|
'https://restaurant.careeracademy.mg/api/tables/${widget.tableId}'), |
|
|
|
headers: {'Content-Type': 'application/json'}, |
|
|
|
body: json.encode({"status": "occupied"}), |
|
|
|
); |
|
|
|
} catch (e) { |
|
|
|
print("Erreur lors de la mise à jour du statut de la table: $e"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void _showErrorDialog(String message) { |
|
|
|
showDialog( |
|
|
|
@ -317,297 +317,290 @@ class _CartPageState extends State<CartPage> { |
|
|
|
SizedBox(width: 16), |
|
|
|
], |
|
|
|
), |
|
|
|
body: |
|
|
|
_cartItems.isEmpty |
|
|
|
? Center( |
|
|
|
child: Column( |
|
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
Icon( |
|
|
|
Icons.shopping_cart_outlined, |
|
|
|
size: 80, |
|
|
|
color: Colors.grey[400], |
|
|
|
), |
|
|
|
SizedBox(height: 16), |
|
|
|
Text( |
|
|
|
'Votre panier est vide', |
|
|
|
style: TextStyle(fontSize: 18, color: Colors.grey[600]), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
) |
|
|
|
: Column( |
|
|
|
body: _cartItems.isEmpty |
|
|
|
? Center( |
|
|
|
child: Column( |
|
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
// Header avec infos table |
|
|
|
Container( |
|
|
|
width: double.infinity, |
|
|
|
padding: EdgeInsets.all(20), |
|
|
|
color: Colors.white, |
|
|
|
child: Text( |
|
|
|
'Table ${widget.tableId} • ${widget.personne} personne${widget.personne > 1 ? 's' : ''}', |
|
|
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]), |
|
|
|
), |
|
|
|
Icon( |
|
|
|
Icons.shopping_cart_outlined, |
|
|
|
size: 80, |
|
|
|
color: Colors.grey[400], |
|
|
|
), |
|
|
|
SizedBox(height: 16), |
|
|
|
Text( |
|
|
|
'Votre panier est vide', |
|
|
|
style: TextStyle(fontSize: 18, color: Colors.grey[600]), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
) |
|
|
|
: Column( |
|
|
|
children: [ |
|
|
|
// Header avec infos table |
|
|
|
Container( |
|
|
|
width: double.infinity, |
|
|
|
padding: EdgeInsets.all(20), |
|
|
|
color: Colors.white, |
|
|
|
child: Text( |
|
|
|
'Table ${widget.tableId} • ${widget.personne} personne${widget.personne > 1 ? 's' : ''}', |
|
|
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]), |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
// Liste des articles |
|
|
|
Expanded( |
|
|
|
child: ListView.separated( |
|
|
|
padding: EdgeInsets.all(16), |
|
|
|
itemCount: _cartItems.length, |
|
|
|
separatorBuilder: |
|
|
|
(context, index) => SizedBox(height: 12), |
|
|
|
itemBuilder: (context, index) { |
|
|
|
final item = _cartItems[index]; |
|
|
|
return Container( |
|
|
|
padding: EdgeInsets.all(16), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: Colors.white, |
|
|
|
borderRadius: BorderRadius.circular(12), |
|
|
|
boxShadow: [ |
|
|
|
BoxShadow( |
|
|
|
color: Colors.black.withOpacity(0.05), |
|
|
|
blurRadius: 5, |
|
|
|
offset: Offset(0, 2), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
child: Column( |
|
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
Row( |
|
|
|
mainAxisAlignment: |
|
|
|
MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Expanded( |
|
|
|
child: Text( |
|
|
|
item.nom, |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
// Liste des articles |
|
|
|
Expanded( |
|
|
|
child: ListView.separated( |
|
|
|
padding: EdgeInsets.all(16), |
|
|
|
itemCount: _cartItems.length, |
|
|
|
separatorBuilder: (context, index) => SizedBox(height: 12), |
|
|
|
itemBuilder: (context, index) { |
|
|
|
final item = _cartItems[index]; |
|
|
|
return Container( |
|
|
|
padding: EdgeInsets.all(16), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: Colors.white, |
|
|
|
borderRadius: BorderRadius.circular(12), |
|
|
|
boxShadow: [ |
|
|
|
BoxShadow( |
|
|
|
color: Colors.black.withOpacity(0.05), |
|
|
|
blurRadius: 5, |
|
|
|
offset: Offset(0, 2), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
child: Column( |
|
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Expanded( |
|
|
|
child: Text( |
|
|
|
item.nom, |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
), |
|
|
|
IconButton( |
|
|
|
onPressed: () => _removeItem(index), |
|
|
|
icon: Icon( |
|
|
|
Icons.delete_outline, |
|
|
|
color: Colors.red, |
|
|
|
), |
|
|
|
constraints: BoxConstraints(), |
|
|
|
padding: EdgeInsets.zero, |
|
|
|
), |
|
|
|
IconButton( |
|
|
|
onPressed: () => _removeItem(index), |
|
|
|
icon: Icon( |
|
|
|
Icons.delete_outline, |
|
|
|
color: Colors.red, |
|
|
|
), |
|
|
|
], |
|
|
|
constraints: BoxConstraints(), |
|
|
|
padding: EdgeInsets.zero, |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
Text( |
|
|
|
'${item.prix.toStringAsFixed(2)} MGA l\'unité', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 14, |
|
|
|
color: Colors.grey[600], |
|
|
|
), |
|
|
|
), |
|
|
|
if (item.notes.isNotEmpty) ...[ |
|
|
|
SizedBox(height: 8), |
|
|
|
Text( |
|
|
|
'${item.prix.toStringAsFixed(2)} MGA l\'unité', |
|
|
|
'Notes: ${item.notes}', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 14, |
|
|
|
color: Colors.grey[600], |
|
|
|
fontStyle: FontStyle.italic, |
|
|
|
), |
|
|
|
), |
|
|
|
if (item.notes.isNotEmpty) ...[ |
|
|
|
SizedBox(height: 8), |
|
|
|
Text( |
|
|
|
'Notes: ${item.notes}', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 14, |
|
|
|
color: Colors.grey[600], |
|
|
|
fontStyle: FontStyle.italic, |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
SizedBox(height: 16), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: |
|
|
|
MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
// Contrôles de quantité |
|
|
|
Row( |
|
|
|
children: [ |
|
|
|
IconButton( |
|
|
|
onPressed: |
|
|
|
() => _updateQuantity( |
|
|
|
index, |
|
|
|
item.quantity - 1, |
|
|
|
), |
|
|
|
icon: Icon(Icons.remove), |
|
|
|
style: IconButton.styleFrom( |
|
|
|
backgroundColor: Colors.grey[200], |
|
|
|
minimumSize: Size(40, 40), |
|
|
|
), |
|
|
|
], |
|
|
|
SizedBox(height: 16), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
// Contrôles de quantité |
|
|
|
Row( |
|
|
|
children: [ |
|
|
|
IconButton( |
|
|
|
onPressed: () => _updateQuantity( |
|
|
|
index, |
|
|
|
item.quantity - 1, |
|
|
|
), |
|
|
|
icon: Icon(Icons.remove), |
|
|
|
style: IconButton.styleFrom( |
|
|
|
backgroundColor: Colors.grey[200], |
|
|
|
minimumSize: Size(40, 40), |
|
|
|
), |
|
|
|
), |
|
|
|
SizedBox(width: 16), |
|
|
|
Text( |
|
|
|
item.quantity.toString(), |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
SizedBox(width: 16), |
|
|
|
Text( |
|
|
|
item.quantity.toString(), |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
), |
|
|
|
SizedBox(width: 16), |
|
|
|
IconButton( |
|
|
|
onPressed: () => _updateQuantity( |
|
|
|
index, |
|
|
|
item.quantity + 1, |
|
|
|
), |
|
|
|
SizedBox(width: 16), |
|
|
|
IconButton( |
|
|
|
onPressed: |
|
|
|
() => _updateQuantity( |
|
|
|
index, |
|
|
|
item.quantity + 1, |
|
|
|
), |
|
|
|
icon: Icon(Icons.add), |
|
|
|
style: IconButton.styleFrom( |
|
|
|
backgroundColor: Colors.grey[200], |
|
|
|
minimumSize: Size(40, 40), |
|
|
|
), |
|
|
|
icon: Icon(Icons.add), |
|
|
|
style: IconButton.styleFrom( |
|
|
|
backgroundColor: Colors.grey[200], |
|
|
|
minimumSize: Size(40, 40), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
// Prix total de l'article |
|
|
|
Text( |
|
|
|
'${(item.prix * item.quantity).toStringAsFixed(2)} MGA', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
color: Colors.green[700], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
// Prix total de l'article |
|
|
|
Text( |
|
|
|
'${(item.prix * item.quantity).toStringAsFixed(2)} MGA', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
color: Colors.green[700], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
); |
|
|
|
}, |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
// Récapitulatif |
|
|
|
Container( |
|
|
|
padding: EdgeInsets.all(20), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: Colors.white, |
|
|
|
border: Border(top: BorderSide(color: Colors.grey[200]!)), |
|
|
|
), |
|
|
|
child: Column( |
|
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
Text( |
|
|
|
'Récapitulatif', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 20, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
), |
|
|
|
SizedBox(height: 16), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text('Articles:', style: TextStyle(fontSize: 16)), |
|
|
|
Text( |
|
|
|
_getTotalArticles().toString(), |
|
|
|
style: TextStyle(fontSize: 16), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
SizedBox(height: 8), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text('Table:', style: TextStyle(fontSize: 16)), |
|
|
|
Text( |
|
|
|
widget.tableId.toString(), |
|
|
|
style: TextStyle(fontSize: 16), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
SizedBox(height: 8), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text('Personnes:', style: TextStyle(fontSize: 16)), |
|
|
|
Text( |
|
|
|
widget.personne.toString(), |
|
|
|
style: TextStyle(fontSize: 16), |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
}, |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
// Récapitulatif |
|
|
|
Container( |
|
|
|
padding: EdgeInsets.all(20), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: Colors.white, |
|
|
|
border: Border(top: BorderSide(color: Colors.grey[200]!)), |
|
|
|
), |
|
|
|
child: Column( |
|
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
Text( |
|
|
|
'Récapitulatif', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 20, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
SizedBox(height: 16), |
|
|
|
Divider(), |
|
|
|
SizedBox(height: 8), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text( |
|
|
|
'Total:', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
), |
|
|
|
SizedBox(height: 16), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text('Articles:', style: TextStyle(fontSize: 16)), |
|
|
|
Text( |
|
|
|
_getTotalArticles().toString(), |
|
|
|
style: TextStyle(fontSize: 16), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
SizedBox(height: 8), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text('Table:', style: TextStyle(fontSize: 16)), |
|
|
|
Text( |
|
|
|
widget.tableId.toString(), |
|
|
|
style: TextStyle(fontSize: 16), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
SizedBox(height: 8), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text('Personnes:', style: TextStyle(fontSize: 16)), |
|
|
|
Text( |
|
|
|
widget.personne.toString(), |
|
|
|
style: TextStyle(fontSize: 16), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
SizedBox(height: 16), |
|
|
|
Divider(), |
|
|
|
SizedBox(height: 8), |
|
|
|
Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text( |
|
|
|
'Total:', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
Text( |
|
|
|
'${_calculateTotal().toStringAsFixed(2)} MGA', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
), |
|
|
|
Text( |
|
|
|
'${_calculateTotal().toStringAsFixed(2)} MGA', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 18, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
SizedBox(height: 20), |
|
|
|
SizedBox( |
|
|
|
width: double.infinity, |
|
|
|
child: ElevatedButton( |
|
|
|
onPressed: |
|
|
|
_cartItems.isNotEmpty && !_isValidating |
|
|
|
? _showConfirmationDialog |
|
|
|
: null, |
|
|
|
style: ElevatedButton.styleFrom( |
|
|
|
backgroundColor: Colors.green[700], |
|
|
|
foregroundColor: Colors.white, |
|
|
|
padding: EdgeInsets.symmetric(vertical: 16), |
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
borderRadius: BorderRadius.circular(12), |
|
|
|
), |
|
|
|
disabledBackgroundColor: Colors.grey[300], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
SizedBox(height: 20), |
|
|
|
SizedBox( |
|
|
|
width: double.infinity, |
|
|
|
child: ElevatedButton( |
|
|
|
onPressed: _cartItems.isNotEmpty && !_isValidating |
|
|
|
? _showConfirmationDialog |
|
|
|
: null, |
|
|
|
style: ElevatedButton.styleFrom( |
|
|
|
backgroundColor: Colors.green[700], |
|
|
|
foregroundColor: Colors.white, |
|
|
|
padding: EdgeInsets.symmetric(vertical: 16), |
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
borderRadius: BorderRadius.circular(12), |
|
|
|
), |
|
|
|
child: Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
if (_isValidating) ...[ |
|
|
|
SizedBox( |
|
|
|
width: 20, |
|
|
|
height: 20, |
|
|
|
child: CircularProgressIndicator( |
|
|
|
strokeWidth: 2, |
|
|
|
valueColor: AlwaysStoppedAnimation<Color>( |
|
|
|
Colors.white, |
|
|
|
), |
|
|
|
disabledBackgroundColor: Colors.grey[300], |
|
|
|
), |
|
|
|
child: Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
if (_isValidating) ...[ |
|
|
|
SizedBox( |
|
|
|
width: 20, |
|
|
|
height: 20, |
|
|
|
child: CircularProgressIndicator( |
|
|
|
strokeWidth: 2, |
|
|
|
valueColor: AlwaysStoppedAnimation<Color>( |
|
|
|
Colors.white, |
|
|
|
), |
|
|
|
), |
|
|
|
SizedBox(width: 8), |
|
|
|
Text( |
|
|
|
'Validation en cours...', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 16, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
), |
|
|
|
SizedBox(width: 8), |
|
|
|
Text( |
|
|
|
'Validation en cours...', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 16, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
] else ...[ |
|
|
|
Icon(Icons.check, size: 20), |
|
|
|
SizedBox(width: 8), |
|
|
|
Text( |
|
|
|
'Valider la commande', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 16, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
), |
|
|
|
] else ...[ |
|
|
|
Icon(Icons.check, size: 20), |
|
|
|
SizedBox(width: 8), |
|
|
|
Text( |
|
|
|
'Valider la commande', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 16, |
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|