import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../../core/app_colors.dart'; import '../../di/service_locator.dart'; import '../../domain/entities/payment_card.dart'; import '../../domain/usecase/add_payment_card_usecase.dart'; import '../../domain/usecase/get_payment_cards_usecase.dart'; import '../../domain/usecase/get_profile_usecase.dart'; import '../../domain/usecase/set_main_payment_card_usecase.dart'; import '../components/custom_app_bar.dart'; import '../components/full_screen_image_viewer.dart'; import '../components/gradient_button.dart'; import '../components/payment_option.dart'; import '../components/sheet/payment_method_sheet.dart'; import '../event/fine_payment_event.dart'; import '../event/payment_method_sheet_event.dart'; import '../state/fine_payment_state.dart'; import '../viewmodel/fine_payment_bloc.dart'; import '../viewmodel/payment_method_sheet_bloc.dart'; class FinePaymentScreen extends StatelessWidget { final int fineId; const FinePaymentScreen({ super.key, required this.fineId, }); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => FinePaymentBloc( getFineDetailsUsecase: getIt(), payFineUsecase: getIt(), )..add(FetchFineDetails(fineId)), child: const _FinePaymentScreenContent(), ); } } class _FinePaymentScreenContent extends StatelessWidget { const _FinePaymentScreenContent(); void _openFullScreenImage(BuildContext context, List photos, int initialIndex) { Navigator.of(context).push( MaterialPageRoute( builder: (_) => FullScreenImageViewer( photoUrls: photos, initialIndex: initialIndex, ), ), ); } @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: const BoxDecoration(gradient: AppColors.phoneScreenBg), child: SafeArea( child: Stack( children: [ Positioned( bottom: 60, left: 0, right: 0, child: Image.asset( 'assets/wave.png', fit: BoxFit.fitWidth, color: Colors.white.withOpacity(0.1), ), ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 16), const Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: CustomAppBar(title: 'Оплата штрафа'), ), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24), child: BlocConsumer( listenWhen: (previous, current) => previous.status != current.status, listener: (context, state) { if (state.status == FinePaymentStatus.success && state.paymentCompleted) { context.go('/home'); } else if (state.status == FinePaymentStatus.failure) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( state.errorMessage ?? 'Произошла ошибка при оплате штрафа', style: const TextStyle(color: Colors.white), ), backgroundColor: Colors.redAccent.withOpacity(0.9), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), margin: const EdgeInsets.all(20), duration: const Duration(seconds: 3), ), ); } }, builder: (context, state) { final fine = state.fine; if (state.status == FinePaymentStatus.loading && fine == null) { return const SizedBox( height: 400, child: Center( child: CircularProgressIndicator(color: Colors.white), ), ); } if (fine == null) return const SizedBox.shrink(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 20), if (fine.photoUrls.isNotEmpty) SizedBox( height: 110, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: fine.photoUrls.length, separatorBuilder: (_, __) => const SizedBox(width: 12), itemBuilder: (context, index) { final photo = fine.photoUrls[index]; final isNetwork = photo.startsWith('http'); return GestureDetector( onTap: () => _openFullScreenImage( context, fine.photoUrls, index, ), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: isNetwork ? Image.network( photo, width: 110, height: 110, fit: BoxFit.cover, ) : Image.asset( photo, width: 110, height: 110, fit: BoxFit.cover, ), ), ); }, ), ), const SizedBox(height: 24), _buildInfoLabel('Самокат:'), _buildValueRow( 'qr_icon.png', fine.scooterNumber, ), const SizedBox(height: 20), _buildInfoLabel('Дата:'), Text( fine.date, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 20), _buildInfoLabel('Описание:'), Text( fine.description, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 28), Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16), decoration: BoxDecoration( color: Colors.white.withOpacity(0.07), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.white.withOpacity(0.2), width: 1, ), ), child: Column( children: [ Text( 'Сумма штрафа:', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 16, ), ), const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( 'assets/icons/money_icon.png', width: 36, height: 36, ), const SizedBox(width: 10), Text( fine.amountPrint, style: const TextStyle( color: Colors.white, fontSize: 28, fontWeight: FontWeight.bold, ), ), ], ), ], ), ), const SizedBox(height: 28), (state.useBalance || state.selectedCard != null) ? PaymentOption( title: state.useBalance ? 'Баланс' : "", subtitle: state.useBalance ? '${state.userBalance.toStringAsFixed(2)} BYN' : '${state.selectedCard!.cardLastNumber}', isSelected: true, onTap: () async { final result = await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (innerContext) => BlocProvider( create: (context) => PaymentMethodSheetBloc( getIt(), getIt(), getIt(), getIt(), )..add(PaymentMethodSheetStarted()), child: PaymentMethodSheet( initialSelectedCard: state.useBalance ? null : state.selectedCard, ), ), ); if (result != null && context.mounted) { if (result is PaymentCard) { context.read().add(FinePaymentCardChanged(result)); } else if (result == 'balance') { context.read().add(SelectFineBalancePressed()); } } }, ) : Container( height: 56, decoration: BoxDecoration( borderRadius: BorderRadius.circular(24), border: Border.all( color: Colors.white.withOpacity(0.4), width: 1, ), ), child: Material( color: Colors.transparent, child: InkWell( onTap: () async { final result = await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (innerContext) => BlocProvider( create: (context) => PaymentMethodSheetBloc( getIt(), getIt(), getIt(), getIt(), )..add(PaymentMethodSheetStarted()), child: const PaymentMethodSheet(), ), ); if (result != null && context.mounted) { if (result is PaymentCard) { context.read().add(FinePaymentCardChanged(result)); } else if (result == 'balance') { context.read().add(SelectFineBalancePressed()); } } }, borderRadius: BorderRadius.circular(24), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Способ оплаты', style: TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600, ), ), const SizedBox(width: 8), Icon( Icons.arrow_forward_ios, size: 12, color: Colors.white.withOpacity(0.6), ), Icon( Icons.arrow_forward_ios, size: 12, color: Colors.white.withOpacity(0.4), ), Icon( Icons.arrow_forward_ios, size: 12, color: Colors.white.withOpacity(0.2), ), ], ), ), ), ), ), const SizedBox(height: 20), ], ); }, ), ), ), Padding( padding: const EdgeInsets.all(24.0), child: BlocBuilder( builder: (context, state) { final isButtonDisabled = state.status == FinePaymentStatus.loading || (!state.useBalance && state.selectedCard == null); return GradientButton( text: state.status == FinePaymentStatus.loading ? 'Обработка...' : 'Оплатить', showArrows: true, height: 56, width: double.infinity, onTap: isButtonDisabled ? null : () { context.read().add(PayFineSubmitted()); }, ); }, ), ), ], ), ], ), ), ), ); } Widget _buildInfoLabel(String text) { return Padding( padding: const EdgeInsets.only(bottom: 6), child: Text( text, style: TextStyle( color: Colors.white.withOpacity(0.7), fontSize: 15, ), ), ); } Widget _buildValueRow(String iconName, String value) { return Row( children: [ Image.asset('assets/icons/$iconName', width: 24, height: 24), const SizedBox(width: 12), Text( value, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w500, ), ), ], ); } }