diff --git a/ios/Podfile b/ios/Podfile index 27c089c7..53a8d390 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -40,6 +40,17 @@ post_install do |installer| target.build_configurations.each do |config| config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" 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 diff --git a/lib/core/permission_handler/permission_handler.dart b/lib/core/permission_handler/permission_handler.dart new file mode 100644 index 00000000..a311e84d --- /dev/null +++ b/lib/core/permission_handler/permission_handler.dart @@ -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 checkPermission({ + required Permission permission, + required String title, + required String description, + required Future 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); + } + } +} diff --git a/lib/core/widgets/show_error_dialog.dart b/lib/core/widgets/show_error_dialog.dart index 9685ad14..8ae4666e 100644 --- a/lib/core/widgets/show_error_dialog.dart +++ b/lib/core/widgets/show_error_dialog.dart @@ -4,8 +4,9 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:permission_handler/permission_handler.dart'; -Future showErrorDialog(BuildContext context, {String? title, String? description}) async { +Future showErrorDialog(BuildContext context, {String? title, String? description, String? buttonTitle, bool? needToOpenSettings}) async { return await showDialog( context: context, barrierColor: Colors.transparent, @@ -43,8 +44,13 @@ Future showErrorDialog(BuildContext context, {String? title, String? descr Align( alignment: Alignment.centerRight, child: TextButton( - onPressed: () => AppRouter.navigatorKey.currentContext?.pop(), - child: Text(s.ok, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), + onPressed: () { + AppRouter.navigatorKey.currentContext?.pop(); + if (needToOpenSettings ?? false) { + openAppSettings(); + } + }, + child: Text(buttonTitle ?? s.ok, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), ), ), const SizedBox(height: 12), diff --git a/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart b/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart index f7b57ddd..d1682338 100644 --- a/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart +++ b/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'dart:io'; +import 'package:eatvio_app/core/app_router.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/widgets/show_error_dialog.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:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:permission_handler/permission_handler.dart'; class BlogPostCreateScreen extends StatefulWidget { final FeedBlogPostModel? feedBlogPostModel; @@ -80,47 +83,65 @@ class _BlogPostCreateScreenState extends State { _showImagePicker() { showCupertinoModalPopup( context: context, - builder: (context) => CupertinoActionSheet( - actions: [ - CupertinoActionSheetAction( - onPressed: () async { - final picker = ImagePicker(); - image = await picker.pickImage(source: ImageSource.camera); - setState(() {}); - if (!context.mounted) return; - Navigator.of(context).pop(); - }, - child: const Row( - children: [ - Icon(Icons.photo_camera), - SizedBox(width: 8), - Text('Фото'), - ], + builder: (context) { + final s = S.of(context); + return CupertinoActionSheet( + actions: [ + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.camera, + title: s.allowAccessToCamera, + description: s.allowAccessToCameraDescription, + onGranted: () async { + final picker = ImagePicker(); + image = await picker.pickImage(source: ImageSource.camera); + setState(() {}); + AppRouter.navigatorKey.currentContext?.pop(); + }, + context: context, + ); + }, + child: const Row( + children: [ + Icon(Icons.photo_camera), + SizedBox(width: 8), + Text('Фото'), + ], + ), ), - ), - CupertinoActionSheetAction( - onPressed: () async { - final picker = ImagePicker(); - image = await picker.pickImage(source: ImageSource.gallery); - setState(() {}); - if (!context.mounted) return; - Navigator.of(context).pop(); - }, - child: const Row( - children: [ - Icon(Icons.photo), - SizedBox(width: 8), - Text('Галерея'), - ], + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.photos, + title: s.allowAcessToGalery, + description: s.allowAcessToGaleryDescription, + onGranted: () async { + final picker = ImagePicker(); + image = await picker.pickImage(source: ImageSource.gallery); + setState(() {}); + if (!context.mounted) return; + AppRouter.navigatorKey.currentContext?.pop(); + }, + 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('Отмена'), - ), - ), + ); + }, ); } diff --git a/lib/features/create_diet/presentation/widgets/diet_image_picker_widget.dart b/lib/features/create_diet/presentation/widgets/diet_image_picker_widget.dart index c6250bb4..192df5d3 100644 --- a/lib/features/create_diet/presentation/widgets/diet_image_picker_widget.dart +++ b/lib/features/create_diet/presentation/widgets/diet_image_picker_widget.dart @@ -1,6 +1,8 @@ import 'dart:io'; 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/theme_helper.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_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:permission_handler/permission_handler.dart'; class DietImagePickerWidget extends StatefulWidget { const DietImagePickerWidget({super.key}); @@ -28,11 +32,18 @@ class _DietImagePickerWidgetState extends State { actions: [ CupertinoActionSheetAction( onPressed: () async { - final picker = ImagePicker(); - final image = await picker.pickImage(source: ImageSource.camera); - cubit.setImage(image); - if (!context.mounted) return; - Navigator.of(context).pop(); + PermissionHandler.checkPermission( + permission: Permission.camera, + title: s.allowAccessToCamera, + description: s.allowAccessToCameraDescription, + onGranted: () async { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.camera); + cubit.setImage(image); + AppRouter.navigatorKey.currentContext?.pop(); + }, + context: context, + ); }, child: Row( children: [ @@ -44,11 +55,18 @@ class _DietImagePickerWidgetState extends State { ), CupertinoActionSheetAction( onPressed: () async { - final picker = ImagePicker(); - final image = await picker.pickImage(source: ImageSource.gallery); - cubit.setImage(image); - if (!context.mounted) return; - Navigator.of(context).pop(); + PermissionHandler.checkPermission( + permission: Permission.photos, + title: s.allowAcessToGalery, + description: s.allowAcessToGaleryDescription, + onGranted: () async { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.gallery); + cubit.setImage(image); + AppRouter.navigatorKey.currentContext?.pop(); + }, + context: context, + ); }, child: Row( children: [ diff --git a/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart b/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart index d5041035..67af4a61 100644 --- a/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart +++ b/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart @@ -1,6 +1,8 @@ import 'dart:io'; 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/theme_helper.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_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:permission_handler/permission_handler.dart'; class PlanImagePickerWidget extends StatefulWidget { const PlanImagePickerWidget({super.key}); @@ -28,11 +32,18 @@ class _PlanImagePickerWidgetState extends State { actions: [ CupertinoActionSheetAction( onPressed: () async { - final picker = ImagePicker(); - final image = await picker.pickImage(source: ImageSource.camera); - cubit.setImage(image); - if (!context.mounted) return; - Navigator.of(context).pop(); + PermissionHandler.checkPermission( + permission: Permission.camera, + title: s.allowAccessToCamera, + description: s.allowAccessToCameraDescription, + onGranted: () async { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.camera); + cubit.setImage(image); + AppRouter.navigatorKey.currentContext?.pop(); + }, + context: context, + ); }, child: Row( children: [ @@ -44,11 +55,18 @@ class _PlanImagePickerWidgetState extends State { ), CupertinoActionSheetAction( onPressed: () async { - final picker = ImagePicker(); - final image = await picker.pickImage(source: ImageSource.gallery); - cubit.setImage(image); - if (!context.mounted) return; - Navigator.of(context).pop(); + PermissionHandler.checkPermission( + permission: Permission.photos, + title: s.allowAcessToGalery, + description: s.allowAcessToGaleryDescription, + onGranted: () async { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.gallery); + cubit.setImage(image); + AppRouter.navigatorKey.currentContext?.pop(); + }, + context: context, + ); }, child: Row( children: [ diff --git a/lib/features/create_recipe/presentation/widgets/image_widget.dart b/lib/features/create_recipe/presentation/widgets/image_widget.dart index 534499e4..eb444b8e 100644 --- a/lib/features/create_recipe/presentation/widgets/image_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/image_widget.dart @@ -1,11 +1,16 @@ 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/generated/l10n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:permission_handler/permission_handler.dart'; class ImageWidget extends StatefulWidget { const ImageWidget({super.key, required this.image}); @@ -67,55 +72,72 @@ class _ImageWidgetState extends State { _showImagePicker() { showCupertinoModalPopup( context: context, - builder: (context) => CupertinoActionSheet( - actions: [ - CupertinoActionSheetAction( - onPressed: () async { - final picker = ImagePicker(); - final image = await picker.pickImage(source: ImageSource.camera); - setState(() {}); - if (image != null) { - createRecipeCubit.replaceImage(File(image.path)); - createRecipeCubit.validateImage(); - } - if (!context.mounted) return; - Navigator.of(context).pop(); - }, - child: const Row( - children: [ - Icon(Icons.photo_camera), - SizedBox(width: 8), - Text('Фото'), - ], + builder: (context) { + final s = S.of(context); + return CupertinoActionSheet( + actions: [ + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.camera, + title: s.allowAccessToCamera, + description: s.allowAccessToCameraDescription, + onGranted: () async { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.camera); + setState(() {}); + if (image != null) { + createRecipeCubit.replaceImage(File(image.path)); + createRecipeCubit.validateImage(); + } + AppRouter.navigatorKey.currentContext?.pop(); + }, + context: context, + ); + }, + child: const Row( + children: [ + Icon(Icons.photo_camera), + SizedBox(width: 8), + Text('Фото'), + ], + ), ), - ), - CupertinoActionSheetAction( - onPressed: () async { - final picker = ImagePicker(); - final image = await picker.pickImage(source: ImageSource.gallery); - setState(() {}); - if (image != null) { - createRecipeCubit.replaceImage(File(image.path)); - createRecipeCubit.validateImage(); - } - if (!context.mounted) return; - Navigator.of(context).pop(); - }, - child: const Row( - children: [ - Icon(Icons.photo), - SizedBox(width: 8), - Text('Галерея'), - ], + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.photos, + title: s.allowAcessToGalery, + description: s.allowAcessToGaleryDescription, + onGranted: () async { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.gallery); + setState(() {}); + if (image != null) { + createRecipeCubit.replaceImage(File(image.path)); + createRecipeCubit.validateImage(); + } + AppRouter.navigatorKey.currentContext?.pop(); + }, + 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('Отмена'), - ), - ), + ); + }, ); } } diff --git a/lib/features/create_recipe/presentation/widgets/step_image_widget.dart b/lib/features/create_recipe/presentation/widgets/step_image_widget.dart index 1189f0f8..d00d08c7 100644 --- a/lib/features/create_recipe/presentation/widgets/step_image_widget.dart +++ b/lib/features/create_recipe/presentation/widgets/step_image_widget.dart @@ -1,10 +1,15 @@ 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/generated/l10n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:permission_handler/permission_handler.dart'; class StepImageWidget extends StatefulWidget { final CreateRecipeCubit createRecipeCubit; @@ -12,7 +17,8 @@ class StepImageWidget extends StatefulWidget { const StepImageWidget({ super.key, - required this.index, required this.createRecipeCubit, + required this.index, + required this.createRecipeCubit, }); @override @@ -79,53 +85,70 @@ class _StepImageWidgetState extends State { _showImagePicker() { showCupertinoModalPopup( context: context, - builder: (context) => CupertinoActionSheet( - actions: [ - CupertinoActionSheetAction( - onPressed: () async { - final picker = ImagePicker(); - image = await picker.pickImage(source: ImageSource.camera); - setState(() {}); - if (image != null) { - widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index); - } - if (!context.mounted) return; - Navigator.of(context).pop(); - }, - child: const Row( - children: [ - Icon(Icons.photo_camera), - SizedBox(width: 8), - Text('Фото'), - ], + builder: (context) { + final s = S.of(context); + return CupertinoActionSheet( + actions: [ + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.camera, + title: s.allowAccessToCamera, + description: s.allowAccessToCameraDescription, + onGranted: () async { + final picker = ImagePicker(); + image = await picker.pickImage(source: ImageSource.camera); + setState(() {}); + if (image != null) { + widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index); + } + AppRouter.navigatorKey.currentContext?.pop(); + }, + context: context, + ); + }, + child: const Row( + children: [ + Icon(Icons.photo_camera), + SizedBox(width: 8), + Text('Фото'), + ], + ), ), - ), - CupertinoActionSheetAction( - onPressed: () async { - final picker = ImagePicker(); - image = await picker.pickImage(source: ImageSource.gallery); - setState(() {}); - if (image != null) { - widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index); - } - if (!context.mounted) return; - Navigator.of(context).pop(); - }, - child: const Row( - children: [ - Icon(Icons.photo), - SizedBox(width: 8), - Text('Галерея'), - ], + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.photos, + title: s.allowAcessToGalery, + description: s.allowAcessToGaleryDescription, + onGranted: () async { + final picker = ImagePicker(); + image = await picker.pickImage(source: ImageSource.gallery); + setState(() {}); + if (image != null) { + widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index); + } + AppRouter.navigatorKey.currentContext?.pop(); + }, + 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('Отмена'), - ), - ), + ); + }, ); } } diff --git a/lib/features/diary/presentation/widgets/photo_gallery_widget.dart b/lib/features/diary/presentation/widgets/photo_gallery_widget.dart index 80c7e58a..7a89d466 100644 --- a/lib/features/diary/presentation/widgets/photo_gallery_widget.dart +++ b/lib/features/diary/presentation/widgets/photo_gallery_widget.dart @@ -1,16 +1,21 @@ import 'dart:io'; 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/permission_handler/permission_handler.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/presentation/bloc/diary_cubit.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/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:permission_handler/permission_handler.dart'; class PhotoGalleryWidget extends StatefulWidget { const PhotoGalleryWidget({super.key}); @@ -32,55 +37,75 @@ class _PhotoGalleryWidgetState extends State { showImagePicker(DiaryDayModel diaryDay) { showCupertinoModalPopup( context: context, - builder: (context) => CupertinoActionSheet( - actions: [ - CupertinoActionSheetAction( - onPressed: () async { - Navigator.of(context).pop(); - final picker = ImagePicker(); - XFile? image = await picker.pickImage(source: ImageSource.camera); - if (image != null) { - diaryCubit.uploadPhotos( - files: [File(image.path)], - diaryId: diaryDay.id, + builder: (context) { + final s = S.of(context); + return CupertinoActionSheet( + actions: [ + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.camera, + title: s.allowAccessToCamera, + description: s.allowAccessToCameraDescription, + 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( - children: [ - Icon(Icons.photo_camera), - SizedBox(width: 8), - Text('Фото'), - ], + }, + child: const Row( + children: [ + Icon(Icons.photo_camera), + SizedBox(width: 8), + Text('Фото'), + ], + ), ), - ), - CupertinoActionSheetAction( - onPressed: () async { - Navigator.of(context).pop(); - final picker = ImagePicker(); - List? images = await picker.pickMultiImage(); - if (images.isNotEmpty) { - diaryCubit.uploadPhotos( - files: images.map((f) => File(f.path)).toList(), - diaryId: diaryDay.id, + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.photos, + title: s.allowAcessToGalery, + description: s.allowAcessToGaleryDescription, + onGranted: () async { + AppRouter.navigatorKey.currentContext?.pop(); + final picker = ImagePicker(); + List? 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( - children: [ - Icon(Icons.photo), - SizedBox(width: 8), - 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('Отмена'), - ), - ), + ); + }, ); } @@ -92,7 +117,9 @@ class _PhotoGalleryWidgetState extends State { int mealIndex = state.selectedMeal - 1; if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) { return GestureDetector( - onTap: () => showImagePicker(state.diary), + onTap: () async { + showImagePicker(state.diary); + }, child: SvgPicture.asset( width: MediaQuery.of(context).size.width - 32, 'assets/no_one_image_was_added.svg', diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index ef9aac1a..6d99d92b 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -2,8 +2,10 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.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/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/widgets/data_error_widget.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/core/locator_service.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/material.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:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:share_plus/share_plus.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -600,9 +604,7 @@ class _ProfileScreenState extends State { _educationBlock(String title, String date) { final parsedDate = DateTime.tryParse(date); - final formattedDate = parsedDate != null - ? DateFormat('d MMMM y', 'ru_RU').format(parsedDate) - : date; + final formattedDate = parsedDate != null ? DateFormat('d MMMM y', 'ru_RU').format(parsedDate) : date; return SizedBox( width: double.infinity, @@ -640,7 +642,6 @@ class _ProfileScreenState extends State { ); } - _socialLinks(ProfileStateLoaded state) { if (!state.profile.roles.contains('consultant')) { return const SizedBox.shrink(); @@ -993,51 +994,70 @@ class _ProfileScreenState extends State { if (value == 'replace') { showCupertinoModalPopup( context: context, - builder: (context) => CupertinoActionSheet( - actions: [ - CupertinoActionSheetAction( - onPressed: () async { - Navigator.of(context).pop(); - final picker = ImagePicker(); - XFile? image = await picker.pickImage(source: ImageSource.camera); - if (image != null) { - await profileCubit.uploadPhoto(File(image.path)); - await sl().getMyProfile(); - } - }, - child: const Row( - children: [ - Icon(Icons.photo_camera), - SizedBox(width: 8), - Text('Фото'), - ], + builder: (context) { + final s = S.of(context); + return CupertinoActionSheet( + actions: [ + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.camera, + title: s.allowAccessToCamera, + description: s.allowAccessToCameraDescription, + onGranted: () async { + AppRouter.navigatorKey.currentContext?.pop(); + final picker = ImagePicker(); + XFile? image = await picker.pickImage(source: ImageSource.camera); + if (image != null) { + await profileCubit.uploadPhoto(File(image.path)); + await sl().getMyProfile(); + } + }, + context: context, + ); + }, + child: const Row( + children: [ + Icon(Icons.photo_camera), + SizedBox(width: 8), + Text('Фото'), + ], + ), ), - ), - CupertinoActionSheetAction( - onPressed: () async { - Navigator.of(context).pop(); - final picker = ImagePicker(); - XFile? image = await picker.pickImage(source: ImageSource.gallery); - if (image != null) { - await profileCubit.uploadPhoto(File(image.path)); - await sl().getMyProfile(); - } - }, - child: const Row( - children: [ - Icon(Icons.photo), - SizedBox(width: 8), - Text('Галерея'), - ], + CupertinoActionSheetAction( + onPressed: () async { + PermissionHandler.checkPermission( + permission: Permission.photos, + title: s.allowAcessToGalery, + description: s.allowAcessToGaleryDescription, + onGranted: () async { + AppRouter.navigatorKey.currentContext?.pop(); + final picker = ImagePicker(); + XFile? image = await picker.pickImage(source: ImageSource.gallery); + if (image != null) { + await profileCubit.uploadPhoto(File(image.path)); + await sl().getMyProfile(); + } + }, + 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') { diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 611eb301..e318c962 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -73,6 +73,19 @@ class MessageLookup extends MessageLookupByLibrary { "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( "комментария", ), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 0bfd0ea0..9f338062 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -3344,6 +3344,56 @@ class S { 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 { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 1145f9d1..96ddc4be 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -397,6 +397,11 @@ "planStarted": "План «{planName}» начался", "userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»", "userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»", - "markAsRead": "Отметить всё прочитанным" + "markAsRead": "Отметить всё прочитанным", + "allowAcessToGalery": "Разрешите доступ к фото", + "allowAcessToGaleryDescription": "Предоставление доступа позволит вам добавлять фото к записям", + "allowAccessToCamera": "Разрешите доступ к камере", + "allowAccessToCameraDescription": "Предоставление доступа позволит вам делать фотографии и добавлять к записям", + "allowAccess": "Разрешить доступ" } diff --git a/pubspec.yaml b/pubspec.yaml index 97712189..086abd4d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -69,6 +69,7 @@ dependencies: shared_preferences: ^2.5.3 version: ^3.0.2 package_info_plus: ^8.3.0 + permission_handler: ^12.0.0+1 dev_dependencies: build_runner: ^2.4.15