added clear message for denied camera or gallery access
This commit is contained in:
@@ -42,7 +42,7 @@ Future<AvatarSheetResult?> showAvatarActionsSheet(
|
||||
onTap: () async {
|
||||
final bytes = await _pickAndCrop(
|
||||
sheetContext,
|
||||
FilePick.singleGalleryPick,
|
||||
() => FilePick.singleGalleryPick(sheetContext),
|
||||
);
|
||||
if (bytes == null || !sheetContext.mounted) return;
|
||||
result = AvatarUploadResult(bytes);
|
||||
@@ -53,7 +53,10 @@ Future<AvatarSheetResult?> showAvatarActionsSheet(
|
||||
leading: const Icon(Icons.photo_camera_outlined),
|
||||
title: const Text('Foto aufnehmen'),
|
||||
onTap: () async {
|
||||
final bytes = await _pickAndCrop(sheetContext, FilePick.cameraPick);
|
||||
final bytes = await _pickAndCrop(
|
||||
sheetContext,
|
||||
() => FilePick.cameraPick(sheetContext),
|
||||
);
|
||||
if (bytes == null || !sheetContext.mounted) return;
|
||||
result = AvatarUploadResult(bytes);
|
||||
Navigator.of(sheetContext).pop();
|
||||
|
||||
@@ -21,7 +21,9 @@ Future<Uint8List?> showChatBackgroundPickerSheet(BuildContext context) async {
|
||||
leading: const Icon(Icons.photo_library_outlined),
|
||||
title: const Text('Aus Galerie wählen'),
|
||||
onTap: () async {
|
||||
final bytes = await _pickRaw(FilePick.singleGalleryPick);
|
||||
final bytes = await _pickRaw(
|
||||
() => FilePick.singleGalleryPick(sheetContext),
|
||||
);
|
||||
if (bytes == null || !sheetContext.mounted) return;
|
||||
result = bytes;
|
||||
Navigator.of(sheetContext).pop();
|
||||
@@ -31,7 +33,7 @@ Future<Uint8List?> showChatBackgroundPickerSheet(BuildContext context) async {
|
||||
leading: const Icon(Icons.photo_camera_outlined),
|
||||
title: const Text('Foto aufnehmen'),
|
||||
onTap: () async {
|
||||
final bytes = await _pickRaw(FilePick.cameraPick);
|
||||
final bytes = await _pickRaw(() => FilePick.cameraPick(sheetContext));
|
||||
if (bytes == null || !sheetContext.mounted) return;
|
||||
result = bytes;
|
||||
Navigator.of(sheetContext).pop();
|
||||
|
||||
@@ -1,22 +1,77 @@
|
||||
import 'package:app_settings/app_settings.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
import '../api/errors/error_mapper.dart';
|
||||
import '../api/errors/permission_exception.dart';
|
||||
import 'confirm_dialog.dart';
|
||||
import 'info_dialog.dart';
|
||||
|
||||
/// Thin wrapper around the platform pickers. Every method resolves to `null`
|
||||
/// when the user cancels **or** when picking failed; failures are reported to
|
||||
/// the user right here (permission denial with a shortcut to the system
|
||||
/// settings, everything else as a plain error dialog) so call sites can treat
|
||||
/// the result like a cancel.
|
||||
class FilePick {
|
||||
static final _picker = ImagePicker();
|
||||
|
||||
static Future<List<XFile>?> multipleGalleryPick() async {
|
||||
final pickedImages = await _picker.pickMultiImage();
|
||||
return pickedImages.isNotEmpty ? pickedImages : null;
|
||||
}
|
||||
static Future<List<XFile>?> multipleGalleryPick(BuildContext context) =>
|
||||
_guarded(context, () async {
|
||||
final pickedImages = await _picker.pickMultiImage();
|
||||
return pickedImages.isNotEmpty ? pickedImages : null;
|
||||
});
|
||||
|
||||
static Future<XFile?> singleGalleryPick() =>
|
||||
_picker.pickImage(source: ImageSource.gallery);
|
||||
static Future<XFile?> singleGalleryPick(BuildContext context) =>
|
||||
_guarded(context, () => _picker.pickImage(source: ImageSource.gallery));
|
||||
|
||||
static Future<XFile?> cameraPick() =>
|
||||
_picker.pickImage(source: ImageSource.camera);
|
||||
static Future<XFile?> cameraPick(BuildContext context) =>
|
||||
_guarded(context, () => _picker.pickImage(source: ImageSource.camera));
|
||||
|
||||
static Future<List<String>?> documentPick() async {
|
||||
final result = await FilePicker.pickFiles(allowMultiple: true);
|
||||
return result?.files.map((e) => e.path).nonNulls.toList();
|
||||
}
|
||||
|
||||
static Future<T?> _guarded<T>(
|
||||
BuildContext context,
|
||||
Future<T?> Function() pick,
|
||||
) async {
|
||||
try {
|
||||
return await pick();
|
||||
} on PlatformException catch (e) {
|
||||
final denied = PermissionException.fromPlatform(e);
|
||||
if (!context.mounted) return null;
|
||||
if (denied != null) {
|
||||
_showPermissionDialog(context, denied);
|
||||
} else {
|
||||
_showErrorDialog(context, e);
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
if (context.mounted) _showErrorDialog(context, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static void _showPermissionDialog(
|
||||
BuildContext context,
|
||||
PermissionException denied,
|
||||
) => ConfirmDialog(
|
||||
icon: Icons.block_outlined,
|
||||
title: 'Zugriff verweigert',
|
||||
content: denied.userMessage,
|
||||
confirmButton: 'Einstellungen öffnen',
|
||||
onConfirm: AppSettings.openAppSettings,
|
||||
).asDialog(context);
|
||||
|
||||
static void _showErrorDialog(BuildContext context, Object error) {
|
||||
final message = errorToUserMessage(error);
|
||||
final details = errorToTechnicalDetails(error);
|
||||
final body = details != null && details != message
|
||||
? '$message\n\n$details'
|
||||
: message;
|
||||
InfoDialog.show(context, body, copyable: true, title: 'Fehler');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user