This commit is contained in:
2026-07-13 01:38:13 +03:00
parent 9eda119527
commit 98c5a7a1d1
54 changed files with 860 additions and 454 deletions

View File

@@ -31,7 +31,8 @@ class _CardBindingResultScreenState extends State<CardBindingResultScreen> {
if (widget.status == CardBindingStatus.success) {
_autoPopTimer = Timer(const Duration(seconds: 5), () {
if (mounted) {
context.go('/home/payment-methods');
final timestamp = DateTime.now().millisecondsSinceEpoch;
context.go("/home/payment-methods?refreshId=$timestamp");
}
});
}
@@ -125,7 +126,10 @@ class _CardBindingResultScreenState extends State<CardBindingResultScreen> {
GradientButton(
text: "Попробовать снова",
onTap: () {},
onTap: () {
final timestamp = DateTime.now().millisecondsSinceEpoch;
context.go("/home/payment-methods?refreshId=$timestamp");
},
enabled: true,
showArrows: true,
),
@@ -135,7 +139,11 @@ class _CardBindingResultScreenState extends State<CardBindingResultScreen> {
width: double.infinity,
height: 56,
child: TextButton(
onPressed: () => context.go("/home/payment-methods"),
onPressed: ()
{
final timestamp = DateTime.now().millisecondsSinceEpoch;
context.go("/home/payment-methods?refreshId=$timestamp");
},
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),

View File

@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import '../../core/app_colors.dart';
import '../../di/service_locator.dart';
import '../../domain/entities/user_profile.dart';
import '../components/custom_app_bar.dart';
import '../components/gradient_button.dart';
@@ -106,7 +107,7 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
}
if (state.isSuccess) {
context.read<ProfileBloc>().add(ProfileUpdated());
getIt<ProfileBloc>().add(ProfileUpdated());
context.pop();
}
if (state.error != null) {

View File

@@ -4,24 +4,9 @@ import 'dart:ui' as ui;
import 'package:bot_toast/bot_toast.dart';
import 'package:be_happy/domain/entities/client_notification.dart';
import 'package:be_happy/domain/usecase/book_scooter_usecase.dart';
import 'package:be_happy/domain/usecase/get_client_orders_usecase.dart';
import 'package:be_happy/domain/usecase/get_map_settings_usecase.dart';
import 'package:be_happy/domain/usecase/get_payment_cards_usecase.dart';
import 'package:be_happy/domain/usecase/get_pedestrian_routes_usecase.dart';
import 'package:be_happy/domain/usecase/get_scooter_usecase.dart';
import 'package:be_happy/domain/usecase/save_map_settings_usecase.dart';
import 'package:be_happy/presentation/components/fine_notification_card.dart';
import 'package:be_happy/presentation/components/map_icon_painter/clusterized_icon_painter.dart';
import 'package:be_happy/presentation/components/payment_notification_card.dart';
import 'package:be_happy/presentation/components/sheet/current_rides_sheet.dart';
import 'package:be_happy/presentation/components/sheet/map_settings_sheet.dart';
import 'package:be_happy/presentation/components/sheet/tariff_sheet.dart';
import 'package:be_happy/presentation/event/current_rides_event.dart';
import 'package:be_happy/presentation/event/map_settings_modal_event.dart';
import 'package:be_happy/presentation/viewmodel/current_rides_bloc.dart';
import 'package:be_happy/presentation/viewmodel/map_settings_modal_bloc.dart';
import 'package:be_happy/presentation/viewmodel/tariff_sheet_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:geolocator/geolocator.dart';
@@ -32,18 +17,12 @@ import '../../core/app_colors.dart';
import '../../di/service_locator.dart';
import '../../domain/entities/scooter.dart';
import '../../domain/entities/zone.dart';
import '../../domain/usecase/get_address_by_point_usecase.dart';
import '../../domain/usecase/get_available_tariffs_usecase.dart';
import '../../domain/usecase/get_notifications_stream_usecase.dart';
import '../components/notification_toast.dart';
import '../components/sheet/scooter_bottom_sheet.dart';
import '../components/side_menu.dart';
import '../components/unpaid_order_notification_card.dart';
import '../event/map_event.dart';
import '../event/scooter_detail_modal_event.dart';
import '../state/map_state.dart';
import '../viewmodel/map_bloc.dart';
import '../viewmodel/scooter_detail_modal_bloc.dart';
class MapScreen extends StatefulWidget {
const MapScreen({super.key});
@@ -609,28 +588,33 @@ class _MapScreenState extends State<MapScreen> {
if (zones == null || zones.isEmpty) return [];
List<MapObject> objects = [];
List<LinearRing> allZoneHoles = [];
List<LinearRing> driveOuterRings = [];
for (var zone in zones) {
var points = zone.points.map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList();
if (zone.type == "Drive") {
for (var polygonData in zone.geometry) {
if (polygonData.isEmpty) continue;
var cleanPoints = <Point>[];
for (var p in points) {
if (cleanPoints.isEmpty || cleanPoints.last != p) {
cleanPoints.add(p);
var outerPoints = polygonData[0].map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList();
var cleanPoints = <Point>[];
for (var p in outerPoints) {
if (cleanPoints.isEmpty || cleanPoints.last != p) {
cleanPoints.add(p);
}
}
if (cleanPoints.length > 2) {
if (cleanPoints.first != cleanPoints.last) {
cleanPoints.add(cleanPoints.first);
}
driveOuterRings.add(LinearRing(points: cleanPoints));
}
}
}
if (cleanPoints.length > 2) {
if (cleanPoints.first != cleanPoints.last) {
cleanPoints.add(cleanPoints.first);
}
allZoneHoles.add(LinearRing(points: cleanPoints));
}
}
// Добавляем глобальную маску (все, что НЕ Drive — затеняется красным)
objects.add(
PolygonMapObject(
mapId: const MapObjectId('global_inverse_mask'),
@@ -641,7 +625,7 @@ class _MapScreenState extends State<MapScreen> {
Point(latitude: -85, longitude: 179.9),
Point(latitude: -85, longitude: -179.9),
]),
innerRings: allZoneHoles,
innerRings: driveOuterRings, // Вырезаем только зоны Drive
),
strokeWidth: 0,
fillColor: Colors.red.withOpacity(0.15),
@@ -649,27 +633,63 @@ class _MapScreenState extends State<MapScreen> {
),
);
// 2. Отрисовываем каждую зону (включая MultiPolygon и вложенные NotDrive/Finish)
for (var zone in zones) {
Color borderColor;
Color fillColor;
// Стилизация в зависимости от типа зоны
if (zone.type == "Drive") {
borderColor = const Color(0xFF5ECD4C);
borderColor = const Color(0xFF5ECD4C); // Зеленый бордюр
fillColor = Colors.transparent; // Прозрачная внутри маски
} else if (zone.type == "NotDrive") {
borderColor = const Color(0xFFEF4444);
borderColor = const Color(0xFFEF4444); // Красный бордюр
fillColor = const Color(0xFFEF4444).withOpacity(0.3); // Красная полупрозрачная заливка для наглядности внутри Drive
} else {
borderColor = const Color(0xFFA78BFA);
borderColor = const Color(0xFFA78BFA); // Фиолетовый бордюр (Парковка)
fillColor = const Color(0xFFA78BFA).withOpacity(0.25); // Фиолетовая заливка
}
objects.add(
PolylineMapObject(
mapId: MapObjectId('zone_contour_${zone.id}'),
polyline: Polyline(
points: zone.points.map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList(),
int polyIndex = 0;
for (var polygonData in zone.geometry) {
if (polygonData.isEmpty) continue;
// Строим внешнее кольцо текущего полигона
var outerPoints = polygonData[0].map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList();
if (outerPoints.first != outerPoints.last) {
outerPoints.add(outerPoints.first);
}
LinearRing outerRing = LinearRing(points: outerPoints);
// Строим внутренние кольца (дыры), если они есть у этого полигона
List<LinearRing> innerRings = [];
for (int i = 1; i < polygonData.length; i++) {
var innerPoints = polygonData[i].map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList();
if (innerPoints.isNotEmpty) {
if (innerPoints.first != innerPoints.last) {
innerPoints.add(innerPoints.first);
}
innerRings.add(LinearRing(points: innerPoints));
}
}
// Рендерим полноценный PolygonMapObject (он поддерживает и заливку, и обводку)
objects.add(
PolygonMapObject(
mapId: MapObjectId('zone_poly_${zone.type}_${zone.id}_$polyIndex'),
polygon: Polygon(
outerRing: outerRing,
innerRings: innerRings,
),
strokeColor: borderColor,
strokeWidth: 2.0,
fillColor: fillColor,
// Поднимаем зоны ограничений и парковок выше маски и контуров Drive
zIndex: zone.type == "Drive" ? 1 : 2,
),
strokeColor: borderColor,
strokeWidth: 2.0,
zIndex: 1,
),
);
);
polyIndex++;
}
}
return objects;
@@ -684,7 +704,10 @@ class _MapScreenState extends State<MapScreen> {
child: Builder(
builder: (innerContext) => _RoundIconButton(
icon: Icons.menu,
onPressed: () => {Scaffold.of(innerContext).openDrawer()},
onPressed: () {
context.read<MapBloc>().add(FetchProfileData());
Scaffold.of(innerContext).openDrawer();
},
),
),
),

View File

@@ -23,7 +23,10 @@ class NotificationsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _NotificationsScreenContent();
return BlocProvider(
create: (context) => getIt<NotificationsBloc>()..add(NotificationsFetchRequested(page: 1)),
child: _NotificationsScreenContent(),
);
}
}
@@ -36,21 +39,31 @@ class _NotificationsScreenContent extends StatefulWidget {
class _NotificationsScreenContentState extends State<_NotificationsScreenContent> {
NotificationFilter _filter = NotificationFilter.all;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _setFilter(NotificationFilter filter) {
setState(() {
_filter = filter;
});
setState(() => _filter = filter);
_scrollController.jumpTo(0);
context.read<NotificationsBloc>().add(NotificationsFetchRequested(page: 1));
}
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<NotificationsBloc>()..add(NotificationsFetchRequested()),
child: NotificationsView(
filter: _filter,
onFilterChanged: _setFilter,
),
return NotificationsView(
filter: _filter,
onFilterChanged: _setFilter,
scrollController: _scrollController,
);
}
}
@@ -58,11 +71,13 @@ class _NotificationsScreenContentState extends State<_NotificationsScreenContent
class NotificationsView extends StatelessWidget {
final NotificationFilter filter;
final ValueChanged<NotificationFilter> onFilterChanged;
final ScrollController? scrollController;
const NotificationsView({
super.key,
this.filter = NotificationFilter.all,
required this.onFilterChanged,
this.scrollController,
});
@override
@@ -119,7 +134,7 @@ class NotificationsView extends StatelessWidget {
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
context.read<NotificationsBloc>().add(NotificationsFetchRequested());
context.read<NotificationsBloc>().add(NotificationsFetchRequested(page: 1));
},
child: const Text('Повторить'),
),
@@ -138,9 +153,6 @@ class NotificationsView extends StatelessWidget {
return n.category == NotificationCategory.payment;
case NotificationFilter.order:
return n.category == NotificationCategory.scooter;
// || n.category == NotificationCategory.adminInfo
// || n.category == NotificationCategory.companyInfo;
default:
return true;
}
@@ -152,14 +164,39 @@ class NotificationsView extends StatelessWidget {
return RefreshIndicator(
onRefresh: () async {
context.read<NotificationsBloc>().add(NotificationsFetchRequested());
context.read<NotificationsBloc>().add(NotificationsFetchRequested(page: 1));
},
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 20),
itemCount: filtered.length,
itemBuilder: (context, index) {
return _NotificationCard(notification: filtered[index]);
child: NotificationListener<ScrollNotification>(
onNotification: (scrollInfo) {
if (scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent - 50 &&
scrollInfo.metrics.maxScrollExtent > 0 &&
state.hasMore &&
!state.isLoadingMore &&
state.status == NotificationsStatus.success) {
context.read<NotificationsBloc>().add(NotificationsLoadMoreRequested());
}
return false;
},
child: ListView.builder(
controller: scrollController,
padding: const EdgeInsets.symmetric(horizontal: 20),
itemCount: filtered.length + (state.hasMore && filtered.length >= 10 ? 1 : 0),
itemBuilder: (context, index) {
if (index == filtered.length && state.hasMore && filtered.length >= 10) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Center(
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2),
),
),
);
}
return _NotificationCard(notification: filtered[index]);
},
),
),
);
},
@@ -308,16 +345,23 @@ class _NotificationCard extends StatelessWidget {
}
String _formatDate(DateTime date) {
final localDate = date.toLocal();
final now = DateTime.now();
final difference = now.difference(date);
if (difference.inDays == 0) {
return 'Сегодня, ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
} else if (difference.inDays == 1) {
return 'Вчера, ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
} else {
return '${date.day.toString().padLeft(2, '0')}.${date.month.toString().padLeft(2, '0')}.${date.year}';
if (localDate.year == now.year &&
localDate.month == now.month &&
localDate.day == now.day) {
return 'Сегодня, ${localDate.hour.toString().padLeft(2, '0')}:${localDate.minute.toString().padLeft(2, '0')}';
}
final yesterday = now.subtract(const Duration(days: 1));
if (localDate.year == yesterday.year &&
localDate.month == yesterday.month &&
localDate.day == yesterday.day) {
return 'Вчера, ${localDate.hour.toString().padLeft(2, '0')}:${localDate.minute.toString().padLeft(2, '0')}';
}
return '${localDate.day.toString().padLeft(2, '0')}.${localDate.month.toString().padLeft(2, '0')}.${localDate.year}';
}
String _getTypeLabel(NotificationType type) {

View File

@@ -5,6 +5,7 @@ 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/custom_app_bar.dart';
import '../components/gradient_button.dart';
@@ -134,11 +135,6 @@ class _PaymentConfirmScreenContent extends StatelessWidget {
const SizedBox(height: 24),
_buildInfoLabel('Начислено баллов:'),
_buildValueRow('points_icon.png', '2 балла'),
const SizedBox(height: 24),
Row(
children: [
Expanded(
@@ -149,7 +145,7 @@ class _PaymentConfirmScreenContent extends StatelessWidget {
_buildInfoLabel('Стоимость поездки:'),
_buildValueRow(
'money_icon.png',
'17,17 BYN',
order?.totalOrderPricePrint ?? '- BYN',
),
],
),
@@ -162,7 +158,7 @@ class _PaymentConfirmScreenContent extends StatelessWidget {
_buildInfoLabel('Оплачено:'),
_buildValueRow(
'money_icon.png',
'10,00 BYN',
order?.payTotalPricePrint ?? '0,00 BYN',
),
],
),
@@ -193,9 +189,9 @@ class _PaymentConfirmScreenContent extends StatelessWidget {
width: 40,
),
const SizedBox(width: 12),
const Text(
'7,17 BYN',
style: TextStyle(
Text(
order?.unpayTotalPricePrint ?? '0,00 BYN',
style: const TextStyle(
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold,
@@ -227,6 +223,7 @@ class _PaymentConfirmScreenContent extends StatelessWidget {
create: (context) => PaymentMethodSheetBloc(
getIt<GetPaymentCardsUsecase>(),
getIt<SetMainPaymentCardUsecase>(),
getIt<GetProfileUseCase>(),
)..add(PaymentMethodSheetStarted()),
child: PaymentMethodSheet(
initialSelectedCard: state.useBalance ? null : state.selectedCard,

View File

@@ -80,7 +80,7 @@ class PaymentMethodsScreen extends StatelessWidget {
);
}
Widget _buildBalanceCard(BuildContext context, int balance) {
Widget _buildBalanceCard(BuildContext context, double balance) {
return Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(

View File

@@ -77,7 +77,7 @@ class _PhoneLoginScreenState extends State<PhoneLoginScreen> {
return BlocConsumer<VerifyCodeBloc, VerifyCodeState>(
listener: (context, state) {
if (state.isSuccess) {
context.go("/pin");
} else if (state.error != null) {
setState(() { _isFirstTry = false; });
codeController.clear();

View File

@@ -13,7 +13,8 @@ import '../viewmodel/scooter_detail_bloc.dart';
class ScooterDetailScreen extends StatelessWidget {
final String address;
const ScooterDetailScreen({super.key, required this.address});
final double distance;
const ScooterDetailScreen({super.key, required this.address, required this.distance});
@override
Widget build(BuildContext context) {
@@ -84,7 +85,7 @@ class ScooterDetailScreen extends StatelessWidget {
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: ScooterInfoSection(distance: scooter?.distance ?? 0, location: address)),
Expanded(child: ScooterInfoSection(distance: distance, location: address, batteryLevel: scooter?.batteryLevel ?? 0,)),
const SizedBox(width: 20),
],
),

View File

@@ -6,6 +6,7 @@ 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';
@@ -170,6 +171,7 @@ class SubscriptionDetailsScreen extends StatelessWidget {
create: (context) => PaymentMethodSheetBloc(
getIt<GetPaymentCardsUsecase>(),
getIt<SetMainPaymentCardUsecase>(),
getIt<GetProfileUseCase>(),
)..add(PaymentMethodSheetStarted()),
child: const PaymentMethodSheet(showBalance: false),
),

View File

@@ -1,84 +1,108 @@
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 'package:path/path.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/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),
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.isLoading) {
return const Center(child: CircularProgressIndicator());
if (state is TopUpLoading) {
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),
],
),
],
),
);
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(TopUpState state, BuildContext context) {
Widget _buildTariffList(TopUpLoaded state, BuildContext context) {
return SizedBox(
height: 120,
child: ListView.separated(
@@ -89,14 +113,11 @@ class TopUpScreen extends StatelessWidget {
final tariff = state.certificates[index];
final isSelected = state.selectedTariff == tariff;
return GestureDetector(
onTap: () =>
context.read<TopUpBloc>().add(SelectCertificate(tariff)),
onTap: () => context.read<TopUpBloc>().add(SelectCertificate(tariff)),
child: Container(
width: 140,
decoration: BoxDecoration(
color: isSelected
? const Color(0xFF80FFC1)
: Colors.white.withOpacity(0.1),
color: isSelected ? const Color(0xFF80FFC1) : Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white24),
),
@@ -127,7 +148,7 @@ class TopUpScreen extends StatelessWidget {
);
}
Widget _buildPriceInfo(TopUpState state) {
Widget _buildPriceInfo(TopUpLoaded state) {
if (state.selectedTariff == null) return const SizedBox.shrink();
return Center(
child: Column(
@@ -157,7 +178,7 @@ class TopUpScreen extends StatelessWidget {
);
}
Widget _buildCardSelector(TopUpState state, BuildContext context) {
Widget _buildCardSelector(TopUpLoaded state, BuildContext context) {
return state.selectedCard != null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
@@ -170,16 +191,14 @@ class TopUpScreen extends StatelessWidget {
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (innerContext) =>
BlocProvider(
create: (context) =>
PaymentMethodSheetBloc(
getIt<GetPaymentCardsUsecase>(),
getIt<SetMainPaymentCardUsecase>(),
)
..add(PaymentMethodSheetStarted()),
child: const PaymentMethodSheet(showBalance: false,),
),
builder: (innerContext) => BlocProvider(
create: (context) => PaymentMethodSheetBloc(
getIt<GetPaymentCardsUsecase>(),
getIt<SetMainPaymentCardUsecase>(),
getIt<GetProfileUseCase>(),
)..add(PaymentMethodSheetStarted()),
child: const PaymentMethodSheet(showBalance: false),
),
);
if (selectedCard != null) {
@@ -210,7 +229,6 @@ class TopUpScreen extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Способ оплаты',
style: TextStyle(
@@ -220,21 +238,9 @@ class TopUpScreen extends StatelessWidget {
),
),
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),
),
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)),
],
),
),
@@ -244,20 +250,19 @@ class TopUpScreen extends StatelessWidget {
);
}
Widget _buildAgreement(TopUpState state, BuildContext context) {
Widget _buildAgreement(TopUpLoaded state, BuildContext context) {
return Row(
children: [
AppCheckbox(
value: state.isAgreed,
onChanged: (v) =>
context.read<TopUpBloc>().add(ToggleAgreement(v ?? false)),
onChanged: (v) => context.read<TopUpBloc>().add(ToggleAgreement(v ?? false)),
),
const SizedBox(width: 12),
Flexible(
const Flexible(
child: Text.rich(
TextSpan(
text: 'Я принимаю условия покупки бонусного пакета, при котором денежные средства не подлежат возврату... ',
style: const TextStyle(
text: 'Я принимаю условия покупки бонусного пакека, при котором денежные средства не подлежат возврату... ',
style: TextStyle(
color: AppColors.white70,
fontSize: 12,
),
@@ -268,7 +273,7 @@ class TopUpScreen extends StatelessWidget {
);
}
Widget _buildPayButton(TopUpState state, VoidCallback onTap) {
Widget _buildPayButton(TopUpLoaded state, VoidCallback onTap) {
return GradientButton(
text: 'Оплатить',
onTap: state.isAgreed ? onTap : null,
@@ -279,5 +284,4 @@ class TopUpScreen extends StatelessWidget {
fontSize: 16,
);
}
}