143 lines
4.3 KiB
Dart
143 lines
4.3 KiB
Dart
import 'package:flutter/material.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 PaymentCardSelector extends StatelessWidget {
|
|
final String? selectedCardId;
|
|
final ValueChanged<String?> onChanged;
|
|
final Future<void> Function()? onAddCard;
|
|
final VoidCallback? onManageCards;
|
|
|
|
const PaymentCardSelector({
|
|
super.key,
|
|
required this.selectedCardId,
|
|
required this.onChanged,
|
|
this.onAddCard,
|
|
this.onManageCards,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Consumer<PaymentCardProvider>(
|
|
builder: (context, provider, child) {
|
|
final loc = AppLocalizations.of(context);
|
|
final cards = provider.cards;
|
|
final unassignedSelected = selectedCardId == null;
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Wrap(
|
|
spacing: 8,
|
|
runSpacing: 8,
|
|
children: [
|
|
Semantics(
|
|
label: loc.paymentCardUnassigned,
|
|
selected: unassignedSelected,
|
|
button: true,
|
|
child: ChoiceChip(
|
|
label: Text(loc.paymentCardUnassigned),
|
|
selected: unassignedSelected,
|
|
onSelected: (_) => onChanged(null),
|
|
avatar: const Icon(Icons.credit_card_off_rounded, size: 18),
|
|
),
|
|
),
|
|
...cards.map((card) => _PaymentCardChip(
|
|
card: card,
|
|
isSelected: selectedCardId == card.id,
|
|
onSelected: () => onChanged(card.id),
|
|
)),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
TextButton.icon(
|
|
onPressed: cards.isEmpty && onAddCard == null
|
|
? null
|
|
: () async {
|
|
if (onAddCard != null) {
|
|
await onAddCard!();
|
|
}
|
|
},
|
|
icon: const Icon(Icons.add),
|
|
label: Text(loc.addNewCard),
|
|
),
|
|
const SizedBox(width: 8),
|
|
TextButton(
|
|
onPressed: onManageCards,
|
|
child: Text(loc.managePaymentCards),
|
|
),
|
|
],
|
|
),
|
|
if (cards.isEmpty)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 8),
|
|
child: Text(
|
|
loc.noPaymentCards,
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
fontSize: 13,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _PaymentCardChip extends StatelessWidget {
|
|
final PaymentCardModel card;
|
|
final bool isSelected;
|
|
final VoidCallback onSelected;
|
|
|
|
const _PaymentCardChip({
|
|
required this.card,
|
|
required this.isSelected,
|
|
required this.onSelected,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final color = PaymentCardUtils.colorFromHex(card.colorHex);
|
|
final icon = PaymentCardUtils.iconForName(card.iconName);
|
|
final cs = Theme.of(context).colorScheme;
|
|
final labelText = '${card.issuerName} · ****${card.last4}';
|
|
return Semantics(
|
|
label: labelText,
|
|
selected: isSelected,
|
|
button: true,
|
|
child: ChoiceChip(
|
|
avatar: CircleAvatar(
|
|
backgroundColor:
|
|
isSelected ? cs.onPrimary : color.withValues(alpha: 0.15),
|
|
child: Icon(
|
|
icon,
|
|
color: isSelected ? color : cs.onSurface,
|
|
size: 16,
|
|
),
|
|
),
|
|
label: Text(labelText),
|
|
selected: isSelected,
|
|
onSelected: (_) => onSelected(),
|
|
selectedColor: color,
|
|
labelStyle: TextStyle(
|
|
color: isSelected ? cs.onPrimary : cs.onSurface,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
backgroundColor: cs.surface,
|
|
side: BorderSide(
|
|
color: isSelected
|
|
? Colors.transparent
|
|
: cs.outline.withValues(alpha: 0.5),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|