merge upstream

This commit is contained in:
mso7nueh
2025-06-18 06:56:09 +00:00
38 changed files with 1272 additions and 627 deletions

View File

@@ -40,6 +40,17 @@ post_install do |installer|
target.build_configurations.each do |config| target.build_configurations.each do |config|
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0' config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',
## dart: PermissionGroup.photos
'PERMISSION_PHOTOS=1',
'PERMISSION_NOTIFICATIONS=1',
## dart: PermissionGroup.mediaLibrary
'PERMISSION_MEDIA_LIBRARY=1',
]
end end
end end
end end

View File

@@ -20,6 +20,8 @@ import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.da
import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart';
import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart';
import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart'; import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart';
import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart'; import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart'; import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart'; import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
@@ -210,10 +212,20 @@ class AppRouter {
}, },
), ),
GoRoute( GoRoute(
path: '/consultant-finance-screen', path: ConsultationsArchiveScreen.path,
name: 'consultantFinanceScreen', name: ConsultationsArchiveScreen.name,
builder: (context, state) => const ConsultationsArchiveScreen(), builder: (context, state) => const ConsultationsArchiveScreen(),
), ),
GoRoute(
path: ConsultantFinancesScreen.path,
name: ConsultantFinancesScreen.name,
builder: (context, state) => const ConsultantFinancesScreen(),
),
GoRoute(
path: UserPurchasesScreen.path,
name: UserPurchasesScreen.name,
builder: (context, state) => const UserPurchasesScreen(),
),
GoRoute( GoRoute(
path: '/user-finance-screen', path: '/user-finance-screen',
name: 'userFinanceScreen', name: 'userFinanceScreen',

View File

@@ -31,6 +31,8 @@ import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart'; import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart'; import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart'; import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart';
@@ -123,8 +125,14 @@ Future<void> init() async {
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl())); sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl())); sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl()));
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl())); sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
sl.registerFactory<DiaryCubit>( sl.registerFactory<DiaryCubit>(() => DiaryCubit(
() => DiaryCubit(chatRepository: sl(), diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl(), dietRepository: sl())); chatRepository: sl(),
diaryRepository: sl(),
glossaryRepository: sl(),
profileRepository: sl(),
dietRepository: sl(),
settingsRepository: sl(),
));
sl.registerFactory<AboutDietCubit>(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl())); sl.registerFactory<AboutDietCubit>(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
sl.registerFactory<BlogPostCreateCubit>(() => BlogPostCreateCubit(blogPostRepository: sl())); sl.registerFactory<BlogPostCreateCubit>(() => BlogPostCreateCubit(blogPostRepository: sl()));
sl.registerFactory<SubCubit>(() => SubCubit(profileRepository: sl())); sl.registerFactory<SubCubit>(() => SubCubit(profileRepository: sl()));
@@ -148,6 +156,8 @@ Future<void> init() async {
sl.registerFactory<VerifyEmailCubit>(() => VerifyEmailCubit(profileRepository: sl())); sl.registerFactory<VerifyEmailCubit>(() => VerifyEmailCubit(profileRepository: sl()));
sl.registerFactory<MyRecipesCubit>(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl())); sl.registerFactory<MyRecipesCubit>(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl()));
sl.registerFactory<ConsultationsArchiveCubit>(() => ConsultationsArchiveCubit(consultationsRepository: sl())); sl.registerFactory<ConsultationsArchiveCubit>(() => ConsultationsArchiveCubit(consultationsRepository: sl()));
sl.registerFactory<ConsultantFinancesCubit>(() => ConsultantFinancesCubit(consultationsRepository: sl()));
sl.registerFactory<UserPurchasesCubit>(() => UserPurchasesCubit(consultationsRepository: sl()));
// Repository // Repository
sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl())); sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl()));

View File

@@ -0,0 +1,23 @@
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:permission_handler/permission_handler.dart';
class PermissionHandler {
static Future<void> checkPermission({
required Permission permission,
required String title,
required String description,
required Future<void> Function() onGranted,
required BuildContext context,
}) async {
final permissionState = await permission.request();
if (permissionState.isGranted) {
await onGranted();
return;
}
if (context.mounted) {
showErrorDialog(context, title: title, description: description, buttonTitle: S.of(context).allowAccess, needToOpenSettings: true);
}
}
}

View File

