From 9af50d46e5d118fd8bc6a7b38bae748bf7877c58 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 21 Jun 2023 16:28:07 -0400 Subject: [PATCH] [file_selector] Add file group to save return value - platform interface (#4254) Platform interface portion of https://github.com/flutter/packages/pull/4222 Part of https://github.com/flutter/flutter/issues/107093 --- .../file_selector/lib/file_selector.dart | 3 ++ .../example/lib/save_text_page.dart | 3 ++ .../example/lib/save_text_page.dart | 3 ++ .../CHANGELOG.md | 4 ++ .../file_selector_interface.dart | 49 ++++++++++++++++--- .../lib/src/types/file_dialog_options.dart | 29 +++++++++++ .../lib/src/types/file_save_location.dart | 26 ++++++++++ .../lib/src/types/types.dart | 4 +- .../{x_type_group => }/x_type_group.dart | 0 .../pubspec.yaml | 2 +- ...file_selector_platform_interface_test.dart | 28 ++++++++++- .../example/lib/save_text_page.dart | 3 ++ 12 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart create mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/file_save_location.dart rename packages/file_selector/file_selector_platform_interface/lib/src/types/{x_type_group => }/x_type_group.dart (100%) diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index c2249565c9..c75cb58233 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -98,6 +98,9 @@ Future getSavePath({ String? suggestedName, String? confirmButtonText, }) async { + // TODO(stuartmorgan): Update this to getSaveLocation in the next federated + // change PR. + // ignore: deprecated_member_use return FileSelectorPlatform.instance.getSavePath( acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, diff --git a/packages/file_selector/file_selector_linux/example/lib/save_text_page.dart b/packages/file_selector/file_selector_linux/example/lib/save_text_page.dart index 174c490c5f..2d259a62f4 100644 --- a/packages/file_selector/file_selector_linux/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/save_text_page.dart @@ -17,6 +17,9 @@ class SaveTextPage extends StatelessWidget { Future _saveFile() async { final String fileName = _nameController.text; + // TODO(stuartmorgan): Update this to getSaveLocation in the next federated + // change PR. + // ignore: deprecated_member_use final String? path = await FileSelectorPlatform.instance.getSavePath( // Operation was canceled by the user. suggestedName: fileName, diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart index 79d9c83c83..84180ac51d 100644 --- a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -17,6 +17,9 @@ class SaveTextPage extends StatelessWidget { Future _saveFile() async { final String fileName = _nameController.text; + // TODO(stuartmorgan): Update this to getSaveLocation in the next federated + // change PR. + // ignore: deprecated_member_use final String? path = await FileSelectorPlatform.instance.getSavePath( suggestedName: fileName, ); diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index ba4ed2cfe1..f33890efe5 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.6.0 + +* Adds `getSaveLocation` and deprecates `getSavePath`. + ## 2.5.1 * Adds compatibility with `http` 1.0. 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 ad4fe617e4..7fe40c72f3 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 @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../../file_selector_platform_interface.dart'; @@ -37,7 +35,10 @@ abstract class FileSelectorPlatform extends PlatformInterface { } /// Opens a file dialog for loading files and returns a file path. - /// Returns `null` if user cancels the operation. + /// + /// Returns `null` if the user cancels the operation. + // TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to + // duplicate this to add a parameter. Future openFile({ List? acceptedTypeGroups, String? initialDirectory, @@ -47,6 +48,10 @@ abstract class FileSelectorPlatform extends PlatformInterface { } /// Opens a file dialog for loading files and returns a list of file paths. + /// + /// Returns an empty list if the user cancels the operation. + // TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to + // duplicate this to add a parameter. Future> openFiles({ List? acceptedTypeGroups, String? initialDirectory, @@ -55,8 +60,13 @@ abstract class FileSelectorPlatform extends PlatformInterface { throw UnimplementedError('openFiles() has not been implemented.'); } - /// Opens a file dialog for saving files and returns a file path at which to save. - /// Returns `null` if user cancels the operation. + /// Opens a file dialog for saving files and returns a file path at which to + /// save. + /// + /// Returns `null` if the user cancels the operation. + // TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to + // duplicate this to add a parameter. + @Deprecated('Use getSaveLocation instead') Future getSavePath({ List? acceptedTypeGroups, String? initialDirectory, @@ -66,8 +76,28 @@ abstract class FileSelectorPlatform extends PlatformInterface { throw UnimplementedError('getSavePath() has not been implemented.'); } + /// Opens a file dialog for saving files and returns a file location at which + /// to save. + /// + /// Returns `null` if the user cancels the operation. + Future getSaveLocation({ + List? acceptedTypeGroups, + SaveDialogOptions options = const SaveDialogOptions(), + }) async { + final String? path = await getSavePath( + acceptedTypeGroups: acceptedTypeGroups, + initialDirectory: options.initialDirectory, + suggestedName: options.suggestedName, + confirmButtonText: options.confirmButtonText, + ); + return path == null ? null : FileSaveLocation(path); + } + /// Opens a file dialog for loading directories and returns a directory path. - /// Returns `null` if user cancels the operation. + /// + /// Returns `null` if the user cancels the operation. + // TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to + // duplicate this to add a parameter. Future getDirectoryPath({ String? initialDirectory, String? confirmButtonText, @@ -75,7 +105,12 @@ abstract class FileSelectorPlatform extends PlatformInterface { throw UnimplementedError('getDirectoryPath() has not been implemented.'); } - /// Opens a file dialog for loading directories and returns multiple directory paths. + /// Opens a file dialog for loading directories and returns multiple directory + /// paths. + /// + /// Returns an empty list if the user cancels the operation. + // TODO(stuartmorgan): Switch to FileDialogOptions if we ever need to + // duplicate this to add a parameter. Future> getDirectoryPaths({ String? initialDirectory, String? confirmButtonText, diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart new file mode 100644 index 0000000000..5797e356b2 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/file_dialog_options.dart @@ -0,0 +1,29 @@ +// 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:flutter/foundation.dart' show immutable; + +/// Configuration options for any file selector dialog. +@immutable +class FileDialogOptions { + /// Creates a new options set with the given settings. + const FileDialogOptions({this.initialDirectory, this.confirmButtonText}); + + /// The initial directory the dialog should open with. + final String? initialDirectory; + + /// The label for the button that confirms selection. + final String? confirmButtonText; +} + +/// Configuration options for a save dialog. +@immutable +class SaveDialogOptions extends FileDialogOptions { + /// Creates a new options set with the given settings. + const SaveDialogOptions( + {super.initialDirectory, super.confirmButtonText, this.suggestedName}); + + /// The suggested name of the file to save or open. + final String? suggestedName; +} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/file_save_location.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/file_save_location.dart new file mode 100644 index 0000000000..4670e33fc5 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/file_save_location.dart @@ -0,0 +1,26 @@ +// 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:flutter/foundation.dart' show immutable; + +import 'x_type_group.dart'; + +export 'x_type_group.dart'; + +/// The response from a save dialog. +@immutable +class FileSaveLocation { + /// Creates a result with the given [path] and optional other dialog state. + const FileSaveLocation(this.path, {this.activeFilter}); + + /// The path to save to. + final String path; + + /// The currently active filter group, if any. + /// + /// This is null on platforms that do not support user-selectable filter + /// groups in save dialogs (for example, macOS), or when no filter groups + /// were provided when showing the dialog. + final XTypeGroup? activeFilter; +} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart index 9caee27c3e..c23ff97a03 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart @@ -3,4 +3,6 @@ // found in the LICENSE file. export 'package:cross_file/cross_file.dart'; -export 'x_type_group/x_type_group.dart'; +export 'file_dialog_options.dart'; +export 'file_save_location.dart'; +export 'x_type_group.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group.dart similarity index 100% rename from packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group.dart diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index b7ce75d960..a947c59eb3 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/packages/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.5.1 +version: 2.6.0 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart index 18334e885f..7592c85122 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart @@ -10,7 +10,7 @@ void main() { // Store the initial instance before any tests change it. final FileSelectorPlatform initialInstance = FileSelectorPlatform.instance; - group('$FileSelectorPlatform', () { + group('FileSelectorPlatform', () { test('$MethodChannelFileSelector() is the default instance', () { expect(initialInstance, isInstanceOf()); }); @@ -20,7 +20,7 @@ void main() { }); }); - group('#GetDirectoryPaths', () { + group('getDirectoryPaths', () { test('Should throw unimplemented exception', () async { final FileSelectorPlatform fileSelector = ExtendsFileSelectorPlatform(); @@ -29,6 +29,30 @@ void main() { }, throwsA(isA())); }); }); + + test('getSaveLocation falls back to getSavePath by default', () async { + final FileSelectorPlatform fileSelector = + OldFileSelectorPlatformImplementation(); + + final FileSaveLocation? result = await fileSelector.getSaveLocation(); + + expect(result?.path, OldFileSelectorPlatformImplementation.savePath); + expect(result?.activeFilter, null); + }); } class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} + +class OldFileSelectorPlatformImplementation extends FileSelectorPlatform { + static const String savePath = '/a/path'; + // Only implement the deprecated getSavePath. + @override + Future getSavePath({ + List? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText, + }) async { + return savePath; + } +} diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart index 174c490c5f..2d259a62f4 100644 --- a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -17,6 +17,9 @@ class SaveTextPage extends StatelessWidget { Future _saveFile() async { final String fileName = _nameController.text; + // TODO(stuartmorgan): Update this to getSaveLocation in the next federated + // change PR. + // ignore: deprecated_member_use final String? path = await FileSelectorPlatform.instance.getSavePath( // Operation was canceled by the user. suggestedName: fileName,