Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e15f5e3
Add new file_selector param canCreateDirectories
danferreira Sep 6, 2025
f3400ee
Bump version
danferreira Sep 6, 2025
92c4498
Apply format
danferreira Sep 6, 2025
c6e44da
Fix tests
danferreira Sep 6, 2025
3ef6eb5
Implement platform interface
danferreira Sep 6, 2025
e808fa4
fix: pass the correct parameter to hostapi
danferreira Sep 6, 2025
8f3f9d6
tests: fix linux tests
danferreira Sep 6, 2025
3f5954d
fix: changelog and docs
danferreira Sep 10, 2025
8dc9a13
fix: undo changes on method channel
danferreira Sep 10, 2025
29b9948
fix: tests
danferreira Sep 11, 2025
788fcfb
adds createDirectories param to getSaveLocation
danferreira Sep 11, 2025
2e294ea
Fix file_selector_windows changelog
danferreira Sep 11, 2025
95a8675
update runnerTests
danferreira Sep 11, 2025
397d15f
add parameter to getSaveLocation
danferreira Sep 11, 2025
59aaeab
Fix cr requests
danferreira Oct 22, 2025
0e6ec65
Fix changelog for platform interface
danferreira Oct 22, 2025
2c09410
Merge branch 'main' into file_selector-can-create-directories-param
danferreira Nov 10, 2025
bdc5ccd
Merge platform interface changes
danferreira Nov 10, 2025
148c25b
Undo deps versions update
danferreira Nov 10, 2025
4b67113
Fix tests to cover new api
danferreira Nov 10, 2025
9f5de16
Merge branch 'main' into file_selector-can-create-directories-param
danferreira Nov 16, 2025
6a723c4
Merge branch 'main' into file_selector-can-create-directories-param
danferreira Nov 21, 2025
ac6b60d
Resolve conflicts with main branch
danferreira Nov 21, 2025
8d69a60
Update macos and linux versions
danferreira Nov 21, 2025
779c818
Update min SDK versions to match dependencies
stuartmorgan-g Nov 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/file_selector/file_selector/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
43898F02A8C2312B24AEE41B /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
34 changes: 28 additions & 6 deletions packages/file_selector/file_selector/lib/file_selector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,25 @@ Future<List<XFile>> 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<FileSaveLocation?> getSaveLocation({
List<XTypeGroup> acceptedTypeGroups = const <XTypeGroup>[],
String? initialDirectory,
String? suggestedName,
String? confirmButtonText,
bool? canCreateDirectories,
}) async {
return FileSelectorPlatform.instance.getSaveLocation(
acceptedTypeGroups: acceptedTypeGroups,
options: SaveDialogOptions(
initialDirectory: initialDirectory,
suggestedName: suggestedName,
confirmButtonText: confirmButtonText,
canCreateDirectories: canCreateDirectories,
),
);
}
Expand All @@ -121,14 +127,22 @@ Future<FileSaveLocation?> 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<String?> 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,
),
);
}

Expand All @@ -144,13 +158,21 @@ Future<String?> 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<List<String?>> 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,
),
);
}
12 changes: 6 additions & 6 deletions packages/file_selector/file_selector/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<XTypeGroup> acceptedTypeGroups = <XTypeGroup>[
XTypeGroup(
label: 'documents',
Expand Down Expand Up @@ -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(<String>[expectedSavePath]);

final FileSaveLocation? location = await getSaveLocation(
canCreateDirectories: canCreateDirectories,
);
expect(location?.path, expectedSavePath);
});
});

group('getDirectoryPath', () {
Expand Down Expand Up @@ -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(<String>[expectedDirectoryPath]);

final String? directoryPath = await getDirectoryPath(
canCreateDirectories: canCreateDirectories,
);
expect(directoryPath, expectedDirectoryPath);
});
});

group('getDirectoryPaths', () {
Expand Down Expand Up @@ -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<String?> directoryPaths = await getDirectoryPaths(
canCreateDirectories: canCreateDirectories,
);
expect(directoryPaths, expectedDirectoryPaths);
});
});
}

Expand All @@ -341,6 +377,7 @@ class FakeFileSelector extends Fake
String? initialDirectory;
String? confirmButtonText;
String? suggestedName;
bool? canCreateDirectories;
// Return values.
List<XFile>? files;
List<String>? paths;
Expand All @@ -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
Expand Down Expand Up @@ -435,19 +474,41 @@ class FakeFileSelector extends Fake
Future<String?> 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<String?> getDirectoryPathWithOptions(FileDialogOptions options) async {
expect(options.initialDirectory, initialDirectory);
expect(options.confirmButtonText, confirmButtonText);
expect(options.canCreateDirectories, canCreateDirectories);
return paths?[0];
}

@override
Future<List<String>> 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<List<String>> getDirectoryPathsWithOptions(
FileDialogOptions options,
) async {
expect(options.initialDirectory, initialDirectory);
expect(options.confirmButtonText, confirmButtonText);
expect(options.canCreateDirectories, canCreateDirectories);
return paths!;
}
}