import 'package:flutter/material.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/theme/app_typography.dart'; import '../../../core/utils/formatters.dart'; import '../../../domain/entities/cart.dart'; /// One row of the bill, with inline quantity stepper. class CartLineTile extends StatelessWidget { const CartLineTile({ super.key, required this.line, required this.onIncrement, required this.onDecrement, required this.onRemove, this.onDiscount, }); final CartLine line; final VoidCallback onIncrement; final VoidCallback onDecrement; final VoidCallback onRemove; final VoidCallback? onDiscount; @override Widget build(BuildContext context) { final p = line.product; return Dismissible( key: ValueKey('dismiss_${p.id}'), direction: DismissDirection.endToStart, onDismissed: (_) => onRemove(), background: Container( alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: AppSpacing.xl), decoration: const BoxDecoration( color: AppColors.dangerSurface, borderRadius: AppRadius.brMd, ), child: const Icon(Icons.delete_outline_rounded, color: AppColors.danger,), ), child: Container( padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( color: AppColors.surfaceAlt, borderRadius: AppRadius.brMd, border: Border.all( color: line.exceedsStock ? AppColors.danger : AppColors.border, ), ), child: Column(children: [ Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 42, height: 42, decoration: BoxDecoration( color: AppColors.surface, borderRadius: AppRadius.brSm, border: Border.all(color: AppColors.border), ), alignment: Alignment.center, child: Text(p.emoji, style: const TextStyle(fontSize: 21)), ), const SizedBox(width: AppSpacing.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( p.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 14.5, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 2), Row(children: [ Text( Formatters.money(p.price), style: AppTypography.money(13.5, weight: FontWeight.w600, color: AppColors.textSecondary,), ), Text(' / ${p.unit.symbol}', style: const TextStyle( fontSize: 11.5, color: AppColors.textTertiary, ),), if (line.discount.isActive) ...[ const SizedBox(width: AppSpacing.sm), Container( padding: const EdgeInsets.symmetric( horizontal: 5, vertical: 1,), decoration: BoxDecoration( color: AppColors.successSurface, borderRadius: BorderRadius.circular(4), ), child: Text( line.discount.label, style: const TextStyle( fontSize: 10, color: AppColors.success, fontWeight: FontWeight.w700, ), ), ), ], ],), ], ), ), IconButton( onPressed: onRemove, icon: const Icon(Icons.close_rounded, size: 18), color: AppColors.textTertiary, constraints: const BoxConstraints(minWidth: 32, minHeight: 32), padding: EdgeInsets.zero, tooltip: 'Remove', ), ],), const SizedBox(height: AppSpacing.sm), Row(children: [ _Stepper( quantity: line.quantity, unit: p.unit.symbol, onIncrement: onIncrement, onDecrement: onDecrement, ), if (onDiscount != null) ...[ const SizedBox(width: AppSpacing.sm), IconButton( onPressed: onDiscount, icon: const Icon(Icons.local_offer_outlined, size: 17), color: AppColors.textSecondary, constraints: const BoxConstraints(minWidth: 34, minHeight: 34), padding: EdgeInsets.zero, tooltip: 'Line discount', ), ], const Spacer(), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ if (line.discountAmount > 0) Text( Formatters.money(line.grossAmount), style: const TextStyle( fontSize: 11.5, color: AppColors.textTertiary, decoration: TextDecoration.lineThrough, ), ), Text( Formatters.money(line.payable), style: AppTypography.money(16), ), ], ), ],), if (line.exceedsStock) Padding( padding: const EdgeInsets.only(top: AppSpacing.sm), child: Row(children: [ const Icon(Icons.error_outline_rounded, size: 14, color: AppColors.danger,), const SizedBox(width: AppSpacing.xs), Text( 'Only ${p.stock.toStringAsFixed(0)} ${p.unit.symbol} ' 'available', style: const TextStyle( fontSize: 11.5, color: AppColors.danger, fontWeight: FontWeight.w600, ), ), ],), ), ],), ), ); } } class _Stepper extends StatelessWidget { const _Stepper({ required this.quantity, required this.unit, required this.onIncrement, required this.onDecrement, }); final double quantity; final String unit; final VoidCallback onIncrement; final VoidCallback onDecrement; @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: AppColors.surface, borderRadius: AppRadius.brSm, border: Border.all(color: AppColors.border), ), child: Row(mainAxisSize: MainAxisSize.min, children: [ _btn(Icons.remove_rounded, onDecrement), Container( constraints: const BoxConstraints(minWidth: 42), alignment: Alignment.center, child: Text( quantity % 1 == 0 ? quantity.toStringAsFixed(0) : quantity.toStringAsFixed(2), style: AppTypography.money(15.5), ), ), _btn(Icons.add_rounded, onIncrement), ],), ); } Widget _btn(IconData icon, VoidCallback onTap) => Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: AppRadius.brSm, child: SizedBox( width: 34, height: 34, child: Icon(icon, size: 17, color: AppColors.primary), ), ), ); }