@@ -4,8 +4,9 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:permission_handler/permission_handler.dart';
Future<void> showErrorDialog(BuildContext context, {String? title, String? description}) async { Future<void> showErrorDialog(BuildContext context, {String? title, String? description, String? buttonTitle, bool? needToOpenSettings}) async {
return await showDialog<void>( return await showDialog<void>(
context: context, context: context,
barrierColor: Colors.transparent, barrierColor: Colors.transparent,
@@ -43,8 +44,13 @@ Future<void> showErrorDialog(BuildContext context, {String? title, String? descr
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: TextButton( child: TextButton(
onPressed: () => AppRouter.navigatorKey.currentContext?.pop(), onPressed: () {
child: Text(s.ok, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), AppRouter.navigatorKey.currentContext?.pop();
if (needToOpenSettings ?? false) {
openAppSettings();
}
},
child: Text(buttonTitle ?? s.ok, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),

View File

@@ -1,6 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/app_decoration.dart'; import 'package:eatvio_app/core/theme/app_decoration.dart';
import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart'; import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
@@ -17,6 +19,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class BlogPostCreateScreen extends StatefulWidget { class BlogPostCreateScreen extends StatefulWidget {
final FeedBlogPostModel? feedBlogPostModel; final FeedBlogPostModel? feedBlogPostModel;
@@ -80,47 +83,65 @@ class _BlogPostCreateScreenState extends State<BlogPostCreateScreen> {
_showImagePicker() { _showImagePicker() {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
final picker = ImagePicker(); CupertinoActionSheetAction(
image = await picker.pickImage(source: ImageSource.camera); onPressed: () async {
setState(() {}); PermissionHandler.checkPermission(
if (!context.mounted) return; permission: Permission.camera,
Navigator.of(context).pop(); title: s.allowAccessToCamera,
}, description: s.allowAccessToCameraDescription,
child: const Row( onGranted: () async {
children: [ final picker = ImagePicker();
Icon(Icons.photo_camera), image = await picker.pickImage(source: ImageSource.camera);
SizedBox(width: 8), setState(() {});
Text('Фото'), AppRouter.navigatorKey.currentContext?.pop();
], },
context: context,
);
},
child: const Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text('Фото'),
],
),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
final picker = ImagePicker(); permission: Permission.photos,
image = await picker.pickImage(source: ImageSource.gallery); title: s.allowAcessToGalery,
setState(() {}); description: s.allowAcessToGaleryDescription,
if (!context.mounted) return; onGranted: () async {
Navigator.of(context).pop(); final picker = ImagePicker();
}, image = await picker.pickImage(source: ImageSource.gallery);
child: const Row( setState(() {});
children: [ if (!context.mounted) return;
Icon(Icons.photo), AppRouter.navigatorKey.currentContext?.pop();
SizedBox(width: 8), },
Text('Галерея'), context: context,
], );
},
child: const Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text('Галерея'),
],
),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }

View File

@@ -6,6 +6,7 @@ import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart'; import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.dart'; import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.dart';
import 'package:eatvio_app/features/consultations/presentation/widgets/consultation_card_widget.dart'; import 'package:eatvio_app/features/consultations/presentation/widgets/consultation_card_widget.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@@ -67,7 +68,7 @@ class ConsultationsContent extends StatelessWidget {
InkWell( InkWell(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
onTap: () { onTap: () {
GoRouter.of(context).pushNamed('consultantFinanceScreen'); GoRouter.of(context).pushNamed(ConsultationsArchiveScreen.name);
}, },
child: Ink( child: Ink(
width: MediaQuery.of(context).size.width - 32, width: MediaQuery.of(context).size.width - 32,

View File

@@ -185,6 +185,8 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
children: List.generate(widget.consultationsModel.duration, (index) { children: List.generate(widget.consultationsModel.duration, (index) {
final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index); final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index);
final hasActivity = day != null && day.$2.state != DiaryStateEnum.init; final hasActivity = day != null && day.$2.state != DiaryStateEnum.init;
final canConsultantOpenThisDay = day != null &&
(day.$2.state != DiaryStateEnum.init && day.$2.state != DiaryStateEnum.started && day.$2.state != DiaryStateEnum.lastDayStarted);
return Padding( return Padding(
padding: const EdgeInsets.only(right: 12), padding: const EdgeInsets.only(right: 12),
child: Column( child: Column(
@@ -192,7 +194,7 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
children: [ children: [
InkWell( InkWell(
onTap: () { onTap: () {
if (!hasActivity) { if (!hasActivity || !canConsultantOpenThisDay) {
showErrorDialog(context, showErrorDialog(context,
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}'); title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
return; return;

View File

@@ -1,7 +1,9 @@
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart'; import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/consultations/services/consultation_service.dart'; import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
class ConsultationsRepository { class ConsultationsRepository {
final ConsultationService consultationService; final ConsultationService consultationService;
@@ -42,4 +44,20 @@ class ConsultationsRepository {
rethrow; rethrow;
} }
} }
Future<List<ConsultantFinanceModel>> getConsultantFinances(String? date, FiltersComparisonEnum? dateComparison) async {
try {
return await consultationService.getConsultantFinances(date, dateComparison);
} catch (e) {
rethrow;
}
}
Future<List<UserPurchasesModel>> getUserPurchases(String? date, FiltersComparisonEnum? dateComparison) async {
try {
return await consultationService.getUserPurchases(date, dateComparison);
} catch (e) {
rethrow;
}
}
} }

View File

@@ -1,7 +1,9 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart'; import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
class ConsultationService { class ConsultationService {
final Dio dio; final Dio dio;
@@ -66,4 +68,42 @@ class ConsultationService {
rethrow; rethrow;
} }
} }
Future<List<ConsultantFinanceModel>> getConsultantFinances(
String? date,
FiltersComparisonEnum? dateComparison,
) async {
try {
final response = await dio.get(
'/api/finances/my_finances',
queryParameters: {
if (dateComparison != null && date != null) 'filters[end_date]': date,
},
);
final data = response.data as Map<String, dynamic>;
final consultations = data['data'] as List<dynamic>;
return consultations.map((consultation) => ConsultantFinanceModel.fromJson(consultation as Map<String, dynamic>)).toList();
} catch (e) {
rethrow;
}
}
Future<List<UserPurchasesModel>> getUserPurchases(
String? date,
FiltersComparisonEnum? dateComparison,
) async {
try {
final response = await dio.get(
'/api/finances/my_purchases',
queryParameters: {
if (dateComparison != null && date != null) 'filters[start_date]': date,
},
);
final data = response.data as Map<String, dynamic>;
final purchases = data['data'] as List<dynamic>;
return purchases.map((purchase) => UserPurchasesModel.fromJson(purchase as Map<String, dynamic>)).toList();
} catch (e) {
rethrow;
}
}
} }

View File

@@ -1,6 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:dotted_border/dotted_border.dart'; import 'package:dotted_border/dotted_border.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart'; import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
@@ -11,7 +13,9 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class DietImagePickerWidget extends StatefulWidget { class DietImagePickerWidget extends StatefulWidget {
const DietImagePickerWidget({super.key}); const DietImagePickerWidget({super.key});
@@ -28,11 +32,18 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
actions: [ actions: [
CupertinoActionSheetAction( CupertinoActionSheetAction(
onPressed: () async { onPressed: () async {
final picker = ImagePicker(); PermissionHandler.checkPermission(
final image = await picker.pickImage(source: ImageSource.camera); permission: Permission.camera,
cubit.setImage(image); title: s.allowAccessToCamera,
if (!context.mounted) return; description: s.allowAccessToCameraDescription,
Navigator.of(context).pop(); onGranted: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.camera);
cubit.setImage(image);
AppRouter.navigatorKey.currentContext?.pop();
},
context: context,
);
}, },
child: Row( child: Row(
children: [ children: [
@@ -44,11 +55,18 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
), ),
CupertinoActionSheetAction( CupertinoActionSheetAction(
onPressed: () async { onPressed: () async {
final picker = ImagePicker(); PermissionHandler.checkPermission(
final image = await picker.pickImage(source: ImageSource.gallery); permission: Permission.photos,
cubit.setImage(image); title: s.allowAcessToGalery,
if (!context.mounted) return; description: s.allowAcessToGaleryDescription,
Navigator.of(context).pop(); onGranted: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.gallery);
cubit.setImage(image);
AppRouter.navigatorKey.currentContext?.pop();
},
context: context,
);
}, },
child: Row( child: Row(
children: [ children: [

View File

@@ -1,6 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:dotted_border/dotted_border.dart'; import 'package:dotted_border/dotted_border.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
@@ -11,7 +13,9 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class PlanImagePickerWidget extends StatefulWidget { class PlanImagePickerWidget extends StatefulWidget {
const PlanImagePickerWidget({super.key}); const PlanImagePickerWidget({super.key});
@@ -28,11 +32,18 @@ class _PlanImagePickerWidgetState extends State<PlanImagePickerWidget> {
actions: [ actions: [
CupertinoActionSheetAction( CupertinoActionSheetAction(
onPressed: () async { onPressed: () async {
final picker = ImagePicker(); PermissionHandler.checkPermission(
final image = await picker.pickImage(source: ImageSource.camera); permission: Permission.camera,
cubit.setImage(image); title: s.allowAccessToCamera,
if (!context.mounted) return; description: s.allowAccessToCameraDescription,
Navigator.of(context).pop(); onGranted: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.camera);
cubit.setImage(image);
AppRouter.navigatorKey.currentContext?.pop();
},
context: context,
);
}, },
child: Row( child: Row(
children: [ children: [
@@ -44,11 +55,18 @@ class _PlanImagePickerWidgetState extends State<PlanImagePickerWidget> {
), ),
CupertinoActionSheetAction( CupertinoActionSheetAction(
onPressed: () async { onPressed: () async {
final picker = ImagePicker(); PermissionHandler.checkPermission(
final image = await picker.pickImage(source: ImageSource.gallery); permission: Permission.photos,
cubit.setImage(image); title: s.allowAcessToGalery,
if (!context.mounted) return; description: s.allowAcessToGaleryDescription,
Navigator.of(context).pop(); onGranted: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.gallery);
cubit.setImage(image);
AppRouter.navigatorKey.currentContext?.pop();
},
context: context,
);
}, },
child: Row( child: Row(
children: [ children: [

View File

@@ -1,11 +1,16 @@
import 'dart:io'; import 'dart:io';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class ImageWidget extends StatefulWidget { class ImageWidget extends StatefulWidget {
const ImageWidget({super.key, required this.image}); const ImageWidget({super.key, required this.image});
@@ -67,55 +72,72 @@ class _ImageWidgetState extends State<ImageWidget> {
_showImagePicker() { _showImagePicker() {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
final picker = ImagePicker(); CupertinoActionSheetAction(
final image = await picker.pickImage(source: ImageSource.camera); onPressed: () async {
setState(() {}); PermissionHandler.checkPermission(
if (image != null) { permission: Permission.camera,
createRecipeCubit.replaceImage(File(image.path)); title: s.allowAccessToCamera,
createRecipeCubit.validateImage(); description: s.allowAccessToCameraDescription,
} onGranted: () async {
if (!context.mounted) return; final picker = ImagePicker();
Navigator.of(context).pop(); final image = await picker.pickImage(source: ImageSource.camera);
}, setState(() {});
child: const Row( if (image != null) {
children: [ createRecipeCubit.replaceImage(File(image.path));
Icon(Icons.photo_camera), createRecipeCubit.validateImage();
SizedBox(width: 8), }
Text('Фото'), AppRouter.navigatorKey.currentContext?.pop();
], },
context: context,
);
},
child: const Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text('Фото'),
],
),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
final picker = ImagePicker(); permission: Permission.photos,
final image = await picker.pickImage(source: ImageSource.gallery); title: s.allowAcessToGalery,
setState(() {}); description: s.allowAcessToGaleryDescription,
if (image != null) { onGranted: () async {
createRecipeCubit.replaceImage(File(image.path)); final picker = ImagePicker();
createRecipeCubit.validateImage(); final image = await picker.pickImage(source: ImageSource.gallery);
} setState(() {});
if (!context.mounted) return; if (image != null) {
Navigator.of(context).pop(); createRecipeCubit.replaceImage(File(image.path));
}, createRecipeCubit.validateImage();
child: const Row( }
children: [ AppRouter.navigatorKey.currentContext?.pop();
Icon(Icons.photo), },
SizedBox(width: 8), context: context,
Text('Галерея'), );
], },
child: const Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text('Галерея'),
],
),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }
} }

View File

@@ -1,10 +1,15 @@
import 'dart:io'; import 'dart:io';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class StepImageWidget extends StatefulWidget { class StepImageWidget extends StatefulWidget {
final CreateRecipeCubit createRecipeCubit; final CreateRecipeCubit createRecipeCubit;
@@ -12,7 +17,8 @@ class StepImageWidget extends StatefulWidget {
const StepImageWidget({ const StepImageWidget({
super.key, super.key,
required this.index, required this.createRecipeCubit, required this.index,
required this.createRecipeCubit,
}); });
@override @override
@@ -79,53 +85,70 @@ class _StepImageWidgetState extends State<StepImageWidget> {
_showImagePicker() { _showImagePicker() {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
final picker = ImagePicker(); CupertinoActionSheetAction(
image = await picker.pickImage(source: ImageSource.camera); onPressed: () async {
setState(() {}); PermissionHandler.checkPermission(
if (image != null) { permission: Permission.camera,
widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index); title: s.allowAccessToCamera,
} description: s.allowAccessToCameraDescription,
if (!context.mounted) return; onGranted: () async {
Navigator.of(context).pop(); final picker = ImagePicker();
}, image = await picker.pickImage(source: ImageSource.camera);
child: const Row( setState(() {});
children: [ if (image != null) {
Icon(Icons.photo_camera), widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
SizedBox(width: 8), }
Text('Фото'), AppRouter.navigatorKey.currentContext?.pop();
], },
context: context,
);
},
child: const Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text('Фото'),
],
),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
final picker = ImagePicker(); permission: Permission.photos,
image = await picker.pickImage(source: ImageSource.gallery); title: s.allowAcessToGalery,
setState(() {}); description: s.allowAcessToGaleryDescription,
if (image != null) { onGranted: () async {
widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index); final picker = ImagePicker();
} image = await picker.pickImage(source: ImageSource.gallery);
if (!context.mounted) return; setState(() {});
Navigator.of(context).pop(); if (image != null) {
}, widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
child: const Row( }
children: [ AppRouter.navigatorKey.currentContext?.pop();
Icon(Icons.photo), },
SizedBox(width: 8), context: context,
Text('Галерея'), );
], },
child: const Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text('Галерея'),
],
),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }
} }

View File

@@ -14,6 +14,7 @@ import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart'; import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:eatvio_app/features/settings/repositories/service_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class DiaryCubit extends Cubit<DiaryState> { class DiaryCubit extends Cubit<DiaryState> {
@@ -25,14 +26,16 @@ class DiaryCubit extends Cubit<DiaryState> {
final ProfileRepository profileRepository; final ProfileRepository profileRepository;
final DietRepository dietRepository; final DietRepository dietRepository;
final ChatRepository chatRepository; final ChatRepository chatRepository;
final SettingsRepository settingsRepository;
int? diaryId; int? diaryId;
DiaryCubit( DiaryCubit({
{required this.chatRepository, required this.chatRepository,
required this.diaryRepository, required this.diaryRepository,
required this.glossaryRepository, required this.glossaryRepository,
required this.profileRepository, required this.profileRepository,
required this.dietRepository}) required this.dietRepository,
: super(const DiaryState.loading()) { required this.settingsRepository,
}) : super(const DiaryState.loading()) {
today = DateTime.now(); today = DateTime.now();
selectedDay = today; selectedDay = today;
diaryRepository.forceFetchStream.listen((_) { diaryRepository.forceFetchStream.listen((_) {
@@ -160,11 +163,16 @@ class DiaryCubit extends Cubit<DiaryState> {
} }
try { try {
final DiaryDayModel diaryDay; final DiaryDayModel diaryDay;
bool isMoodEnabled = false;
bool isWakeUpAndRetireTimeEnabled = false;
if (diaryId != null) { if (diaryId != null) {
diaryDay = await diaryRepository.getDiaryDay(diaryId); diaryDay = await diaryRepository.getDiaryDay(diaryId);
} else { } else {
final stringDate = selectedDay.format('yyyy-MM-dd'); final stringDate = selectedDay.format('yyyy-MM-dd');
diaryDay = await diaryRepository.getDiaryByDate(stringDate); diaryDay = await diaryRepository.getDiaryByDate(stringDate);
final profileSettings = await settingsRepository.getProfileSettings();
isMoodEnabled = profileSettings.isMoodFillable;
isWakeUpAndRetireTimeEnabled = profileSettings.isBedtimeFillable;
} }
ConsultantReviewModel? consultantReview; ConsultantReviewModel? consultantReview;
int? planDuration; int? planDuration;
@@ -197,6 +205,8 @@ class DiaryCubit extends Cubit<DiaryState> {
chosenPlanCurrentDay: currentPlanDay, chosenPlanCurrentDay: currentPlanDay,
chosenPlanDuration: planDuration, chosenPlanDuration: planDuration,
chosenPlanName: planName, chosenPlanName: planName,
isMoodEnabled: isMoodEnabled,
isWakeUpAndRetireEnabled: isWakeUpAndRetireTimeEnabled
), ),
); );
} catch (e, st) { } catch (e, st) {

View File

@@ -27,6 +27,8 @@ abstract class DiaryState with _$DiaryState {
@Default(false) bool isConsultantView, @Default(false) bool isConsultantView,
@Default(false) bool isDiaryFinished, @Default(false) bool isDiaryFinished,
@Default(false) bool isDiaryCommented, @Default(false) bool isDiaryCommented,
@Default(false) bool isWakeUpAndRetireEnabled,
@Default(false) bool isMoodEnabled,
}) = DiaryStateLoaded; }) = DiaryStateLoaded;
const factory DiaryState.loadedTomorrowWithDiet({ const factory DiaryState.loadedTomorrowWithDiet({

View File

@@ -80,13 +80,17 @@ class DiaryScreenContent extends StatelessWidget {
return Scaffold( return Scaffold(
backgroundColor: Colors.grey.shade200, backgroundColor: Colors.grey.shade200,
appBar: AppBar( appBar: diaryId == null
title: Text( ? AppBar(
state is DiaryStateLoaded ? DateTime.parse(state.diary.date).toLocal().format('d MMMM') : '', toolbarHeight: 0,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), )
), : AppBar(
centerTitle: true, title: Text(
), state is DiaryStateLoaded ? DateTime.parse(state.diary.date).toLocal().format('d MMMM') : '',
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
centerTitle: true,
),
body: SingleChildScrollView( body: SingleChildScrollView(
padding: const EdgeInsets.only(bottom: 16, top: 8), padding: const EdgeInsets.only(bottom: 16, top: 8),
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
@@ -101,13 +105,13 @@ class DiaryScreenContent extends StatelessWidget {
], ],
if (state is DiaryStateLoaded) if (state is DiaryStateLoaded)
WakeupAndRetireWidget( WakeupAndRetireWidget(
showRetire: state.diary.typeOfDiary.hasChosenPlan, isEnabled: state.diary.typeOfDiary.hasChosenPlan || state.isConsultantView || state.isWakeUpAndRetireEnabled,
disable: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented, disable: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
if (state is DiaryStateLoaded) if (state is DiaryStateLoaded)
MealWidget( MealWidget(
showMood: state.diary.typeOfDiary.hasChosenPlan, showMood: state.diary.typeOfDiary.hasChosenPlan || state.isConsultantView || state.isMoodEnabled,
showAddButton: !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented, showAddButton: !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented,
), ),
if (state is DiaryStateLoadedTomorrowWithDiet) if (state is DiaryStateLoadedTomorrowWithDiet)

View File

@@ -30,7 +30,7 @@ class CaloriesChartWidget extends StatelessWidget {
context: context, context: context,
builder: (context) => const AlertDialog( builder: (context) => const AlertDialog(
content: Text( content: Text(
'График отображает сколько калорий вам нужно ежедневно для похуденияи поддержания веса, согласно формуле Харриса-Бенедикта.\nИзменить коэффициент физической нагрузки можно в настройках.', 'График отображает сколько калорий Вам нужно ежедневно для поддержания веса, согласно формуле Харриса-Бенедикта. Изменить коэффициент физической нагрузки можно в настройках',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xFF323F4B), color: Color(0xFF323F4B),

View File

@@ -59,7 +59,6 @@ class _MealWidgetState extends State<MealWidget> {
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
const TimeOfMealPickerWidget(), const TimeOfMealPickerWidget(),
const ChosenDietWidget(), const ChosenDietWidget(),
const ChosenPlanWidget(), const ChosenPlanWidget(),
const ProductsListWidget(), const ProductsListWidget(),

View File

@@ -1,16 +1,21 @@
import 'dart:io'; import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class PhotoGalleryWidget extends StatefulWidget { class PhotoGalleryWidget extends StatefulWidget {
const PhotoGalleryWidget({super.key}); const PhotoGalleryWidget({super.key});
@@ -32,55 +37,75 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
showImagePicker(DiaryDayModel diaryDay) { showImagePicker(DiaryDayModel diaryDay) {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
Navigator.of(context).pop(); CupertinoActionSheetAction(
final picker = ImagePicker(); onPressed: () async {
XFile? image = await picker.pickImage(source: ImageSource.camera); PermissionHandler.checkPermission(
if (image != null) { permission: Permission.camera,
diaryCubit.uploadPhotos( title: s.allowAccessToCamera,
files: [File(image.path)], description: s.allowAccessToCameraDescription,
diaryId: diaryDay.id, onGranted: () async {
AppRouter.navigatorKey.currentContext?.pop();
final picker = ImagePicker();
XFile? image = await picker.pickImage(source: ImageSource.camera);
if (image != null) {
diaryCubit.uploadPhotos(
files: [File(image.path)],
diaryId: diaryDay.id,
);
}
},
context: context,
); );
} },
}, child: const Row(
child: const Row( children: [
children: [ Icon(Icons.photo_camera),
Icon(Icons.photo_camera), SizedBox(width: 8),
SizedBox(width: 8), Text('Фото'),
Text('Фото'), ],
], ),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
Navigator.of(context).pop(); permission: Permission.photos,
final picker = ImagePicker(); title: s.allowAcessToGalery,
List<XFile>? images = await picker.pickMultiImage(); description: s.allowAcessToGaleryDescription,
if (images.isNotEmpty) { onGranted: () async {
diaryCubit.uploadPhotos( AppRouter.navigatorKey.currentContext?.pop();
files: images.map((f) => File(f.path)).toList(), final picker = ImagePicker();
diaryId: diaryDay.id, List<XFile>? images = await picker.pickMultiImage();
if (images.isNotEmpty) {
diaryCubit.uploadPhotos(
files: images.map((f) => File(f.path)).toList(),
diaryId: diaryDay.id,
);
}
},
context: context,
); );
} },
}, child: const Row(
child: const Row( children: [
children: [ Icon(Icons.photo),
Icon(Icons.photo), SizedBox(width: 8),
SizedBox(width: 8), Text('Галерея'),
Text('Галерея'), ],
], ),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }
@@ -92,7 +117,9 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
int mealIndex = state.selectedMeal - 1; int mealIndex = state.selectedMeal - 1;
if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) { if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) {
return GestureDetector( return GestureDetector(
onTap: () => showImagePicker(state.diary), onTap: () async {
showImagePicker(state.diary);
},
child: SvgPicture.asset( child: SvgPicture.asset(
width: MediaQuery.of(context).size.width - 32, width: MediaQuery.of(context).size.width - 32,
'assets/no_one_image_was_added.svg', 'assets/no_one_image_was_added.svg',

View File

@@ -8,8 +8,8 @@ import 'package:flutter_svg/flutter_svg.dart';
import '../../../../core/theme/theme_helper.dart'; import '../../../../core/theme/theme_helper.dart';
class WakeupAndRetireWidget extends StatefulWidget { class WakeupAndRetireWidget extends StatefulWidget {
const WakeupAndRetireWidget({super.key, required this.showRetire, this.disable = false}); const WakeupAndRetireWidget({super.key, required this.isEnabled, this.disable = false});
final bool showRetire; final bool isEnabled;
final bool disable; final bool disable;
@override @override
State<WakeupAndRetireWidget> createState() => _WakeupAndRetireWidgetState(); State<WakeupAndRetireWidget> createState() => _WakeupAndRetireWidgetState();
@@ -94,6 +94,9 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<DiaryCubit, DiaryState>( return BlocBuilder<DiaryCubit, DiaryState>(
builder: (context, state) { builder: (context, state) {
if (!widget.isEnabled) {
return const SizedBox.shrink();
}
if (state is DiaryStateLoaded) { if (state is DiaryStateLoaded) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.symmetric(horizontal: 4),
@@ -157,67 +160,65 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
), ),
), ),
), ),
if (widget.showRetire) ...[ const SizedBox(width: 8),
const SizedBox(width: 8), Expanded(
Expanded( child: Material(
child: Material( borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(12), color: Colors.white,
color: Colors.white, child: Padding(
child: Padding( padding: const EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric( horizontal: 8,
horizontal: 8, vertical: 12,
vertical: 12, ),
), child: Column(
child: Column( children: [
children: [ Row(
Row( mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, children: [
children: [ SvgPicture.asset('assets/icons/retire.svg'),
SvgPicture.asset('assets/icons/retire.svg'), const SizedBox(width: 8),
const SizedBox(width: 8), const Text(
const Text( 'Уход ко сну',
'Уход ко сну', style: TextStyle(
style: TextStyle( fontSize: 16,
fontSize: 16, fontWeight: FontWeight.w500,
height: 1,
),
),
],
),
const SizedBox(height: 8),
InkWell(
onTap: widget.disable ? null : () => showTimePicker(state.diary.id, false),
borderRadius: BorderRadius.circular(100),
child: Ink(
height: 32,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: const Color(0xFFF1F2F6),
),
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
state.diary.retire.toString().substring(
0,
state.diary.retire.toString().length - 3,
),
style: const TextStyle(
color: Color(0xFF6D6F71),
fontSize: 17,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
height: 1,
),
),
],
),
const SizedBox(height: 8),
InkWell(
onTap: widget.disable ? null : () => showTimePicker(state.diary.id, false),
borderRadius: BorderRadius.circular(100),
child: Ink(
height: 32,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: const Color(0xFFF1F2F6),
),
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
state.diary.retire.toString().substring(
0,
state.diary.retire.toString().length - 3,
),
style: const TextStyle(
color: Color(0xFF6D6F71),
fontSize: 17,
fontWeight: FontWeight.w500,
),
), ),
), ),
), ),
), ),
], ),
), ],
), ),
), ),
), ),
] ),
], ],
), ),
); );

View File

@@ -1,5 +1,6 @@
import 'dart:developer'; import 'dart:developer';
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart'; import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
@@ -13,19 +14,22 @@ class ConsultantFinancesCubit extends Cubit<ConsultantFinancesState> {
ConsultantFinancesState(), ConsultantFinancesState(),
); );
Future<void> getConsultations() async { Future<void> getConsultantFinances() async {
try { try {
emit(state.copyWith(isLoading: true)); emit(state.copyWith(isLoading: true));
final consultantFinances = await consultationsRepository.getConsultantFinances(
state.periodFilterStatus?.value,
state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual,
);
emit(state.copyWith(financesModels: consultantFinances, isLoading: false));
} catch (e) { } catch (e) {
emit(state.copyWith(isLoading: false)); emit(state.copyWith(financesModels: [], isLoading: false));
log(e.toString()); log(e.toString());
} }
} }
Future<void> setConsultationsPeriod(PeriodFilterStatus period) async { Future<void> setFinancesPeriod(PeriodFilterStatus period) async {
emit(state.copyWith(periodFilterStatus: period)); emit(state.copyWith(periodFilterStatus: period));
await getConsultations(); await getConsultantFinances();
} }
} }

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@@ -6,7 +7,7 @@ part 'consultant_finances_state.freezed.dart';
@freezed @freezed
abstract class ConsultantFinancesState with _$ConsultantFinancesState { abstract class ConsultantFinancesState with _$ConsultantFinancesState {
factory ConsultantFinancesState({ factory ConsultantFinancesState({
@Default([]) List<String> financesModels, @Default([]) List<ConsultantFinanceModel> financesModels,
@Default(false) isLoading, @Default(false) isLoading,
@Default(null) String? error, @Default(null) String? error,
@Default(null) PeriodFilterStatus? periodFilterStatus, @Default(null) PeriodFilterStatus? periodFilterStatus,

View File

@@ -0,0 +1,36 @@
import 'dart:developer';
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class UserPurchasesCubit extends Cubit<UserPurchasesState> {
final ConsultationsRepository consultationsRepository;
UserPurchasesCubit({
required this.consultationsRepository,
}) : super(
UserPurchasesState(),
);
Future<void> getUserPurchases() async {
try {
emit(state.copyWith(isLoading: true));
final purchasesModels = await consultationsRepository.getUserPurchases(
state.periodFilterStatus?.value,
state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual,
);
emit(state.copyWith(purchasesModels: purchasesModels, isLoading: false));
} catch (e) {
emit(state.copyWith(purchasesModels: [], isLoading: false));
log(e.toString());
}
}
Future<void> setPurchasesPeriod(PeriodFilterStatus period) async {
emit(state.copyWith(periodFilterStatus: period));
await getUserPurchases();
}
}

View File

@@ -0,0 +1,15 @@
import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_purchases_state.freezed.dart';
@freezed
abstract class UserPurchasesState with _$UserPurchasesState {
factory UserPurchasesState({
@Default([]) List<UserPurchasesModel> purchasesModels,
@Default(false) isLoading,
@Default(null) String? error,
@Default(null) PeriodFilterStatus? periodFilterStatus,
}) = _UserPurchasesState;
}

View File

@@ -0,0 +1,33 @@
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'consultant_finance_model.freezed.dart';
part 'consultant_finance_model.g.dart';
@freezed
abstract class ConsultantFinanceModel with _$ConsultantFinanceModel {
factory ConsultantFinanceModel({
@JsonKey(name: 'start_date', fromJson: parseDate) required DateTime startDate,
@JsonKey(name: 'bank_id') required String bankId,
@JsonKey(name: 'title') required String title,
@JsonKey(name: 'clientName') required String clientName,
@JsonKey(name: 'duration') required int duration,
@JsonKey(name: 'payment_price', fromJson: _parsePayment) required String paymentPrice,
required PaymentStatus? paymentStatus,
}) = _ConsultantFinanceModel;
factory ConsultantFinanceModel.fromJson(Map<String, dynamic> json) => _$ConsultantFinanceModelFromJson(json);
}
String _parsePayment(json) {
return ((json as int) / 100).toStringAsFixed(2);
}
enum PaymentStatus {
@JsonValue('COMPLETED')
completed,
@JsonValue('REJECTED')
rejected,
@JsonValue('CHECKED')
checked
}

View File

@@ -0,0 +1,21 @@
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_purchases_model.freezed.dart';
part 'user_purchases_model.g.dart';
@freezed
abstract class UserPurchasesModel with _$UserPurchasesModel {
factory UserPurchasesModel({
@JsonKey(name: 'start_date', fromJson: parseDate) required DateTime startDate,
@JsonKey(name: 'bank_id') required String bankId,
@JsonKey(name: 'title') required String title,
@JsonKey(name: 'consultantName') required String consultantName,
@JsonKey(name: 'duration') required int duration,
@JsonKey(name: 'price') required int paymentPrice,
@JsonKey(name: 'chosen_plan_last_state') required ChosenPlanStateEnum chosenPlanState,
}) = _UserPurchasesModel;
factory UserPurchasesModel.fromJson(Map<String, dynamic> json) => _$UserPurchasesModelFromJson(json);
}

View File

@@ -1,281 +1,225 @@
// import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/locator_service.dart';
// import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart';
// import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
// import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
// import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart';
// import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart'; import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
// import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/loader.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart'; import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart';
// import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
// import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
// class ConsultantFinancesScreen extends StatefulWidget { class ConsultantFinancesScreen extends StatefulWidget {
// const ConsultantFinancesScreen({super.key}); const ConsultantFinancesScreen({super.key});
static const name = 'consultantFinancesScreen';
static const path = '/consultant-finances-screen';
@override
State<ConsultantFinancesScreen> createState() => _ConsultantFinancesScreenState();
}
// @override class _ConsultantFinancesScreenState extends State<ConsultantFinancesScreen> {
// State<ConsultantFinancesScreen> createState() => _ConsultantFinancesScreenState(); @override
// } Widget build(BuildContext context) {
final s = S.of(context);
// class _ConsultantFinancesScreenState extends State<ConsultantFinancesScreen> { return BlocProvider<ConsultantFinancesCubit>(
// @override create: (context) => sl<ConsultantFinancesCubit>()..getConsultantFinances(),
// Widget build(BuildContext context) { child: BlocBuilder<ConsultantFinancesCubit, ConsultantFinancesState>(
// final s = S.of(context); builder: (context, state) {
// return BlocProvider<ConsultantFinancesCubit>( return Scaffold(
// create: (context) => sl<ConsultantFinancesCubit>(), backgroundColor: const Color(0xFFF0F2F5),
// child: BlocBuilder<ConsultantFinancesCubit, ConsultantFinancesState>( appBar: AppBar(
// builder: (context, state) { shape: const RoundedRectangleBorder(
// return Scaffold( borderRadius: BorderRadiusDirectional.vertical(
// backgroundColor: const Color(0xFFF0F2F5), bottom: Radius.circular(12),
// appBar: AppBar( ),
// shape: const RoundedRectangleBorder( ),
// borderRadius: BorderRadiusDirectional.vertical( title: Text(
// bottom: Radius.circular(12), 'Финансы',
// ), style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
// ), ),
// title: const Text('Архив консультаций'), centerTitle: true,
// centerTitle: true, ),
// ), body: state.isLoading
// body: state.isLoading ? const Center(
// ? const Center( child: Loader(),
// child: Loader(), )
// ) : SingleChildScrollView(
// : SingleChildScrollView( padding: const EdgeInsets.symmetric(vertical: 8),
// padding: const EdgeInsets.symmetric(vertical: 8), child: Column(
// child: Column( children: [
// children: [ InkWell(
// Material( onTap: () {
// color: Colors.white, GoRouter.of(context).pushNamed(CardSettingsScreen.name);
// borderRadius: BorderRadius.circular(12), },
// child: Column( borderRadius: BorderRadius.circular(20),
// crossAxisAlignment: CrossAxisAlignment.start, child: Ink(
// children: [ decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400),
// const SizedBox(height: 16), width: MediaQuery.of(context).size.width - 32,
// Padding( child: const Center(
// padding: const EdgeInsets.symmetric(horizontal: 16), child: Padding(
// child: Text( padding: EdgeInsets.all(16),
// 'Всего консультаций: ${state.statisticModel.total}', child: Text('Управление картами', style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600)),
// style: const TextStyle( ),
// fontSize: 16, ),
// fontWeight: FontWeight.w500, ),
// color: Color(0xFF6D6F71), ),
// height: 1, const SizedBox(height: 8),
// ), Material(
// ), color: Colors.white,
// ), borderRadius: BorderRadius.circular(12),
// const SizedBox(height: 12), child: Padding(
// Row( padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
// children: [ child: SingleChildScrollView(
// const SizedBox(width: 16), physics: const NeverScrollableScrollPhysics(),
// Material( child: Column(
// color: const Color(0xFF0BB450).withAlpha(11), children: [
// borderRadius: BorderRadius.circular(8), CustomDropDownButton<PeriodFilterStatus>(
// child: Padding( items: PeriodFilterStatus.values
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), .map(
// child: Text( (value) => DropdownMenuItem(
// 'Завершено: ${state.statisticModel.finished}', value: value,
// style: const TextStyle( child: Text(
// fontSize: 14, value.name,
// fontWeight: FontWeight.w400, style: TextStyles.textMMedium.copyWith(color: appTheme.black),
// color: Color(0xFF0BB450), ),
// height: 1, ),
// ), )
// ), .toList(),
// ), onChanged: (value) {
// ), if (value == null) return;
// const SizedBox(width: 8), context.read<ConsultantFinancesCubit>().setFinancesPeriod(value);
// Material( },
// color: const Color(0xFFD01717).withAlpha(11), value: state.periodFilterStatus ?? PeriodFilterStatus.values.first,
// borderRadius: BorderRadius.circular(8), ),
// child: Padding( const SizedBox(height: 12),
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), if (state.financesModels.isNotEmpty)
// child: Text( ListView.separated(
// 'Отменено: ${state.statisticModel.canceled}', shrinkWrap: true,
// style: const TextStyle( physics: const NeverScrollableScrollPhysics(),
// fontSize: 14, itemBuilder: (context, index) {
// fontWeight: FontWeight.w400, final Status modelStatus = switch (state.financesModels[index].paymentStatus) {
// color: Color(0xFFD01717), null => Status.inProgress,
// height: 1, PaymentStatus.completed => Status.success,
// ), PaymentStatus.rejected => Status.canceled,
// ), PaymentStatus.checked => Status.success,
// ), };
// ), return Material(
// ], color: const Color(0xFFF6F6F6),
// ), borderRadius: BorderRadius.circular(20),
// const SizedBox(height: 16), child: Padding(
// ], padding: const EdgeInsets.all(12),
// ), child: Column(
// ), crossAxisAlignment: CrossAxisAlignment.start,
// const SizedBox(height: 8), children: [
// Material( Row(
// color: Colors.white, mainAxisAlignment: MainAxisAlignment.spaceBetween,
// borderRadius: BorderRadius.circular(12), children: [
// child: Padding( Builder(builder: (context) {
// padding: const EdgeInsets.only(left: 16, right: 16, top: 16), final idLen = state.financesModels[index].bankId.length;
// child: SingleChildScrollView( return Text(
// physics: const NeverScrollableScrollPhysics(), '${state.financesModels[index].bankId.substring(0, 4)}...${state.financesModels[index].bankId.substring(idLen - 4, idLen)} ',
// child: Column( style: TextStyles.textSMedium.copyWith(color: appTheme.black),
// children: [ );
// CustomDropDownButton<PeriodFilterStatus>( }),
// items: PeriodFilterStatus.values Text(
// .map( state.financesModels[index].startDate.format('yyyy.MM.dd'),
// (value) => DropdownMenuItem( style: const TextStyle(
// value: value, color: Color(0xFF6D6F71),
// child: Text( height: 1,
// value.name, fontWeight: FontWeight.w500,
// style: TextStyles.textMMedium.copyWith(color: appTheme.black), ),
// ), ),
// ), ],
// ) ),
// .toList(), const SizedBox(height: 8),
// onChanged: (value) { Text(
// if (value == null) return; state.financesModels[index].title,
// context.read<ConsultationsArchiveCubit>().setConsultationsPeriod(value); style: TextStyle(
// }, color: appTheme.blueA400,
// value: state.periodFilterStatus ?? PeriodFilterStatus.values.first, height: 1,
// ), fontSize: 18,
// const SizedBox(height: 12), fontWeight: FontWeight.w600,
// CustomDropDownButton<ConsultationFilterStatus>( ),
// items: ConsultationFilterStatus.values ),
// .map( const SizedBox(height: 8),
// (value) => DropdownMenuItem( Text(
// value: value, 'Покупатель: ${state.financesModels[index].clientName}',
// child: Text( style: const TextStyle(
// value.name, color: Color(0xFF323F4B),
// style: TextStyles.textMMedium.copyWith(color: appTheme.black), height: 1,
// ), fontSize: 14,
// ), fontWeight: FontWeight.w500,
// ) ),
// .toList(), ),
// onChanged: (value) { const SizedBox(height: 8),
// if (value == null) return; Text(
// context.read<ConsultationsArchiveCubit>().setConsultationsStatus(value); '${state.financesModels[index].duration} ${getDayForm(state.financesModels[index].duration, s)}',
// }, style: const TextStyle(
// value: state.consultationFilterStatus, color: Color(0xFF323F4B),
// ), height: 1,
// const SizedBox(height: 12), fontSize: 14,
// if (state.consultations.isNotEmpty) fontWeight: FontWeight.w500,
// ListView.separated( ),
// shrinkWrap: true, ),
// physics: const NeverScrollableScrollPhysics(), const SizedBox(height: 8),
// itemBuilder: (context, index) { Container(
// return Material( height: 1,
// color: const Color(0xFFF6F6F6), width: double.infinity,
// borderRadius: BorderRadius.circular(20), decoration: const BoxDecoration(
// child: Padding( color: Color(0xFFDEDEDF),
// padding: const EdgeInsets.all(12), ),
// child: Column( ),
// crossAxisAlignment: CrossAxisAlignment.start, const SizedBox(height: 8),
// children: [ Row(
// Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
// mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
// children: [ Text(
// Text( '${state.financesModels[index].paymentPrice} ₽',
// state.consultations[index].id.toString(), style: const TextStyle(
// style: const TextStyle( color: Color(0xFF323F4B),
// color: Color(0xFF323F4B), height: 1,
// height: 1, fontSize: 18,
// fontWeight: FontWeight.w500, fontWeight: FontWeight.w600,
// ), ),
// ), ),
// Text( StatusWidget(
// state.consultations[index].startDate.format('yyyy.MM.dd'), status: modelStatus,
// style: const TextStyle( ),
// color: Color(0xFF6D6F71), ],
// height: 1, ),
// fontWeight: FontWeight.w500, ],
// ), ),
// ), ),
// ], );
// ), },
// const SizedBox(height: 8), separatorBuilder: (context, index) => const SizedBox(height: 12),
// Text( itemCount: state.financesModels.length,
// state.consultations[index].planTitle, )
// style: TextStyle( else
// color: appTheme.blueA400, const Padding(
// height: 1, padding: EdgeInsets.symmetric(vertical: 40.0),
// fontSize: 18, child: Text(
// fontWeight: FontWeight.w600, 'У вас нет таких консультаций',
// ), style: TextStyles.textMMedium,
// ), ),
// const SizedBox(height: 8), ),
// Text( ],
// 'Покупатель: ${state.consultations[index].userName}', ),
// style: const TextStyle( ),
// color: Color(0xFF323F4B), ),
// height: 1, ),
// fontSize: 14, ],
// fontWeight: FontWeight.w500, )),
// ), );
// ), },
// const SizedBox(height: 8), ),
// Text( );
// '${state.consultations[index].duration} ${getDayForm(state.consultations[index].duration, s)}', }
// style: const TextStyle( }
// color: Color(0xFF323F4B),
// height: 1,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// ),
// ),
// const SizedBox(height: 8),
// Container(
// height: 1,
// width: double.infinity,
// decoration: const BoxDecoration(
// color: Color(0xFFDEDEDF),
// ),
// ),
// const SizedBox(height: 8),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// '${state.consultations[index].consultantPayment.toStringAsFixed(2)} ₽',
// style: const TextStyle(
// color: Color(0xFF323F4B),
// height: 1,
// fontSize: 18,
// fontWeight: FontWeight.w600,
// ),
// ),
// StatusWidget(
// status: state.consultations[index].chosenPlanState,
// ),
// ],
// ),
// ],
// ),
// ),
// );
// },
// separatorBuilder: (context, index) => const SizedBox(height: 12),
// itemCount: state.financesModels.length,
// )
// else
// const Padding(
// padding: EdgeInsets.symmetric(vertical: 40.0),
// child: Text(
// 'У вас нет таких консультаций',
// style: TextStyles.textMMedium,
// ),
// ),
// ],
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// );
// },
// ),
// );
// }
// }

View File

@@ -9,13 +9,15 @@ import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/arc
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class ConsultationsArchiveScreen extends StatefulWidget { class ConsultationsArchiveScreen extends StatefulWidget {
const ConsultationsArchiveScreen({super.key}); const ConsultationsArchiveScreen({super.key});
static const name = 'consultationsArchiveScreen';
static const path = '/consultations-archive-screen';
@override @override
State<ConsultationsArchiveScreen> createState() => _ConsultationsArchiveScreenState(); State<ConsultationsArchiveScreen> createState() => _ConsultationsArchiveScreenState();
} }
@@ -164,6 +166,15 @@ class _ConsultationsArchiveScreenState extends State<ConsultationsArchiveScreen>
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final Status consultationStatus;
if (state.consultations[index].chosenPlanState == ChosenPlanStateEnum.stateCanceledByTheInitiator) {
consultationStatus = Status.canceled;
} else if (state.consultations[index].chosenPlanState == ChosenPlanStateEnum.stateChargeWasCollected) {
consultationStatus = Status.success;
} else {
consultationStatus = Status.inProgress;
}
return Material( return Material(
color: const Color(0xFFF6F6F6), color: const Color(0xFFF6F6F6),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
@@ -245,7 +256,7 @@ class _ConsultationsArchiveScreenState extends State<ConsultationsArchiveScreen>
), ),
), ),
StatusWidget( StatusWidget(
status: state.consultations[index].chosenPlanState, status: consultationStatus,
), ),
], ],
), ),

View File

@@ -0,0 +1,209 @@
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class UserPurchasesScreen extends StatefulWidget {
const UserPurchasesScreen({super.key});
static const name = 'userPurchasesScreen';
static const path = '/user-purchases-screen';
@override
State<UserPurchasesScreen> createState() => _UserPurchasesScreen();
}
class _UserPurchasesScreen extends State<UserPurchasesScreen> {
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocProvider<UserPurchasesCubit>(
create: (context) => sl<UserPurchasesCubit>()..getUserPurchases(),
child: BlocBuilder<UserPurchasesCubit, UserPurchasesState>(
builder: (context, state) {
return Scaffold(
backgroundColor: const Color(0xFFF0F2F5),
appBar: AppBar(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadiusDirectional.vertical(
bottom: Radius.circular(12),
),
),
title: Text(
'Финансы',
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
centerTitle: true,
),
body: state.isLoading
? const Center(
child: Loader(),
)
: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
children: [
const SizedBox(height: 8),
Material(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
child: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Column(
children: [
CustomDropDownButton<PeriodFilterStatus>(
items: PeriodFilterStatus.values
.map(
(value) => DropdownMenuItem(
value: value,
child: Text(
value.name,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
)
.toList(),
onChanged: (value) {
if (value == null) return;
context.read<UserPurchasesCubit>().setPurchasesPeriod(value);
},
value: state.periodFilterStatus ?? PeriodFilterStatus.values.first,
),
const SizedBox(height: 12),
if (state.purchasesModels.isNotEmpty)
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final Status purchaseStatus;
if (state.purchasesModels[index].chosenPlanState == ChosenPlanStateEnum.stateCanceledByTheInitiator) {
purchaseStatus = Status.canceled;
} else if (state.purchasesModels[index].chosenPlanState == ChosenPlanStateEnum.stateChargeWasCollected) {
purchaseStatus = Status.success;
} else {
purchaseStatus = Status.inProgress;
}
return Material(
color: const Color(0xFFF6F6F6),
borderRadius: BorderRadius.circular(20),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Builder(builder: (context) {
final idLen = state.purchasesModels[index].bankId.length;
return Text(
'${state.purchasesModels[index].bankId.substring(0, 4)}...${state.purchasesModels[index].bankId.substring(idLen - 4, idLen)} ',
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
);
}),
Text(
state.purchasesModels[index].startDate.format('yyyy.MM.dd'),
style: const TextStyle(
color: Color(0xFF6D6F71),
height: 1,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 8),
Text(
state.purchasesModels[index].title,
style: TextStyle(
color: appTheme.blueA400,
height: 1,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
Text(
'Продавец: ${state.purchasesModels[index].consultantName}',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Text(
'${state.purchasesModels[index].duration} ${getDayForm(state.purchasesModels[index].duration, s)}',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Container(
height: 1,
width: double.infinity,
decoration: const BoxDecoration(
color: Color(0xFFDEDEDF),
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${state.purchasesModels[index].paymentPrice} ₽',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
StatusWidget(
status: purchaseStatus,
),
],
),
],
),
),
);
},
separatorBuilder: (context, index) => const SizedBox(height: 12),
itemCount: state.purchasesModels.length,
)
else
const Padding(
padding: EdgeInsets.symmetric(vertical: 40.0),
child: Text(
'У вас нет таких консультаций',
style: TextStyles.textMMedium,
),
),
],
),
),
),
),
],
)),
);
},
),
);
}
}

View File

@@ -1,15 +1,15 @@
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class StatusWidget extends StatelessWidget { class StatusWidget extends StatelessWidget {
const StatusWidget({super.key, required this.status}); const StatusWidget({super.key, required this.status});
final ChosenPlanStateEnum status; final Status status;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (status == ChosenPlanStateEnum.stateCanceledByTheInitiator) { return switch (status){
return Material(
Status.canceled => Material(
color: appTheme.lightRed, color: appTheme.lightRed,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: Padding( child: Padding(
@@ -24,9 +24,9 @@ class StatusWidget extends StatelessWidget {
), ),
), ),
), ),
); ),
} else if (status == ChosenPlanStateEnum.stateChargeWasCollected) {
return Material( Status.success => Material(
color: const Color(0xFF0BB450).withAlpha(11), color: const Color(0xFF0BB450).withAlpha(11),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: const Padding( child: const Padding(
@@ -41,9 +41,9 @@ class StatusWidget extends StatelessWidget {
), ),
), ),
), ),
); ),
}
return Material( Status.inProgress => Material(
color: appTheme.transparentBlue, color: appTheme.transparentBlue,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: Padding( child: Padding(
@@ -58,6 +58,13 @@ class StatusWidget extends StatelessWidget {
), ),
), ),
), ),
); ),
};
} }
} }
enum Status {
canceled,
success,
inProgress
}

View File

@@ -4,6 +4,8 @@ import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/utils/image_constant.dart'; import 'package:eatvio_app/core/utils/image_constant.dart';
import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart';
import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart'; import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart';
import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart'; import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart';
@@ -40,7 +42,11 @@ class MenuScreen extends StatelessWidget {
leading: 'assets/icons/finance.svg', leading: 'assets/icons/finance.svg',
title: 'Мои финансы', title: 'Мои финансы',
onTap: () { onTap: () {
GoRouter.of(context).pushNamed('userFinanceScreen'); if (profileModel.roles.contains('consultant')) {
GoRouter.of(context).push(ConsultantFinancesScreen.path);
return;
}
GoRouter.of(context).push(UserPurchasesScreen.path);
}, },
), ),
_divider(context), _divider(context),

View File

@@ -2,8 +2,10 @@ import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/loader.dart';
@@ -26,6 +28,7 @@ import 'package:eatvio_app/features/profile/presentation/widgets/profile_blog_po
import 'package:eatvio_app/features/profile/presentation/widgets/weight_chart_widget.dart'; import 'package:eatvio_app/features/profile/presentation/widgets/weight_chart_widget.dart';
import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@@ -34,6 +37,7 @@ import 'package:go_router/go_router.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@@ -600,9 +604,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
_educationBlock(String title, String date) { _educationBlock(String title, String date) {
final parsedDate = DateTime.tryParse(date); final parsedDate = DateTime.tryParse(date);
final formattedDate = parsedDate != null final formattedDate = parsedDate != null ? DateFormat('d MMMM y', 'ru_RU').format(parsedDate) : date;
? DateFormat('d MMMM y', 'ru_RU').format(parsedDate)
: date;
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
@@ -640,7 +642,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
); );
} }
_socialLinks(ProfileStateLoaded state) { _socialLinks(ProfileStateLoaded state) {
if (!state.profile.roles.contains('consultant')) { if (!state.profile.roles.contains('consultant')) {
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -993,51 +994,70 @@ class _ProfileScreenState extends State<ProfileScreen> {
if (value == 'replace') { if (value == 'replace') {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
Navigator.of(context).pop(); CupertinoActionSheetAction(
final picker = ImagePicker(); onPressed: () async {
XFile? image = await picker.pickImage(source: ImageSource.camera); PermissionHandler.checkPermission(
if (image != null) { permission: Permission.camera,
await profileCubit.uploadPhoto(File(image.path)); title: s.allowAccessToCamera,
await sl<ProfileAvatarCubit>().getMyProfile(); description: s.allowAccessToCameraDescription,
} onGranted: () async {
}, AppRouter.navigatorKey.currentContext?.pop();
child: const Row( final picker = ImagePicker();
children: [ XFile? image = await picker.pickImage(source: ImageSource.camera);
Icon(Icons.photo_camera), if (image != null) {
SizedBox(width: 8), await profileCubit.uploadPhoto(File(image.path));
Text('Фото'), await sl<ProfileAvatarCubit>().getMyProfile();
], }
},
context: context,
);
},
child: const Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text('Фото'),
],
),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
Navigator.of(context).pop(); permission: Permission.photos,
final picker = ImagePicker(); title: s.allowAcessToGalery,
XFile? image = await picker.pickImage(source: ImageSource.gallery); description: s.allowAcessToGaleryDescription,
if (image != null) { onGranted: () async {
await profileCubit.uploadPhoto(File(image.path)); AppRouter.navigatorKey.currentContext?.pop();
await sl<ProfileAvatarCubit>().getMyProfile(); final picker = ImagePicker();
} XFile? image = await picker.pickImage(source: ImageSource.gallery);
}, if (image != null) {
child: const Row( await profileCubit.uploadPhoto(File(image.path));
children: [ await sl<ProfileAvatarCubit>().getMyProfile();
Icon(Icons.photo), }
SizedBox(width: 8), },
Text('Галерея'), context: context,
], );
},
child: const Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text('Галерея'),
],
),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }
if (value == 'delete') { if (value == 'delete') {

View File

@@ -73,6 +73,19 @@ class MessageLookup extends MessageLookupByLibrary {
"allergies_presence": MessageLookupByLibrary.simpleMessage( "allergies_presence": MessageLookupByLibrary.simpleMessage(
"Наличие аллергии", "Наличие аллергии",
), ),
"allowAccess": MessageLookupByLibrary.simpleMessage("Разрешить доступ"),
"allowAccessToCamera": MessageLookupByLibrary.simpleMessage(
"Разрешите доступ к камере",
),
"allowAccessToCameraDescription": MessageLookupByLibrary.simpleMessage(
"Предоставление доступа позволит вам делать фотографии и добавлять к записям",
),
"allowAcessToGalery": MessageLookupByLibrary.simpleMessage(
"Разрешите доступ к фото",
),
"allowAcessToGaleryDescription": MessageLookupByLibrary.simpleMessage(
"Предоставление доступа позволит вам добавлять фото к записям",
),
"another_form_of_comment": MessageLookupByLibrary.simpleMessage( "another_form_of_comment": MessageLookupByLibrary.simpleMessage(
"комментария", "комментария",
), ),

View File

@@ -28,9 +28,10 @@ class S {
static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
static Future<S> load(Locale locale) { static Future<S> load(Locale locale) {
final name = (locale.countryCode?.isEmpty ?? false) final name =
? locale.languageCode (locale.countryCode?.isEmpty ?? false)
: locale.toString(); ? locale.languageCode
: locale.toString();
final localeName = Intl.canonicalizedLocale(name); final localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) { return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName; Intl.defaultLocale = localeName;
@@ -3343,6 +3344,56 @@ class S {
args: [], args: [],
); );
} }
/// `Разрешите доступ к фото`
String get allowAcessToGalery {
return Intl.message(
'Разрешите доступ к фото',
name: 'allowAcessToGalery',
desc: '',
args: [],
);
}
/// `Предоставление доступа позволит вам добавлять фото к записям`
String get allowAcessToGaleryDescription {
return Intl.message(
'Предоставление доступа позволит вам добавлять фото к записям',
name: 'allowAcessToGaleryDescription',
desc: '',
args: [],
);
}
/// `Разрешите доступ к камере`
String get allowAccessToCamera {
return Intl.message(
'Разрешите доступ к камере',
name: 'allowAccessToCamera',
desc: '',
args: [],
);
}
/// `Предоставление доступа позволит вам делать фотографии и добавлять к записям`
String get allowAccessToCameraDescription {
return Intl.message(
'Предоставление доступа позволит вам делать фотографии и добавлять к записям',
name: 'allowAccessToCameraDescription',
desc: '',
args: [],
);
}
/// `Разрешить доступ`
String get allowAccess {
return Intl.message(
'Разрешить доступ',
name: 'allowAccess',
desc: '',
args: [],
);
}
} }
class AppLocalizationDelegate extends LocalizationsDelegate<S> { class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@@ -397,6 +397,11 @@
"planStarted": "План «{planName}» начался", "planStarted": "План «{planName}» начался",
"userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»", "userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»",
"userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»", "userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»",
"markAsRead": "Отметить всё прочитанным" "markAsRead": "Отметить всё прочитанным",
"allowAcessToGalery": "Разрешите доступ к фото",
"allowAcessToGaleryDescription": "Предоставление доступа позволит вам добавлять фото к записям",
"allowAccessToCamera": "Разрешите доступ к камере",
"allowAccessToCameraDescription": "Предоставление доступа позволит вам делать фотографии и добавлять к записям",
"allowAccess": "Разрешить доступ"
} }

View File

@@ -70,6 +70,7 @@ dependencies:
version: ^3.0.2 version: ^3.0.2
package_info_plus: ^8.3.0 package_info_plus: ^8.3.0
device_info_plus: ^11.4.0 device_info_plus: ^11.4.0
permission_handler: ^12.0.0+1
dev_dependencies: dev_dependencies:
build_runner: ^2.4.15 build_runner: ^2.4.15