new project stable version
This commit is contained in:
288
lib/presentation/screens/payment_methods_screen.dart
Normal file
288
lib/presentation/screens/payment_methods_screen.dart
Normal file
@@ -0,0 +1,288 @@
|
||||
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 '../../domain/entities/payment_card.dart';
|
||||
import '../components/custom_app_bar.dart';
|
||||
import '../event/payment_methods_event.dart';
|
||||
import '../state/payment_methods_state.dart';
|
||||
import '../viewmodel/payment_methods_bloc.dart';
|
||||
|
||||
class PaymentMethodsScreen extends StatelessWidget {
|
||||
const PaymentMethodsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
decoration: const BoxDecoration(gradient: AppColors.phoneScreenBg),
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomAppBar(title: 'Способы оплаты'),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Expanded(
|
||||
child: BlocConsumer<PaymentMethodsBloc, PaymentMethodsState>(
|
||||
listener: (context, state) {
|
||||
if (state.status == PaymentMethodsStatus.failure) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(state.errorMessage ?? 'Ошибка')),
|
||||
);
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
if (state.status == PaymentMethodsStatus.loading && state.cards.isEmpty) {
|
||||
return const Center(child: CircularProgressIndicator(color: Color(0xFF00D4AA)));
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildBalanceCard(context, state.balance),
|
||||
const SizedBox(height: 20),
|
||||
_buildCardsList(context, state),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBalanceCard(BuildContext context, int balance) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
gradient: AppColors.activeButtonGradient,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Баланс',
|
||||
style: TextStyle(color: Color(0xFF0A0F2E), fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text(
|
||||
balance.toStringAsFixed(2),
|
||||
style: TextStyle(color: Color(0xFF0A0F2E), fontSize: 32, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'баллов',
|
||||
style: TextStyle(color: const Color(0xFF0A0F2E).withOpacity(0.7), fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildTopUpBalanceButton(context),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
right: -30,
|
||||
top: -50,
|
||||
child: Image.asset('assets/icons/card-screen.png', width: 100, height: 100, fit: BoxFit.contain),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCardsList(BuildContext context, PaymentMethodsState state) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF0A0F2E).withOpacity(0.65),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Карты',
|
||||
style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
if (state.cards.isEmpty && state.status == PaymentMethodsStatus.success)
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 20),
|
||||
child: Text('У вас пока нет привязанных карт', style: TextStyle(color: Colors.white70)),
|
||||
),
|
||||
|
||||
...state.cards.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final card = entry.value;
|
||||
return Column(
|
||||
children: [
|
||||
_CardItem(
|
||||
card: card,
|
||||
onDelete: () => context.read<PaymentMethodsBloc>().add(PaymentMethodsDeleteCard(card.id)),
|
||||
onMakeMain: card.isMain
|
||||
? null
|
||||
: () => context.read<PaymentMethodsBloc>().add(PaymentMethodsSetMainCard(card.id)),
|
||||
),
|
||||
if (index < state.cards.length - 1) const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
_buildAddCardButton(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAddCardButton(BuildContext context) {
|
||||
return Container(
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF0A0F2E),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => context.go('/home/payment-methods/add-card'),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.credit_card, color: Color(0xFF00D4AA), size: 24),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Привязать карту',
|
||||
style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
Icon(Icons.add, color: Color(0xFF00D4AA), size: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTopUpBalanceButton(BuildContext context) {
|
||||
return Container(
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF0A0F2E),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => context.go('/home/payment-methods/top-up'),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset("assets/icons/money_icon.png", width: 24, height: 24),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Пополнить баланс',
|
||||
style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
Icon(Icons.add, color: Color(0xFF00D4AA), size: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class _CardItem extends StatelessWidget {
|
||||
final PaymentCard card;
|
||||
final VoidCallback onDelete;
|
||||
final VoidCallback? onMakeMain;
|
||||
|
||||
const _CardItem({
|
||||
required this.card,
|
||||
required this.onDelete,
|
||||
this.onMakeMain,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
_getDefaultIconPath(card.type),
|
||||
width: 40,
|
||||
height: 40,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: onMakeMain, // Нажатие на текст карты делает её основной
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${card.type} ****${card.cardLastNumber}',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
card.isMain ? 'основная' : 'сделать основной',
|
||||
style: TextStyle(
|
||||
color: card.isMain ? const Color(0xFF66E3C4) : Colors.white.withOpacity(0.5),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: onDelete,
|
||||
child: const Icon(Icons.close, color: Color(0xFF00D4AA), size: 20),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _getDefaultIconPath(String cardType) {
|
||||
switch (cardType) {
|
||||
case 'Belcard': return 'assets/icons/belcard.png';
|
||||
case 'Visa': return 'assets/icons/visa.png';
|
||||
case 'Maestro': return 'assets/icons/maestro.png';
|
||||
case 'Mir': return 'assets/icons/mir.png';
|
||||
case 'Mastercard': return 'assets/icons/mastercard.png';
|
||||
default: return 'assets/icons/belcard.png';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user