added clear message for denied camera or gallery access

This commit is contained in:
2026-09-02 20:31:44 +02:00
parent f19f212fb4
commit 43dfc52bc7
8 changed files with 146 additions and 21 deletions
+5 -2
View File
@@ -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();
+4 -2
View File
@@ -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();
+63 -8
View File
@@ -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');
}
}