Files
be_happy_public/lib/presentation/screens/top_up_screen.dart
Polyanka 182be1049b some fix
# Conflicts:
#	lib/presentation/navigation/app_router.dart
#	pubspec.yaml
2026-08-11 00:23:16 +03:00

289 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:be_happy/domain/usecase/add_payment_card_usecase.dart';
import 'package:be_happy/presentation/viewmodel/payment_methods_bloc.dart';
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/get_payment_cards_usecase.dart';
import '../../domain/usecase/get_profile_usecase.dart';
import '../../domain/usecase/set_main_payment_card_usecase.dart';
import '../components/app_checkbox.dart';
import '../components/custom_app_bar.dart';
import '../components/gradient_button.dart';
import '../components/payment_option.dart';
import '../components/sheet/payment_method_sheet.dart';
import '../event/payment_method_sheet_event.dart';
import '../event/payment_methods_event.dart';
import '../event/top_up_event.dart';
import '../state/top_up_state.dart';
import '../viewmodel/payment_method_sheet_bloc.dart';
import '../viewmodel/top_up_bloc.dart';
class TopUpScreen extends StatelessWidget {
const TopUpScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(gradient: AppColors.phoneScreenBg),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: BlocListener<TopUpBloc, TopUpState>(
listener: (context, state) {
if (state is TopUpPaymentSuccess) {
context.go('/home/payment-methods/top-up/result/success');
} else if (state is TopUpPaymentFailure) {
context.go('/home/payment-methods/top-up/result/failed');
}
},
child: Column(
children: [
const SizedBox(height: 16),
const CustomAppBar(title: 'Пополнение баланса'),
const SizedBox(height: 20),
BlocBuilder<TopUpBloc, TopUpState>(
builder: (context, state) {
if (state is TopUpLoading) {
return const Expanded(
child: Center(child: CircularProgressIndicator()),
);
}
if (state is TopUpLoaded) {
if (state.isProcessingPayment) {
return const Expanded(
child: Center(child: CircularProgressIndicator()),
);
}
return Expanded(
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTariffList(state, context),
const SizedBox(height: 30),
_buildPriceInfo(state),
const SizedBox(height: 40),
const Text(
'Способ оплаты',
style: TextStyle(color: Colors.white70),
),
const SizedBox(height: 15),
_buildCardSelector(state, context),
const SizedBox(height: 20),
_buildAgreement(state, context),
const Spacer(),
_buildPayButton(state, () {
context.read<TopUpBloc>().add(TopUpButtonPressed());
}),
const SizedBox(height: 30),
],
),
],
),
);
}
return const SizedBox.shrink();
},
),
],
),
),
),
),
),
);
}
Widget _buildTariffList(TopUpLoaded state, BuildContext context) {
return SizedBox(
height: 120,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: state.certificates.length,
separatorBuilder: (_, __) => const SizedBox(width: 12),
itemBuilder: (context, index) {
final tariff = state.certificates[index];
final isSelected = state.selectedTariff == tariff;
return GestureDetector(
onTap: () => context.read<TopUpBloc>().add(SelectCertificate(tariff)),
child: Container(
width: 140,
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF80FFC1) : Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white24),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (tariff.discount != null)
Text(
'скидка: ${tariff.discount!.toInt()}%',
style: TextStyle(
color: isSelected ? Colors.black87 : Colors.tealAccent,
),
),
Text(
'${tariff.value} баллов',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isSelected ? Colors.black : Colors.white,
),
),
],
),
),
);
},
),
);
}
Widget _buildPriceInfo(TopUpLoaded state) {
if (state.selectedTariff == null) return const SizedBox.shrink();
return Center(
child: Column(
children: [
Text(
'Купить со скидкой ${state.selectedTariff!.discount?.toInt()}%:',
style: const TextStyle(color: Colors.white, fontSize: 16),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset("assets/icons/money_icon.png", width: 24, height: 24),
const SizedBox(width: 8),
Text(
'${state.selectedTariff!.price.toStringAsFixed(2)} BYN',
style: const TextStyle(
color: Color(0xFF80FFC1),
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
],
),
],
),
);
}
Widget _buildCardSelector(TopUpLoaded state, BuildContext context) {
return state.selectedCard != null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: PaymentOption(
title: "Карта",
subtitle: '${state.selectedCard!.cardLastNumber}',
isSelected: true,
onTap: () async {
final selectedCard = await showModalBottomSheet<PaymentCard>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (innerContext) => BlocProvider(
create: (context) => PaymentMethodSheetBloc(
getIt<GetPaymentCardsUsecase>(),
getIt<SetMainPaymentCardUsecase>(),
getIt<GetProfileUseCase>(),
getIt<AddPaymentCardUsecase>(),
)..add(PaymentMethodSheetStarted()),
child: const PaymentMethodSheet(showBalance: false),
),
);
if (selectedCard != null) {
context.read<TopUpBloc>().add(SelectCard(selectedCard));
}
},
),
)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: 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: () {
context.pushReplacement('/home/payment-method-sheet');
},
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)),
],
),
),
),
),
),
);
}
Widget _buildAgreement(TopUpLoaded state, BuildContext context) {
return Row(
children: [
AppCheckbox(
value: state.isAgreed,
onChanged: (v) => context.read<TopUpBloc>().add(ToggleAgreement(v ?? false)),
),
const SizedBox(width: 12),
const Flexible(
child: Text.rich(
TextSpan(
text: 'Я принимаю условия покупки бонусного пакека, при котором денежные средства не подлежат возврату... ',
style: TextStyle(
color: AppColors.white70,
fontSize: 12,
),
),
),
),
],
);
}
Widget _buildPayButton(TopUpLoaded state, VoidCallback onTap) {
return GradientButton(
text: 'Оплатить',
onTap: state.isAgreed ? onTap : null,
enabled: state.isAgreed,
showArrows: true,
height: 56,
width: double.infinity,
fontSize: 16,
);
}
}