Skip to content

Commit 6a84a54

Browse files
authored
[file_selector] Add parameter to control whether the new folders button is visible in the file dialog (#9965)
Adds a new parameter `canCreateDirectories` to `FileDialogOptions` to control the visibility of the New Folder button in file dialogs on supported platforms. Currently only configurable on Linux and macOS This is the "Combined PR" Let me know if we are okay with this approach so I can open a new PR covering the interface changes Fixes: flutter/flutter#141339 ## Pre-Review Checklist **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 4bf2df4 commit 6a84a54

File tree

6 files changed

+119
-17
lines changed

6 files changed

+119
-17
lines changed

packages/file_selector/file_selector/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 1.1.0
22

3+
* Adds `canCreateDirectories` param to `getSaveLocation`, `getDirectoryPath` and `getDirectoryPaths` to control whether users can create directories during location selection.
34
* Updates minimum supported SDK version to Flutter 3.32/Dart 3.8.
45

56
## 1.0.4

packages/file_selector/file_selector/example/macos/Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
platform :osx, '10.14'
1+
platform :osx, '10.15'
22

33
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
44
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
33CC10EB2044A3C60003C045 /* Resources */,
187187
33CC110E2044A8840003C045 /* Bundle Framework */,
188188
3399D490228B24CF009A79C7 /* ShellScript */,
189+
43898F02A8C2312B24AEE41B /* [CP] Embed Pods Frameworks */,
189190
);
190191
buildRules = (
191192
);
@@ -299,6 +300,23 @@
299300
shellPath = /bin/sh;
300301
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
301302
};
303+
43898F02A8C2312B24AEE41B /* [CP] Embed Pods Frameworks */ = {
304+
isa = PBXShellScriptBuildPhase;
305+
buildActionMask = 2147483647;
306+
files = (
307+
);
308+
inputFileListPaths = (
309+
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
310+
);
311+
name = "[CP] Embed Pods Frameworks";
312+
outputFileListPaths = (
313+
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
314+
);
315+
runOnlyForDeploymentPostprocessing = 0;
316+
shellPath = /bin/sh;
317+
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
318+
showEnvVarsInLog = 0;
319+
};
302320
A778864BDDD7B12C41D66FBB /* [CP] Check Pods Manifest.lock */ = {
303321
isa = PBXShellScriptBuildPhase;
304322
buildActionMask = 2147483647;
@@ -395,7 +413,7 @@
395413
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
396414
GCC_WARN_UNUSED_FUNCTION = YES;
397415
GCC_WARN_UNUSED_VARIABLE = YES;
398-
MACOSX_DEPLOYMENT_TARGET = 10.14;
416+
MACOSX_DEPLOYMENT_TARGET = 10.15;
399417
MTL_ENABLE_DEBUG_INFO = NO;
400418
SDKROOT = macosx;
401419
SWIFT_COMPILATION_MODE = wholemodule;
@@ -474,7 +492,7 @@
474492
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
475493
GCC_WARN_UNUSED_FUNCTION = YES;
476494
GCC_WARN_UNUSED_VARIABLE = YES;
477-
MACOSX_DEPLOYMENT_TARGET = 10.14;
495+
MACOSX_DEPLOYMENT_TARGET = 10.15;
478496
MTL_ENABLE_DEBUG_INFO = YES;
479497
ONLY_ACTIVE_ARCH = YES;
480498
SDKROOT = macosx;
@@ -521,7 +539,7 @@
521539
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
522540
GCC_WARN_UNUSED_FUNCTION = YES;
523541
GCC_WARN_UNUSED_VARIABLE = YES;
524-
MACOSX_DEPLOYMENT_TARGET = 10.14;
542+
MACOSX_DEPLOYMENT_TARGET = 10.15;
525543
MTL_ENABLE_DEBUG_INFO = NO;
526544
SDKROOT = macosx;
527545
SWIFT_COMPILATION_MODE = wholemodule;

packages/file_selector/file_selector/lib/file_selector.dart

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,25 @@ Future<List<XFile>> openFiles({
9393
/// [confirmButtonText] is the text in the confirmation button of the dialog.
9494
/// When not provided, the default OS label is used (for example, "Save").
9595
///
96+
/// [canCreateDirectories] controls whether the user is allowed to create new
97+
/// directories in the save dialog. When not provided, uses the platform default.
98+
/// May not be supported on all platforms.
99+
///
96100
/// Returns `null` if the user cancels the operation.
97101
Future<FileSaveLocation?> getSaveLocation({
98102
List<XTypeGroup> acceptedTypeGroups = const <XTypeGroup>[],
99103
String? initialDirectory,
100104
String? suggestedName,
101105
String? confirmButtonText,
106+
bool? canCreateDirectories,
102107
}) async {
103108
return FileSelectorPlatform.instance.getSaveLocation(
104109
acceptedTypeGroups: acceptedTypeGroups,
105110
options: SaveDialogOptions(
106111
initialDirectory: initialDirectory,
107112
suggestedName: suggestedName,
108113
confirmButtonText: confirmButtonText,
114+
canCreateDirectories: canCreateDirectories,
109115
),
110116
);
111117
}
@@ -121,14 +127,22 @@ Future<FileSaveLocation?> getSaveLocation({
121127
/// [confirmButtonText] is the text in the confirmation button of the dialog.
122128
/// When not provided, the default OS label is used (for example, "Open").
123129
///
130+
/// [canCreateDirectories] controls whether the user is allowed to create new
131+
/// directories in the dialog. When not provided, uses the platform default.
132+
/// May not be supported on all platforms.
133+
///
124134
/// Returns `null` if the user cancels the operation.
125135
Future<String?> getDirectoryPath({
126136
String? initialDirectory,
127137
String? confirmButtonText,
138+
bool? canCreateDirectories,
128139
}) async {
129-
return FileSelectorPlatform.instance.getDirectoryPath(
130-
initialDirectory: initialDirectory,
131-
confirmButtonText: confirmButtonText,
140+
return FileSelectorPlatform.instance.getDirectoryPathWithOptions(
141+
FileDialogOptions(
142+
initialDirectory: initialDirectory,
143+
confirmButtonText: confirmButtonText,
144+
canCreateDirectories: canCreateDirectories,
145+
),
132146
);
133147
}
134148

@@ -144,13 +158,21 @@ Future<String?> getDirectoryPath({
144158
/// [confirmButtonText] is the text in the confirmation button of the dialog.
145159
/// When not provided, the default OS label is used (for example, "Open").
146160
///
161+
/// [canCreateDirectories] controls whether the user is allowed to create new
162+
/// directories in the dialog. When not provided, uses the platform default.
163+
/// May not be supported on all platforms.
164+
///
147165
/// Returns an empty array if the user cancels the operation.
148166
Future<List<String?>> getDirectoryPaths({
149167
String? initialDirectory,
150168
String? confirmButtonText,
169+
bool? canCreateDirectories,
151170
}) async {
152-
return FileSelectorPlatform.instance.getDirectoryPaths(
153-
initialDirectory: initialDirectory,
154-
confirmButtonText: confirmButtonText,
171+
return FileSelectorPlatform.instance.getDirectoryPathsWithOptions(
172+
FileDialogOptions(
173+
initialDirectory: initialDirectory,
174+
confirmButtonText: confirmButtonText,
175+
canCreateDirectories: canCreateDirectories,
176+
),
155177
);
156178
}

packages/file_selector/file_selector/pubspec.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ description: Flutter plugin for opening and saving files, or selecting
33
directories, using native file selection UI.
44
repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector
55
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22
6-
version: 1.0.4
6+
version: 1.1.0
77

88
environment:
9-
sdk: ^3.8.0
10-
flutter: ">=3.32.0"
9+
sdk: ^3.9.0
10+
flutter: ">=3.35.0"
1111

1212
flutter:
1313
plugin:
@@ -28,9 +28,9 @@ flutter:
2828
dependencies:
2929
file_selector_android: ^0.5.0
3030
file_selector_ios: ^0.5.0
31-
file_selector_linux: ^0.9.2
32-
file_selector_macos: ^0.9.3
33-
file_selector_platform_interface: ^2.6.0
31+
file_selector_linux: ^0.9.4
32+
file_selector_macos: ^0.9.5
33+
file_selector_platform_interface: ^2.7.0
3434
file_selector_web: ^0.9.1
3535
file_selector_windows: ^0.9.3
3636
flutter:

packages/file_selector/file_selector/test/file_selector_test.dart

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ void main() {
1212
const String initialDirectory = '/home/flutteruser';
1313
const String confirmButtonText = 'Use this profile picture';
1414
const String suggestedName = 'suggested_name';
15+
1516
const List<XTypeGroup> acceptedTypeGroups = <XTypeGroup>[
1617
XTypeGroup(
1718
label: 'documents',
@@ -227,6 +228,18 @@ void main() {
227228
);
228229
expect(location?.path, expectedSavePath);
229230
});
231+
232+
test('sets the directory creation control flag', () async {
233+
const bool canCreateDirectories = false;
234+
fakePlatformImplementation
235+
..setExpectations(canCreateDirectories: canCreateDirectories)
236+
..setPathsResponse(<String>[expectedSavePath]);
237+
238+
final FileSaveLocation? location = await getSaveLocation(
239+
canCreateDirectories: canCreateDirectories,
240+
);
241+
expect(location?.path, expectedSavePath);
242+
});
230243
});
231244

232245
group('getDirectoryPath', () {
@@ -278,6 +291,18 @@ void main() {
278291
);
279292
expect(directoryPath, expectedDirectoryPath);
280293
});
294+
295+
test('sets the directory creation control flag', () async {
296+
const bool canCreateDirectories = true;
297+
fakePlatformImplementation
298+
..setExpectations(canCreateDirectories: canCreateDirectories)
299+
..setPathsResponse(<String>[expectedDirectoryPath]);
300+
301+
final String? directoryPath = await getDirectoryPath(
302+
canCreateDirectories: canCreateDirectories,
303+
);
304+
expect(directoryPath, expectedDirectoryPath);
305+
});
281306
});
282307

283308
group('getDirectoryPaths', () {
@@ -330,6 +355,17 @@ void main() {
330355
);
331356
expect(directoryPaths, expectedDirectoryPaths);
332357
});
358+
test('sets the directory creation control flag', () async {
359+
const bool canCreateDirectories = true;
360+
fakePlatformImplementation
361+
..setExpectations(canCreateDirectories: canCreateDirectories)
362+
..setPathsResponse(expectedDirectoryPaths);
363+
364+
final List<String?> directoryPaths = await getDirectoryPaths(
365+
canCreateDirectories: canCreateDirectories,
366+
);
367+
expect(directoryPaths, expectedDirectoryPaths);
368+
});
333369
});
334370
}
335371

@@ -341,6 +377,7 @@ class FakeFileSelector extends Fake
341377
String? initialDirectory;
342378
String? confirmButtonText;
343379
String? suggestedName;
380+
bool? canCreateDirectories;
344381
// Return values.
345382
List<XFile>? files;
346383
List<String>? paths;
@@ -351,11 +388,13 @@ class FakeFileSelector extends Fake
351388
String? initialDirectory,
352389
String? suggestedName,
353390
String? confirmButtonText,
391+
bool? canCreateDirectories,
354392
}) {
355393
this.acceptedTypeGroups = acceptedTypeGroups;
356394
this.initialDirectory = initialDirectory;
357395
this.suggestedName = suggestedName;
358396
this.confirmButtonText = confirmButtonText;
397+
this.canCreateDirectories = canCreateDirectories;
359398
}
360399

361400
// ignore: use_setters_to_change_properties
@@ -435,19 +474,41 @@ class FakeFileSelector extends Fake
435474
Future<String?> getDirectoryPath({
436475
String? initialDirectory,
437476
String? confirmButtonText,
477+
bool canCreateDirectories = true,
438478
}) async {
439479
expect(initialDirectory, this.initialDirectory);
440480
expect(confirmButtonText, this.confirmButtonText);
481+
expect(canCreateDirectories, this.canCreateDirectories);
482+
return paths?[0];
483+
}
484+
485+
@override
486+
Future<String?> getDirectoryPathWithOptions(FileDialogOptions options) async {
487+
expect(options.initialDirectory, initialDirectory);
488+
expect(options.confirmButtonText, confirmButtonText);
489+
expect(options.canCreateDirectories, canCreateDirectories);
441490
return paths?[0];
442491
}
443492

444493
@override
445494
Future<List<String>> getDirectoryPaths({
446495
String? initialDirectory,
447496
String? confirmButtonText,
497+
bool canCreateDirectories = true,
448498
}) async {
449499
expect(initialDirectory, this.initialDirectory);
450500
expect(confirmButtonText, this.confirmButtonText);
501+
expect(canCreateDirectories, this.canCreateDirectories);
502+
return paths!;
503+
}
504+
505+
@override
506+
Future<List<String>> getDirectoryPathsWithOptions(
507+
FileDialogOptions options,
508+
) async {
509+
expect(options.initialDirectory, initialDirectory);
510+
expect(options.confirmButtonText, confirmButtonText);
511+
expect(options.canCreateDirectories, canCreateDirectories);
451512
return paths!;
452513
}
453514
}

0 commit comments

Comments
 (0)