diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 189d99c9634..81bc93dc1b4 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.1.0 +* Adds `canCreateDirectories` param to `getSaveLocation`, `getDirectoryPath` and `getDirectoryPaths` to control whether users can create directories during location selection. * Updates minimum supported SDK version to Flutter 3.32/Dart 3.8. ## 1.0.4 diff --git a/packages/file_selector/file_selector/example/macos/Podfile b/packages/file_selector/file_selector/example/macos/Podfile index ae77cc1d426..66f6172bbb3 100644 --- a/packages/file_selector/file_selector/example/macos/Podfile +++ b/packages/file_selector/file_selector/example/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.14' +platform :osx, '10.15' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj index 7aa95e4ed28..36b6359c6e8 100644 --- a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj @@ -186,6 +186,7 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 43898F02A8C2312B24AEE41B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -299,6 +300,23 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + 43898F02A8C2312B24AEE41B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; A778864BDDD7B12C41D66FBB /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -395,7 +413,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -474,7 +492,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -521,7 +539,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index e4c784398ef..bba72bccc58 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -93,12 +93,17 @@ Future> openFiles({ /// [confirmButtonText] is the text in the confirmation button of the dialog. /// When not provided, the default OS label is used (for example, "Save"). /// +/// [canCreateDirectories] controls whether the user is allowed to create new +/// directories in the save dialog. When not provided, uses the platform default. +/// May not be supported on all platforms. +/// /// Returns `null` if the user cancels the operation. Future getSaveLocation({ List acceptedTypeGroups = const [], String? initialDirectory, String? suggestedName, String? confirmButtonText, + bool? canCreateDirectories, }) async { return FileSelectorPlatform.instance.getSaveLocation( acceptedTypeGroups: acceptedTypeGroups, @@ -106,6 +111,7 @@ Future getSaveLocation({ initialDirectory: initialDirectory, suggestedName: suggestedName, confirmButtonText: confirmButtonText, + canCreateDirectories: canCreateDirectories, ), ); } @@ -121,14 +127,22 @@ Future getSaveLocation({ /// [confirmButtonText] is the text in the confirmation button of the dialog. /// When not provided, the default OS label is used (for example, "Open"). /// +/// [canCreateDirectories] controls whether the user is allowed to create new +/// directories in the dialog. When not provided, uses the platform default. +/// May not be supported on all platforms. +/// /// Returns `null` if the user cancels the operation. Future getDirectoryPath({ String? initialDirectory, String? confirmButtonText, + bool? canCreateDirectories, }) async { - return FileSelectorPlatform.instance.getDirectoryPath( - initialDirectory: initialDirectory, - confirmButtonText: confirmButtonText, + return FileSelectorPlatform.instance.getDirectoryPathWithOptions( + FileDialogOptions( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + canCreateDirectories: canCreateDirectories, + ), ); } @@ -144,13 +158,21 @@ Future getDirectoryPath({ /// [confirmButtonText] is the text in the confirmation button of the dialog. /// When not provided, the default OS label is used (for example, "Open"). /// +/// [canCreateDirectories] controls whether the user is allowed to create new +/// directories in the dialog. When not provided, uses the platform default. +/// May not be supported on all platforms. +/// /// Returns an empty array if the user cancels the operation. Future> getDirectoryPaths({ String? initialDirectory, String? confirmButtonText, + bool? canCreateDirectories, }) async { - return FileSelectorPlatform.instance.getDirectoryPaths( - initialDirectory: initialDirectory, - confirmButtonText: confirmButtonText, + return FileSelectorPlatform.instance.getDirectoryPathsWithOptions( + FileDialogOptions( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + canCreateDirectories: canCreateDirectories, + ), ); } diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 5a8c457a1e8..ea5bc02928e 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,11 +3,11 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 1.0.4 +version: 1.1.0 environment: - sdk: ^3.8.0 - flutter: ">=3.32.0" + sdk: ^3.9.0 + flutter: ">=3.35.0" flutter: plugin: @@ -28,9 +28,9 @@ flutter: dependencies: file_selector_android: ^0.5.0 file_selector_ios: ^0.5.0 - file_selector_linux: ^0.9.2 - file_selector_macos: ^0.9.3 - file_selector_platform_interface: ^2.6.0 + file_selector_linux: ^0.9.4 + file_selector_macos: ^0.9.5 + file_selector_platform_interface: ^2.7.0 file_selector_web: ^0.9.1 file_selector_windows: ^0.9.3 flutter: diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 9320ceb0db2..4a0cda7983b 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -12,6 +12,7 @@ void main() { const String initialDirectory = '/home/flutteruser'; const String confirmButtonText = 'Use this profile picture'; const String suggestedName = 'suggested_name'; + const List acceptedTypeGroups = [ XTypeGroup( label: 'documents', @@ -227,6 +228,18 @@ void main() { ); expect(location?.path, expectedSavePath); }); + + test('sets the directory creation control flag', () async { + const bool canCreateDirectories = false; + fakePlatformImplementation + ..setExpectations(canCreateDirectories: canCreateDirectories) + ..setPathsResponse([expectedSavePath]); + + final FileSaveLocation? location = await getSaveLocation( + canCreateDirectories: canCreateDirectories, + ); + expect(location?.path, expectedSavePath); + }); }); group('getDirectoryPath', () { @@ -278,6 +291,18 @@ void main() { ); expect(directoryPath, expectedDirectoryPath); }); + + test('sets the directory creation control flag', () async { + const bool canCreateDirectories = true; + fakePlatformImplementation + ..setExpectations(canCreateDirectories: canCreateDirectories) + ..setPathsResponse([expectedDirectoryPath]); + + final String? directoryPath = await getDirectoryPath( + canCreateDirectories: canCreateDirectories, + ); + expect(directoryPath, expectedDirectoryPath); + }); }); group('getDirectoryPaths', () { @@ -330,6 +355,17 @@ void main() { ); expect(directoryPaths, expectedDirectoryPaths); }); + test('sets the directory creation control flag', () async { + const bool canCreateDirectories = true; + fakePlatformImplementation + ..setExpectations(canCreateDirectories: canCreateDirectories) + ..setPathsResponse(expectedDirectoryPaths); + + final List directoryPaths = await getDirectoryPaths( + canCreateDirectories: canCreateDirectories, + ); + expect(directoryPaths, expectedDirectoryPaths); + }); }); } @@ -341,6 +377,7 @@ class FakeFileSelector extends Fake String? initialDirectory; String? confirmButtonText; String? suggestedName; + bool? canCreateDirectories; // Return values. List? files; List? paths; @@ -351,11 +388,13 @@ class FakeFileSelector extends Fake String? initialDirectory, String? suggestedName, String? confirmButtonText, + bool? canCreateDirectories, }) { this.acceptedTypeGroups = acceptedTypeGroups; this.initialDirectory = initialDirectory; this.suggestedName = suggestedName; this.confirmButtonText = confirmButtonText; + this.canCreateDirectories = canCreateDirectories; } // ignore: use_setters_to_change_properties @@ -435,9 +474,19 @@ class FakeFileSelector extends Fake Future getDirectoryPath({ String? initialDirectory, String? confirmButtonText, + bool canCreateDirectories = true, }) async { expect(initialDirectory, this.initialDirectory); expect(confirmButtonText, this.confirmButtonText); + expect(canCreateDirectories, this.canCreateDirectories); + return paths?[0]; + } + + @override + Future getDirectoryPathWithOptions(FileDialogOptions options) async { + expect(options.initialDirectory, initialDirectory); + expect(options.confirmButtonText, confirmButtonText); + expect(options.canCreateDirectories, canCreateDirectories); return paths?[0]; } @@ -445,9 +494,21 @@ class FakeFileSelector extends Fake Future> getDirectoryPaths({ String? initialDirectory, String? confirmButtonText, + bool canCreateDirectories = true, }) async { expect(initialDirectory, this.initialDirectory); expect(confirmButtonText, this.confirmButtonText); + expect(canCreateDirectories, this.canCreateDirectories); + return paths!; + } + + @override + Future> getDirectoryPathsWithOptions( + FileDialogOptions options, + ) async { + expect(options.initialDirectory, initialDirectory); + expect(options.confirmButtonText, confirmButtonText); + expect(options.canCreateDirectories, canCreateDirectories); return paths!; } }