diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index 4274cd53af5..8fa2b0d72ff 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.7.0 +* Adds `canCreateDirectories` parameter to `FileDialogOptions` to control whether directory creation is enabled during path selection. * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. ## 2.6.2 diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index d2c3acbd9ca..97c59688168 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -96,8 +96,7 @@ abstract class FileSelectorPlatform extends PlatformInterface { /// Opens a file dialog for loading directories and returns a directory path. /// /// Returns `null` if the user cancels the operation. - // TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to - // duplicate this to add a parameter. + @Deprecated('Use getDirectoryPathWithOptions instead') Future getDirectoryPath({ String? initialDirectory, String? confirmButtonText, @@ -105,16 +104,42 @@ abstract class FileSelectorPlatform extends PlatformInterface { throw UnimplementedError('getDirectoryPath() has not been implemented.'); } + /// Opens a file dialog for loading directories and returns a directory path. + /// + /// The `options` argument controls additional settings that can be passed to + /// file dialog. See [FileDialogOptions] for more details. + /// + /// Returns `null` if the user cancels the operation. + Future getDirectoryPathWithOptions(FileDialogOptions options) { + return getDirectoryPath( + initialDirectory: options.initialDirectory, + confirmButtonText: options.confirmButtonText, + ); + } + /// Opens a file dialog for loading directories and returns multiple directory /// paths. /// /// Returns an empty list if the user cancels the operation. - // TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to - // duplicate this to add a parameter. + @Deprecated('Use getDirectoryPathsWithOptions instead') Future> getDirectoryPaths({ String? initialDirectory, String? confirmButtonText, }) { throw UnimplementedError('getDirectoryPaths() has not been implemented.'); } + + /// Opens a file dialog for loading directories and returns multiple directory + /// paths. + /// + /// The `options` argument controls additional settings that can be passed to + /// the file dialog. See [FileDialogOptions] for more details. + /// + /// Returns an empty list if the user cancels the operation. + Future> getDirectoryPathsWithOptions(FileDialogOptions options) { + return getDirectoryPaths( + initialDirectory: options.initialDirectory, + confirmButtonText: options.confirmButtonText, + ); + } } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart index 324b54ff769..8833b62e553 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart @@ -8,13 +8,24 @@ import 'package:flutter/foundation.dart' show immutable; @immutable class FileDialogOptions { /// Creates a new options set with the given settings. - const FileDialogOptions({this.initialDirectory, this.confirmButtonText}); + const FileDialogOptions({ + this.initialDirectory, + this.confirmButtonText, + this.canCreateDirectories, + }); /// The initial directory the dialog should open with. final String? initialDirectory; /// The label for the button that confirms selection. final String? confirmButtonText; + + /// Whether the user is allowed to create new directories in the dialog. + /// + /// If null, the platform will decide the default value. + /// + /// May not be supported on all platforms. + final bool? canCreateDirectories; } /// Configuration options for a save dialog. @@ -24,6 +35,7 @@ class SaveDialogOptions extends FileDialogOptions { const SaveDialogOptions({ super.initialDirectory, super.confirmButtonText, + super.canCreateDirectories, this.suggestedName, }); diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index b52bcc0944b..d68d95826d0 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.6.2 +version: 2.7.0 environment: sdk: ^3.7.0 diff --git a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart index c902e86a96a..541897b81ce 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart @@ -20,6 +20,30 @@ void main() { }); }); + group('getDirectoryPath', () { + test('Should throw unimplemented exception', () async { + final FileSelectorPlatform fileSelector = ExtendsFileSelectorPlatform(); + + await expectLater(() async { + return fileSelector.getDirectoryPath(); + }, throwsA(isA())); + }); + }); + + group('getDirectoryPathWithOptions', () { + test('Should fall back to getDirectoryPath by default', () async { + final FileSelectorPlatform fileSelector = + OldFileSelectorPlatformImplementation(); + + final String? result = await fileSelector.getDirectoryPathWithOptions( + const FileDialogOptions(), + ); + + // Should call the old method and return its result + expect(result, OldFileSelectorPlatformImplementation.directoryPath); + }); + }); + group('getDirectoryPaths', () { test('Should throw unimplemented exception', () async { final FileSelectorPlatform fileSelector = ExtendsFileSelectorPlatform(); @@ -30,6 +54,21 @@ void main() { }); }); + group('getDirectoryPathsWithOptions', () { + test('Should fall back to getDirectoryPaths by default', () async { + final FileSelectorPlatform fileSelector = + OldFileSelectorPlatformImplementation(); + + final List result = await fileSelector + .getDirectoryPathsWithOptions(const FileDialogOptions()); + + // Should call the old method and return its result + expect(result, [ + OldFileSelectorPlatformImplementation.directoryPath, + ]); + }); + }); + test('getSaveLocation falls back to getSavePath by default', () async { final FileSelectorPlatform fileSelector = OldFileSelectorPlatformImplementation(); @@ -45,7 +84,8 @@ class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} class OldFileSelectorPlatformImplementation extends FileSelectorPlatform { static const String savePath = '/a/path'; - // Only implement the deprecated getSavePath. + static const String directoryPath = '/a/directory'; + @override Future getSavePath({ List? acceptedTypeGroups, @@ -55,4 +95,20 @@ class OldFileSelectorPlatformImplementation extends FileSelectorPlatform { }) async { return savePath; } + + @override + Future getDirectoryPath({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return directoryPath; + } + + @override + Future> getDirectoryPaths({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return [directoryPath]; + } }