diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index efc79751..3aa576a0 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -15,6 +14,7 @@ import '../../i18n/strings.g.dart'; import '../main_screen.dart'; import '../../mixins/refreshable.dart'; import '../../services/download_storage_service.dart'; +import '../../services/file_picker_service.dart'; import '../../services/saf_storage_service.dart'; import '../../providers/settings_provider.dart'; import '../../providers/theme_provider.dart'; @@ -506,7 +506,7 @@ class _SettingsScreenState extends State with FocusableTab { return; } } else { - final result = await FilePicker.platform.getDirectoryPath(dialogTitle: t.settings.selectFolder); + final result = await FilePickerService.instance.getDirectoryPath(dialogTitle: t.settings.selectFolder); selectedPath = result; } diff --git a/lib/services/file_picker_service.dart b/lib/services/file_picker_service.dart new file mode 100644 index 00000000..71bbb52d --- /dev/null +++ b/lib/services/file_picker_service.dart @@ -0,0 +1,46 @@ +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/services.dart'; + +import '../utils/app_logger.dart'; + +/// Serializes file_picker invocations to avoid +/// `PlatformException(already_active, File picker is already active)`. +class FilePickerService { + static final FilePickerService _instance = FilePickerService._(); + static FilePickerService get instance => _instance; + FilePickerService._(); + + bool _active = false; + + Future _guard(String opName, Future Function() body) async { + if (_active) return null; + _active = true; + try { + return await body(); + } on PlatformException catch (e, st) { + if (e.code == 'already_active') return null; + appLogger.e('FilePicker.$opName failed', error: e, stackTrace: st); + rethrow; + } finally { + _active = false; + } + } + + Future pickFiles({ + FileType type = FileType.any, + List? allowedExtensions, + bool withData = false, + }) { + return _guard('pickFiles', () => FilePicker.platform.pickFiles( + type: type, + allowedExtensions: allowedExtensions, + withData: withData, + )); + } + + Future getDirectoryPath({String? dialogTitle}) { + return _guard('getDirectoryPath', () => FilePicker.platform.getDirectoryPath( + dialogTitle: dialogTitle, + )); + } +} diff --git a/lib/widgets/artwork_picker_dialog.dart b/lib/widgets/artwork_picker_dialog.dart index e9c4d74d..1aecc716 100644 --- a/lib/widgets/artwork_picker_dialog.dart +++ b/lib/widgets/artwork_picker_dialog.dart @@ -4,6 +4,7 @@ import 'package:material_symbols_icons/symbols.dart'; import '../focus/focusable_button.dart'; import '../focus/focusable_wrapper.dart'; import '../i18n/strings.g.dart'; +import '../services/file_picker_service.dart'; import '../services/plex_client.dart'; import '../utils/dialogs.dart'; import '../utils/snackbar_helper.dart'; @@ -105,7 +106,7 @@ class _ArtworkPickerDialogState extends State { } Future _uploadFile() async { - final result = await FilePicker.platform.pickFiles( + final result = await FilePickerService.instance.pickFiles( type: FileType.image, withData: true, ); diff --git a/lib/widgets/video_controls/sheets/video_settings_sheet.dart b/lib/widgets/video_controls/sheets/video_settings_sheet.dart index ba7e96ba..3420437e 100644 --- a/lib/widgets/video_controls/sheets/video_settings_sheet.dart +++ b/lib/widgets/video_controls/sheets/video_settings_sheet.dart @@ -13,6 +13,7 @@ import 'package:provider/provider.dart'; import '../../../models/shader_preset.dart'; import '../../../mpv/mpv.dart'; import '../../../providers/shader_provider.dart'; +import '../../../services/file_picker_service.dart'; import '../../../services/settings_service.dart'; import '../../../services/shader_service.dart'; import '../../../services/sleep_timer_service.dart'; @@ -744,7 +745,7 @@ class _VideoSettingsSheetState extends State { } Future _importCustomShader(ShaderProvider shaderProvider) async { - final result = await FilePicker.platform.pickFiles( + final result = await FilePickerService.instance.pickFiles( type: FileType.custom, allowedExtensions: ['glsl'], );