import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../../l10n/app_localizations.dart'; import '../../models/payment_card_model.dart'; import '../../providers/payment_card_provider.dart'; import '../../utils/payment_card_utils.dart'; class PaymentCardFormSheet extends StatefulWidget { final PaymentCardModel? card; final String? initialIssuerName; final String? initialLast4; final String? initialColorHex; final String? initialIconName; const PaymentCardFormSheet({ super.key, this.card, this.initialIssuerName, this.initialLast4, this.initialColorHex, this.initialIconName, }); static Future show( BuildContext context, { PaymentCardModel? card, String? initialIssuerName, String? initialLast4, String? initialColorHex, String? initialIconName, }) async { return showModalBottomSheet( context: context, isScrollControlled: true, useSafeArea: true, builder: (_) => PaymentCardFormSheet( card: card, initialIssuerName: initialIssuerName, initialLast4: initialLast4, initialColorHex: initialColorHex, initialIconName: initialIconName, ), ); } @override State createState() => _PaymentCardFormSheetState(); } class _PaymentCardFormSheetState extends State { final _formKey = GlobalKey(); late TextEditingController _issuerController; late TextEditingController _last4Controller; late String _selectedColor; late String _selectedIcon; late bool _isDefault; bool _isSaving = false; @override void initState() { super.initState(); _issuerController = TextEditingController( text: widget.card?.issuerName ?? widget.initialIssuerName ?? '', ); _last4Controller = TextEditingController( text: widget.card?.last4 ?? widget.initialLast4 ?? '', ); _selectedColor = widget.card?.colorHex ?? widget.initialColorHex ?? PaymentCardUtils.colorPalette.first; _selectedIcon = widget.card?.iconName ?? widget.initialIconName ?? PaymentCardUtils.iconMap.keys.first; _isDefault = widget.card?.isDefault ?? false; } @override void dispose() { _issuerController.dispose(); _last4Controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); final isEditing = widget.card != null; return Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(24, 24, 24, 32), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( isEditing ? loc.editPaymentCard : loc.addPaymentCard, style: Theme.of(context).textTheme.titleLarge, ), const Spacer(), IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.close), ), ], ), const SizedBox(height: 16), Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( controller: _issuerController, decoration: InputDecoration( labelText: loc.paymentCardIssuer, border: const OutlineInputBorder(), ), textInputAction: TextInputAction.next, validator: (value) { if (value == null || value.trim().isEmpty) { return loc.requiredFieldsError; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _last4Controller, decoration: InputDecoration( labelText: loc.paymentCardLast4, border: const OutlineInputBorder(), counterText: '', ), keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(4), ], validator: (value) { if (value == null || value.length != 4) { return loc.paymentCardLast4; } return null; }, ), const SizedBox(height: 16), Text( loc.paymentCardColor, style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), Wrap( spacing: 8, runSpacing: 8, children: PaymentCardUtils.colorPalette.map((hex) { final color = PaymentCardUtils.colorFromHex(hex); final selected = _selectedColor == hex; return GestureDetector( onTap: () { setState(() { _selectedColor = hex; }); }, child: Container( width: 36, height: 36, decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: Border.all( color: selected ? Theme.of(context).colorScheme.onSurface : Colors.transparent, width: 2, ), ), ), ); }).toList(), ), const SizedBox(height: 16), Text( loc.paymentCardIcon, style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), Wrap( spacing: 8, runSpacing: 8, children: PaymentCardUtils.iconMap.entries.map((entry) { final selected = _selectedIcon == entry.key; return ChoiceChip( label: Icon(entry.value, color: selected ? Theme.of(context).colorScheme.onPrimary : Theme.of(context).colorScheme.onSurface), selected: selected, onSelected: (_) { setState(() { _selectedIcon = entry.key; }); }, selectedColor: Theme.of(context).colorScheme.primary, backgroundColor: Theme.of(context) .colorScheme .surfaceContainerHighest, ); }).toList(), ), const SizedBox(height: 16), SwitchListTile( contentPadding: EdgeInsets.zero, title: Text(loc.setAsDefaultCard), value: _isDefault, onChanged: (value) { setState(() { _isDefault = value; }); }, ), const SizedBox(height: 24), SizedBox( width: double.infinity, child: FilledButton( onPressed: _isSaving ? null : _handleSubmit, child: _isSaving ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : Text(loc.save), ), ), ], ), ), ], ), ), ); } Future _handleSubmit() async { if (!_formKey.currentState!.validate()) return; setState(() { _isSaving = true; }); try { final provider = context.read(); String cardId; if (widget.card == null) { final card = await provider.addCard( issuerName: _issuerController.text.trim(), last4: _last4Controller.text.trim(), colorHex: _selectedColor, iconName: _selectedIcon, isDefault: _isDefault, ); cardId = card.id; } else { widget.card! ..issuerName = _issuerController.text.trim() ..last4 = _last4Controller.text.trim() ..colorHex = _selectedColor ..iconName = _selectedIcon ..isDefault = _isDefault; await provider.updateCard(widget.card!); cardId = widget.card!.id; } if (mounted) { Navigator.of(context).pop(cardId); } } finally { if (mounted) { setState(() { _isSaving = false; }); } } } }