diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 7983aa57561f..f1e6a406ef3d 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.9.2+2 +* Adds `getDirectoryPaths` method. + +## 0.9.2+1 + * Improves API docs and examples. * Changes XTypeGroup initialization from final to const. * Updates minimum Flutter version to 2.10. diff --git a/packages/file_selector/file_selector/example/lib/get_multiple_directories_page.dart b/packages/file_selector/file_selector/example/lib/get_multiple_directories_page.dart new file mode 100644 index 000000000000..6a450ca1dfb2 --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/get_multiple_directories_page.dart @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select one or more directories using `getDirectoryPaths`, +/// then displays the selected directories in a dialog. +class GetMultipleDirectoriesPage extends StatelessWidget { + /// Default Constructor + const GetMultipleDirectoriesPage({Key? key}) : super(key: key); + + Future _getDirectoryPaths(BuildContext context) async { + const String confirmButtonText = 'Choose'; + final List? directoryPaths = await getDirectoryPaths( + confirmButtonText: confirmButtonText, + ); + if (directoryPaths == null) { + // Operation was canceled by the user. + return; + } + String paths = ''; + for (final String? path in directoryPaths) { + paths += '${path!} \n'; + } + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(paths), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Select multiple directories'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + child: const Text( + 'Press to ask user to choose multiple directories'), + onPressed: () => _getDirectoryPaths(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Creates a `TextDisplay`. + const TextDisplay(this.directoriesPaths, {Key? key}) : super(key: key); + + /// The path selected in the dialog. + final String directoriesPaths; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Selected Directories'), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(directoriesPaths), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index 7b4582c5f5e3..907de4d783de 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -55,6 +55,13 @@ class HomePage extends StatelessWidget { child: const Text('Open a get directory dialog'), onPressed: () => Navigator.pushNamed(context, '/directory'), ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open a get multi directories dialog'), + onPressed: () => + Navigator.pushNamed(context, '/multi-directories'), + ), ], ), ), diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index a15842a1191c..5a9ebe84f478 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'get_directory_page.dart'; +import 'get_multiple_directories_page.dart'; import 'home_page.dart'; import 'open_image_page.dart'; import 'open_multiple_images_page.dart'; @@ -36,6 +37,8 @@ class MyApp extends StatelessWidget { '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), '/directory': (BuildContext context) => GetDirectoryPage(), + '/multi-directories': (BuildContext context) => + const GetMultipleDirectoriesPage() }, ); } diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index f357af07321a..e8b0751151d9 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -123,3 +123,22 @@ Future getDirectoryPath({ return FileSelectorPlatform.instance.getDirectoryPath( initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); } + +/// Opens a directory selection dialog and returns a list of the paths chosen by the user. +/// This always returns `null` on the web. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Open"). +/// +/// Returns `null` if the user cancels the operation. +Future?> getDirectoryPaths({ + String? initialDirectory, + String? confirmButtonText, +}) async { + return FileSelectorPlatform.instance.getDirectoryPaths( + initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); +} diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index ad187d6f446a..b0936f9f15b8 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -4,6 +4,8 @@ description: Flutter plugin for opening and saving files, or selecting repository: https://github.com/flutter/plugins/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: 0.9.2+2 +# TODO(eugerossetto): This should be reverted once file_selector_platform_interface 2.3.0 is published. +publish_to: 'none' environment: sdk: ">=2.12.0 <3.0.0" @@ -23,13 +25,20 @@ flutter: windows: default_package: file_selector_windows +# TODO(eugerossetto): This should be reverted once file_selector_platform_interface 2.3.0 is published. dependencies: - file_selector_ios: ^0.5.0 - file_selector_linux: ^0.9.0 - file_selector_macos: ^0.9.0 - file_selector_platform_interface: ^2.2.0 - file_selector_web: ^0.9.0 - file_selector_windows: ^0.9.0 + file_selector_ios: + path: ../file_selector_ios + file_selector_linux: + path: ../file_selector_linux + file_selector_macos: + path: ../file_selector_macos + file_selector_platform_interface: + path: ../file_selector_platform_interface + file_selector_web: + path: ../file_selector_web + file_selector_windows: + path: ../file_selector_windows flutter: sdk: 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 13c986b09922..3e2d558c9f9c 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -258,6 +258,55 @@ void main() { expect(directoryPath, expectedDirectoryPath); }); }); + + group('getDirectoryPaths', () { + const List expectedDirectoryPaths = [ + '/example/path', + '/example/2/path' + ]; + + test('works', () async { + fakePlatformImplementation + ..setExpectations( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText) + ..setPathsResponse(expectedDirectoryPaths); + + final List? directoryPaths = await getDirectoryPaths( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + ); + + expect(directoryPaths, expectedDirectoryPaths); + }); + + test('works with no arguments', () async { + fakePlatformImplementation.setPathsResponse(expectedDirectoryPaths); + + final List? directoryPaths = await getDirectoryPaths(); + expect(directoryPaths, expectedDirectoryPaths); + }); + + test('sets the initial directory', () async { + fakePlatformImplementation + ..setExpectations(initialDirectory: initialDirectory) + ..setPathsResponse(expectedDirectoryPaths); + + final List? directoryPaths = + await getDirectoryPaths(initialDirectory: initialDirectory); + expect(directoryPaths, expectedDirectoryPaths); + }); + + test('sets the button confirmation label', () async { + fakePlatformImplementation + ..setExpectations(confirmButtonText: confirmButtonText) + ..setPathsResponse(expectedDirectoryPaths); + + final List? directoryPaths = + await getDirectoryPaths(confirmButtonText: confirmButtonText); + expect(directoryPaths, expectedDirectoryPaths); + }); + }); } class FakeFileSelector extends Fake @@ -271,6 +320,7 @@ class FakeFileSelector extends Fake // Return values. List? files; String? path; + List? paths; void setExpectations({ List acceptedTypeGroups = const [], @@ -294,6 +344,11 @@ class FakeFileSelector extends Fake this.path = path; } + // ignore: use_setters_to_change_properties + void setPathsResponse(List paths) { + this.paths = paths; + } + @override Future openFile({ List? acceptedTypeGroups, @@ -341,4 +396,14 @@ class FakeFileSelector extends Fake expect(confirmButtonText, this.confirmButtonText); return path; } + + @override + Future?> getDirectoryPaths({ + String? initialDirectory, + String? confirmButtonText, + }) async { + expect(initialDirectory, this.initialDirectory); + expect(confirmButtonText, this.confirmButtonText); + return paths; + } } diff --git a/packages/file_selector/file_selector_ios/example/pubspec.yaml b/packages/file_selector/file_selector_ios/example/pubspec.yaml index 5a2eaa6f7dcd..0624ea6b8f61 100644 --- a/packages/file_selector/file_selector_ios/example/pubspec.yaml +++ b/packages/file_selector/file_selector_ios/example/pubspec.yaml @@ -17,7 +17,9 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: .. - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../../file_selector_platform_interface flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_ios/pubspec.yaml b/packages/file_selector/file_selector_ios/pubspec.yaml index 3f8ecfac04ce..e94972abd0c9 100644 --- a/packages/file_selector/file_selector_ios/pubspec.yaml +++ b/packages/file_selector/file_selector_ios/pubspec.yaml @@ -3,6 +3,8 @@ description: iOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 version: 0.5.0+2 +# TODO(eugerossetto): remove this once file_selector_platform_interface version is updated to 2.4.0 +publish_to: 'none' environment: sdk: ">=2.14.4 <3.0.0" @@ -17,7 +19,9 @@ flutter: pluginClass: FFSFileSelectorPlugin dependencies: - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../file_selector_platform_interface flutter: sdk: flutter @@ -27,4 +31,4 @@ dev_dependencies: sdk: flutter mockito: ^5.1.0 pigeon: ^3.2.5 - + diff --git a/packages/file_selector/file_selector_linux/example/pubspec.yaml b/packages/file_selector/file_selector_linux/example/pubspec.yaml index 51bdb28717aa..c1ff0d274305 100644 --- a/packages/file_selector/file_selector_linux/example/pubspec.yaml +++ b/packages/file_selector/file_selector_linux/example/pubspec.yaml @@ -9,7 +9,9 @@ environment: dependencies: file_selector_linux: path: ../ - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../../file_selector_platform_interface flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_linux/pubspec.yaml b/packages/file_selector/file_selector_linux/pubspec.yaml index a8aea37d72e2..24d638fac6ca 100644 --- a/packages/file_selector/file_selector_linux/pubspec.yaml +++ b/packages/file_selector/file_selector_linux/pubspec.yaml @@ -3,6 +3,8 @@ description: Liunx implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 version: 0.9.0+1 +# TODO(eugerossetto): remove this once file_selector_platform_interface version is updated to 2.4.0 +publish_to: 'none' environment: sdk: ">=2.12.0 <3.0.0" @@ -18,7 +20,9 @@ flutter: dependencies: cross_file: ^0.3.1 - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../file_selector_platform_interface flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_macos/example/pubspec.yaml b/packages/file_selector/file_selector_macos/example/pubspec.yaml index d3f3114bb481..d7cbc126fb0f 100644 --- a/packages/file_selector/file_selector_macos/example/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/example/pubspec.yaml @@ -15,7 +15,9 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: .. - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../../file_selector_platform_interface flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 3fc3832d7280..f8022c215011 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -3,6 +3,8 @@ description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 version: 0.9.0+3 +# TODO(eugerossetto): remove this once file_selector_platform_interface version is updated to 2.4.0 +publish_to: 'none' environment: sdk: ">=2.12.0 <3.0.0" @@ -18,7 +20,9 @@ flutter: dependencies: cross_file: ^0.3.1 - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../file_selector_platform_interface flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index ad803fb12e66..e0b08f086977 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.0 + +* Adds `getDirectoryPaths` method to the interface. + ## 2.3.0 * Replaces `macUTIs` with `uniformTypeIdentifiers`. `macUTIs` is available as an alias, but will be deprecated in a future release. diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index d6aebd01730f..51fd010386ea 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -93,4 +93,18 @@ class MethodChannelFileSelector extends FileSelectorPlatform { }, ); } + + /// Gets a list of directories paths from a dialog + @override + Future?> getDirectoryPaths( + {String? initialDirectory, String? confirmButtonText}) async { + return _channel.invokeListMethod( + 'getDirectoryPaths', + { + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + 'multiple': true, + }, + ); + } } 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 eb4563c47917..a277b62b0877 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 @@ -74,4 +74,13 @@ abstract class FileSelectorPlatform extends PlatformInterface { }) { throw UnimplementedError('getDirectoryPath() has not been implemented.'); } + + /// Open file dialog for loading directories and return multiple directories paths + /// Returns `null` if user cancels the operation. + Future?> getDirectoryPaths({ + String? initialDirectory, + String? confirmButtonText, + }) { + throw UnimplementedError('getDirectoryPaths() has not been implemented.'); + } } diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index ac8727c09e36..4ab63acbf7e6 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/plugins/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.3.0 +version: 2.4.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart index 0f5f3a17ae0c..40938fa9967f 100644 --- a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart @@ -247,6 +247,37 @@ void main() { ); }); }); + group('#getDirectoryPaths', () { + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPaths( + initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getDirectoryPaths', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': true + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPaths(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getDirectoryPaths', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': true + }), + ], + ); + }); + }); }); }); } diff --git a/packages/file_selector/file_selector_web/example/pubspec.yaml b/packages/file_selector/file_selector_web/example/pubspec.yaml index e14f5c2eedea..af9dfc30247c 100644 --- a/packages/file_selector/file_selector_web/example/pubspec.yaml +++ b/packages/file_selector/file_selector_web/example/pubspec.yaml @@ -6,7 +6,9 @@ environment: flutter: ">=2.10.0" dependencies: - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../../file_selector_platform_interface file_selector_web: path: ../ flutter: diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 848a41b754af..fd6b7d5cd41a 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -3,6 +3,8 @@ description: Web platform implementation of file_selector repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 version: 0.9.0+2 +# TODO(eugerossetto): remove this once file_selector_platform_interface version is updated to 2.4.0 +publish_to: 'none' environment: sdk: ">=2.12.0 <3.0.0" @@ -17,7 +19,9 @@ flutter: fileName: file_selector_web.dart dependencies: - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../file_selector_platform_interface flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/file_selector/file_selector_windows/example/pubspec.yaml b/packages/file_selector/file_selector_windows/example/pubspec.yaml index bc886d32c896..c3379bd05ceb 100644 --- a/packages/file_selector/file_selector_windows/example/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/example/pubspec.yaml @@ -8,7 +8,9 @@ environment: flutter: ">=2.10.0" dependencies: - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../../file_selector_platform_interface file_selector_windows: # When depending on this package from a real application you should use: # file_selector_windows: ^x.y.z diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index ee0701b3fd30..8fc8e8bf1850 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -3,6 +3,8 @@ description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 version: 0.9.1+4 +# TODO(eugerossetto): remove this once file_selector_platform_interface version is updated to 2.4.0 +publish_to: 'none' environment: sdk: ">=2.12.0 <3.0.0" @@ -18,7 +20,9 @@ flutter: dependencies: cross_file: ^0.3.1 - file_selector_platform_interface: ^2.2.0 + # TODO(eugerossetto): This should be 2.4.0 once it is published. + file_selector_platform_interface: + path: ../file_selector_platform_interface flutter: sdk: flutter