Skip to content

Commit 29211e8

Browse files
authored
[file_selector] Add canCreateDirectories to FileDialogOptions (#10317)
This is the platform interface update for #9965 Part of: 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 4cec230 commit 29211e8

File tree

5 files changed

+102
-8
lines changed

5 files changed

+102
-8
lines changed

packages/file_selector/file_selector_platform_interface/CHANGELOG.md

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

3+
* Adds `canCreateDirectories` parameter to `FileDialogOptions` to control whether directory creation is enabled during path selection.
34
* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7.
45

56
## 2.6.2

packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,25 +96,50 @@ abstract class FileSelectorPlatform extends PlatformInterface {
9696
/// Opens a file dialog for loading directories and returns a directory path.
9797
///
9898
/// Returns `null` if the user cancels the operation.
99-
// TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to
100-
// duplicate this to add a parameter.
99+
@Deprecated('Use getDirectoryPathWithOptions instead')
101100
Future<String?> getDirectoryPath({
102101
String? initialDirectory,
103102
String? confirmButtonText,
104103
}) {
105104
throw UnimplementedError('getDirectoryPath() has not been implemented.');
106105
}
107106

107+
/// Opens a file dialog for loading directories and returns a directory path.
108+
///
109+
/// The `options` argument controls additional settings that can be passed to
110+
/// file dialog. See [FileDialogOptions] for more details.
111+
///
112+
/// Returns `null` if the user cancels the operation.
113+
Future<String?> getDirectoryPathWithOptions(FileDialogOptions options) {
114+
return getDirectoryPath(
115+
initialDirectory: options.initialDirectory,
116+
confirmButtonText: options.confirmButtonText,
117+
);
118+
}
119+
108120
/// Opens a file dialog for loading directories and returns multiple directory
109121
/// paths.
110122
///
111123
/// Returns an empty list if the user cancels the operation.
112-
// TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to
113-
// duplicate this to add a parameter.
124+
@Deprecated('Use getDirectoryPathsWithOptions instead')
114125
Future<List<String>> getDirectoryPaths({
115126
String? initialDirectory,
116127
String? confirmButtonText,
117128
}) {
118129
throw UnimplementedError('getDirectoryPaths() has not been implemented.');
119130
}
131+
132+
/// Opens a file dialog for loading directories and returns multiple directory
133+
/// paths.
134+
///
135+
/// The `options` argument controls additional settings that can be passed to
136+
/// the file dialog. See [FileDialogOptions] for more details.
137+
///
138+
/// Returns an empty list if the user cancels the operation.
139+
Future<List<String>> getDirectoryPathsWithOptions(FileDialogOptions options) {
140+
return getDirectoryPaths(
141+
initialDirectory: options.initialDirectory,
142+
confirmButtonText: options.confirmButtonText,
143+
);
144+
}
120145
}

packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,24 @@ import 'package:flutter/foundation.dart' show immutable;
88
@immutable
99
class FileDialogOptions {
1010
/// Creates a new options set with the given settings.
11-
const FileDialogOptions({this.initialDirectory, this.confirmButtonText});
11+
const FileDialogOptions({
12+
this.initialDirectory,
13+
this.confirmButtonText,
14+
this.canCreateDirectories,
15+
});
1216

1317
/// The initial directory the dialog should open with.
1418
final String? initialDirectory;
1519

1620
/// The label for the button that confirms selection.
1721
final String? confirmButtonText;
22+
23+
/// Whether the user is allowed to create new directories in the dialog.
24+
///
25+
/// If null, the platform will decide the default value.
26+
///
27+
/// May not be supported on all platforms.
28+
final bool? canCreateDirectories;
1829
}
1930

2031
/// Configuration options for a save dialog.
@@ -24,6 +35,7 @@ class SaveDialogOptions extends FileDialogOptions {
2435
const SaveDialogOptions({
2536
super.initialDirectory,
2637
super.confirmButtonText,
38+
super.canCreateDirectories,
2739
this.suggestedName,
2840
});
2941

packages/file_selector/file_selector_platform_interface/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/file_selector
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22
55
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
66
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
7-
version: 2.6.2
7+
version: 2.7.0
88

99
environment:
1010
sdk: ^3.7.0

packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ void main() {
2020
});
2121
});
2222

23+
group('getDirectoryPath', () {
24+
test('Should throw unimplemented exception', () async {
25+
final FileSelectorPlatform fileSelector = ExtendsFileSelectorPlatform();
26+
27+
await expectLater(() async {
28+
return fileSelector.getDirectoryPath();
29+
}, throwsA(isA<UnimplementedError>()));
30+
});
31+
});
32+
33+
group('getDirectoryPathWithOptions', () {
34+
test('Should fall back to getDirectoryPath by default', () async {
35+
final FileSelectorPlatform fileSelector =
36+
OldFileSelectorPlatformImplementation();
37+
38+
final String? result = await fileSelector.getDirectoryPathWithOptions(
39+
const FileDialogOptions(),
40+
);
41+
42+
// Should call the old method and return its result
43+
expect(result, OldFileSelectorPlatformImplementation.directoryPath);
44+
});
45+
});
46+
2347
group('getDirectoryPaths', () {
2448
test('Should throw unimplemented exception', () async {
2549
final FileSelectorPlatform fileSelector = ExtendsFileSelectorPlatform();
@@ -30,6 +54,21 @@ void main() {
3054
});
3155
});
3256

57+
group('getDirectoryPathsWithOptions', () {
58+
test('Should fall back to getDirectoryPaths by default', () async {
59+
final FileSelectorPlatform fileSelector =
60+
OldFileSelectorPlatformImplementation();
61+
62+
final List<String> result = await fileSelector
63+
.getDirectoryPathsWithOptions(const FileDialogOptions());
64+
65+
// Should call the old method and return its result
66+
expect(result, <String>[
67+
OldFileSelectorPlatformImplementation.directoryPath,
68+
]);
69+
});
70+
});
71+
3372
test('getSaveLocation falls back to getSavePath by default', () async {
3473
final FileSelectorPlatform fileSelector =
3574
OldFileSelectorPlatformImplementation();
@@ -45,7 +84,8 @@ class ExtendsFileSelectorPlatform extends FileSelectorPlatform {}
4584

4685
class OldFileSelectorPlatformImplementation extends FileSelectorPlatform {
4786
static const String savePath = '/a/path';
48-
// Only implement the deprecated getSavePath.
87+
static const String directoryPath = '/a/directory';
88+
4989
@override
5090
Future<String?> getSavePath({
5191
List<XTypeGroup>? acceptedTypeGroups,
@@ -55,4 +95,20 @@ class OldFileSelectorPlatformImplementation extends FileSelectorPlatform {
5595
}) async {
5696
return savePath;
5797
}
98+
99+
@override
100+
Future<String?> getDirectoryPath({
101+
String? initialDirectory,
102+
String? confirmButtonText,
103+
}) async {
104+
return directoryPath;
105+
}
106+
107+
@override
108+
Future<List<String>> getDirectoryPaths({
109+
String? initialDirectory,
110+
String? confirmButtonText,
111+
}) async {
112+
return <String>[directoryPath];
113+
}
58114
}

0 commit comments

Comments
 (0)