fix: serialize file picker calls
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@@ -15,6 +14,7 @@ import '../../i18n/strings.g.dart';
|
|||||||
import '../main_screen.dart';
|
import '../main_screen.dart';
|
||||||
import '../../mixins/refreshable.dart';
|
import '../../mixins/refreshable.dart';
|
||||||
import '../../services/download_storage_service.dart';
|
import '../../services/download_storage_service.dart';
|
||||||
|
import '../../services/file_picker_service.dart';
|
||||||
import '../../services/saf_storage_service.dart';
|
import '../../services/saf_storage_service.dart';
|
||||||
import '../../providers/settings_provider.dart';
|
import '../../providers/settings_provider.dart';
|
||||||
import '../../providers/theme_provider.dart';
|
import '../../providers/theme_provider.dart';
|
||||||
@@ -506,7 +506,7 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final result = await FilePicker.platform.getDirectoryPath(dialogTitle: t.settings.selectFolder);
|
final result = await FilePickerService.instance.getDirectoryPath(dialogTitle: t.settings.selectFolder);
|
||||||
selectedPath = result;
|
selectedPath = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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<T?> _guard<T>(String opName, Future<T?> 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<FilePickerResult?> pickFiles({
|
||||||
|
FileType type = FileType.any,
|
||||||
|
List<String>? allowedExtensions,
|
||||||
|
bool withData = false,
|
||||||
|
}) {
|
||||||
|
return _guard('pickFiles', () => FilePicker.platform.pickFiles(
|
||||||
|
type: type,
|
||||||
|
allowedExtensions: allowedExtensions,
|
||||||
|
withData: withData,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getDirectoryPath({String? dialogTitle}) {
|
||||||
|
return _guard('getDirectoryPath', () => FilePicker.platform.getDirectoryPath(
|
||||||
|
dialogTitle: dialogTitle,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import '../focus/focusable_button.dart';
|
import '../focus/focusable_button.dart';
|
||||||
import '../focus/focusable_wrapper.dart';
|
import '../focus/focusable_wrapper.dart';
|
||||||
import '../i18n/strings.g.dart';
|
import '../i18n/strings.g.dart';
|
||||||
|
import '../services/file_picker_service.dart';
|
||||||
import '../services/plex_client.dart';
|
import '../services/plex_client.dart';
|
||||||
import '../utils/dialogs.dart';
|
import '../utils/dialogs.dart';
|
||||||
import '../utils/snackbar_helper.dart';
|
import '../utils/snackbar_helper.dart';
|
||||||
@@ -105,7 +106,7 @@ class _ArtworkPickerDialogState extends State<ArtworkPickerDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _uploadFile() async {
|
Future<void> _uploadFile() async {
|
||||||
final result = await FilePicker.platform.pickFiles(
|
final result = await FilePickerService.instance.pickFiles(
|
||||||
type: FileType.image,
|
type: FileType.image,
|
||||||
withData: true,
|
withData: true,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../../../models/shader_preset.dart';
|
import '../../../models/shader_preset.dart';
|
||||||
import '../../../mpv/mpv.dart';
|
import '../../../mpv/mpv.dart';
|
||||||
import '../../../providers/shader_provider.dart';
|
import '../../../providers/shader_provider.dart';
|
||||||
|
import '../../../services/file_picker_service.dart';
|
||||||
import '../../../services/settings_service.dart';
|
import '../../../services/settings_service.dart';
|
||||||
import '../../../services/shader_service.dart';
|
import '../../../services/shader_service.dart';
|
||||||
import '../../../services/sleep_timer_service.dart';
|
import '../../../services/sleep_timer_service.dart';
|
||||||
@@ -744,7 +745,7 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _importCustomShader(ShaderProvider shaderProvider) async {
|
Future<void> _importCustomShader(ShaderProvider shaderProvider) async {
|
||||||
final result = await FilePicker.platform.pickFiles(
|
final result = await FilePickerService.instance.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
allowedExtensions: ['glsl'],
|
allowedExtensions: ['glsl'],
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user