diff --git a/example/lib/src/file_picker_demo.dart b/example/lib/src/file_picker_demo.dart index 85d5c608c..c9ec7006c 100644 --- a/example/lib/src/file_picker_demo.dart +++ b/example/lib/src/file_picker_demo.dart @@ -35,6 +35,7 @@ class _FilePickerDemoState extends State { bool _lockParentWindow = false; bool _userAborted = false; bool _multiPick = false; + bool _orderedSelection = true; bool _safPersist = false; bool _safReadWrite = false; bool _supportsSafOptions = false; @@ -111,7 +112,10 @@ class _FilePickerDemoState extends State { lockParentWindow: _lockParentWindow, acceptLabel: _acceptLabelFromInput(), ), - darwinOptions: DarwinOptions(acceptLabel: _acceptLabelFromInput()), + darwinOptions: DarwinOptions( + acceptLabel: _acceptLabelFromInput(), + orderedSelection: _orderedSelection, + ), androidOptions: _androidSafOptionsFromFlags() ?? const AndroidOptions(), ); @@ -131,7 +135,10 @@ class _FilePickerDemoState extends State { lockParentWindow: _lockParentWindow, acceptLabel: _acceptLabelFromInput(), ), - darwinOptions: DarwinOptions(acceptLabel: _acceptLabelFromInput()), + darwinOptions: DarwinOptions( + acceptLabel: _acceptLabelFromInput(), + orderedSelection: _orderedSelection, + ), androidOptions: _androidSafOptionsFromFlags() ?? const AndroidOptions(), ); @@ -638,6 +645,17 @@ class _FilePickerDemoState extends State { value: _multiPick, ), ), + ConstrainedBox( + constraints: const BoxConstraints.tightFor(width: 400.0), + child: SwitchListTile.adaptive( + title: const Text( + 'Ordered selection (iOS photo library)', + textAlign: TextAlign.left, + ), + onChanged: (value) => setState(() => _orderedSelection = value), + value: _orderedSelection, + ), + ), ConstrainedBox( constraints: const BoxConstraints.tightFor(width: 400.0), child: SwitchListTile.adaptive( diff --git a/packages/file_picker/README.md b/packages/file_picker/README.md index e54054e37..c2158a5ef 100644 --- a/packages/file_picker/README.md +++ b/packages/file_picker/README.md @@ -163,6 +163,22 @@ compatible representation. Non-automatic modes require `compressionQuality: 0` and only affect media selected from the iOS photo library. +#### iOS photo-library swipe to select + +```dart +List files = await FilePicker.pickFiles( + type: FileType.media, + darwinOptions: const DarwinOptions(orderedSelection: false), +); +``` + +By default the iOS photo library picker numbers the selected media and returns +it in the order the user selected it, which disables selecting several items +by swiping across the grid. Set `orderedSelection: false` to restore swipe to +select when the selection order does not matter. It only applies to +`FileType.image`, `FileType.video` and `FileType.media`, the types that use the +photo library picker. + #### Confirm button text ```dart diff --git a/packages/file_picker_darwin/CHANGELOG.md b/packages/file_picker_darwin/CHANGELOG.md index 73ca4543c..681675af2 100644 --- a/packages/file_picker_darwin/CHANGELOG.md +++ b/packages/file_picker_darwin/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.0 + +- Added support for `DarwinOptions.orderedSelection`. Setting it to `false` restores swipe to select in the iOS photo library picker, which ordered selection disables. [#2241](https://github.com/vicajilau/flutter_file_picker/issues/2241) + ## 2.1.4 - Fixed a partial swipe down on the iOS picker sheet completing the pick as cancelled while the picker stayed open, which dropped the files selected afterwards. [#2243](https://github.com/vicajilau/flutter_file_picker/issues/2243) diff --git a/packages/file_picker_darwin/darwin/file_picker_darwin/Sources/file_picker_darwin/IOSFilePickerHandler.swift b/packages/file_picker_darwin/darwin/file_picker_darwin/Sources/file_picker_darwin/IOSFilePickerHandler.swift index 738c283af..6bc9a2b79 100644 --- a/packages/file_picker_darwin/darwin/file_picker_darwin/Sources/file_picker_darwin/IOSFilePickerHandler.swift +++ b/packages/file_picker_darwin/darwin/file_picker_darwin/Sources/file_picker_darwin/IOSFilePickerHandler.swift @@ -18,6 +18,7 @@ final class IOSFilePickerHandler: NSObject, private var eventSink: FlutterEventSink? private var allowMultipleSelection = false private var assetRepresentationMode = PHPickerConfiguration.AssetRepresentationMode.automatic + private var orderedSelection = true private var isDirectoryPicker = false private var isFileAndDirectoryPicker = false private var isSaveFile = false @@ -68,6 +69,7 @@ final class IOSFilePickerHandler: NSObject, (arguments["allowMultipleSelection"] as? Bool) ?? false assetRepresentationMode = resolveAssetRepresentationMode( arguments["assetRepresentationMode"] as? String) + orderedSelection = (arguments["orderedSelection"] as? Bool) ?? true switch call.method { case "any": @@ -267,7 +269,9 @@ final class IOSFilePickerHandler: NSObject, var configuration = PHPickerConfiguration(photoLibrary: .shared()) configuration.preferredAssetRepresentationMode = assetRepresentationMode configuration.selectionLimit = allowsMultipleSelection ? 0 : 1 - if #available(iOS 15.0, *) { + // Ordered selection keeps the results in the order the user picked + // them, but disables swipe to select, so apps can opt out. + if orderedSelection, #available(iOS 15.0, *) { configuration.selection = .ordered } diff --git a/packages/file_picker_darwin/lib/src/file_picker_darwin.dart b/packages/file_picker_darwin/lib/src/file_picker_darwin.dart index dda723afc..b6c5e871a 100644 --- a/packages/file_picker_darwin/lib/src/file_picker_darwin.dart +++ b/packages/file_picker_darwin/lib/src/file_picker_darwin.dart @@ -118,6 +118,7 @@ class FilePickerDarwin extends FilePickerPlatform { 'assetRepresentationMode': darwinOptions.assetRepresentationMode.name, 'acceptLabel': darwinOptions.acceptLabel, + 'orderedSelection': darwinOptions.orderedSelection, 'dialogTitle': dialogTitle, 'initialDirectory': initialDirectory, }); diff --git a/packages/file_picker_darwin/pubspec.yaml b/packages/file_picker_darwin/pubspec.yaml index b7bedad3d..78367df4b 100644 --- a/packages/file_picker_darwin/pubspec.yaml +++ b/packages/file_picker_darwin/pubspec.yaml @@ -1,6 +1,6 @@ name: file_picker_darwin description: Darwin (iOS and macOS) implementation of the file_picker plugin, supporting native file picking, saving, and directory selection. -version: 2.1.4 +version: 2.2.0 homepage: https://github.com/vicajilau/flutter_file_picker/tree/main/packages/file_picker_darwin repository: https://github.com/vicajilau/flutter_file_picker/tree/main/packages/file_picker_darwin topics: @@ -31,7 +31,7 @@ flutter: dependencies: flutter: sdk: flutter - file_picker_platform_interface: ^4.0.0 + file_picker_platform_interface: ^4.1.0 cross_file: ^0.3.5+4 path: ^1.9.0 diff --git a/packages/file_picker_darwin/test/file_picker_darwin_test.dart b/packages/file_picker_darwin/test/file_picker_darwin_test.dart index 6651ab274..c8bfd37f1 100644 --- a/packages/file_picker_darwin/test/file_picker_darwin_test.dart +++ b/packages/file_picker_darwin/test/file_picker_darwin_test.dart @@ -115,6 +115,40 @@ void main() { expect(receivedLabel, isNull); }); + test('pickFile and pickFiles send orderedSelection', () async { + final picker = FilePickerDarwin(); + bool? orderedSelection; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(picker.methodChannel, (call) async { + orderedSelection = + (call.arguments as Map)['orderedSelection'] as bool?; + return >[]; + }); + addTearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(picker.methodChannel, null); + }); + + await picker.pickFiles(type: FileType.media); + expect(orderedSelection, isTrue); + + await picker.pickFiles( + type: FileType.media, + darwinOptions: const DarwinOptions(orderedSelection: false), + ); + expect(orderedSelection, isFalse); + + await picker.pickFile(type: FileType.media); + expect(orderedSelection, isTrue); + + await picker.pickFile( + type: FileType.media, + darwinOptions: const DarwinOptions(orderedSelection: false), + ); + expect(orderedSelection, isFalse); + }); + test( 'pickFile and pickFiles send dialogTitle and initialDirectory', () async { diff --git a/packages/file_picker_platform_interface/CHANGELOG.md b/packages/file_picker_platform_interface/CHANGELOG.md index e81d9275d..a6fd9a3d2 100644 --- a/packages/file_picker_platform_interface/CHANGELOG.md +++ b/packages/file_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.1.0 + +- Added `DarwinOptions.orderedSelection`, `true` by default. Set it to `false` to restore swipe to select in the iOS photo library picker. [#2241](https://github.com/vicajilau/flutter_file_picker/issues/2241) + ## 4.0.0 - **BREAKING CHANGE**: `PlatformFile.length()` now returns `Future` instead of `Future`, matching `lengthSync()`. `null` means the length could not be determined (e.g. a failed disk read), distinct from a genuinely empty file, which still returns `0`. Previously both cases returned `0`, with no way to tell them apart. [#2197](https://github.com/vicajilau/flutter_file_picker/issues/2197) diff --git a/packages/file_picker_platform_interface/lib/src/file_picker_options/darwin_options.dart b/packages/file_picker_platform_interface/lib/src/file_picker_options/darwin_options.dart index fe7c509e5..d1afa0dca 100644 --- a/packages/file_picker_platform_interface/lib/src/file_picker_options/darwin_options.dart +++ b/packages/file_picker_platform_interface/lib/src/file_picker_options/darwin_options.dart @@ -16,6 +16,7 @@ class DarwinOptions { const DarwinOptions({ this.assetRepresentationMode = DarwinAssetRepresentationMode.automatic, this.acceptLabel, + this.orderedSelection = true, }); /// The preferred representation for media selected from the iOS photo @@ -33,6 +34,20 @@ class DarwinOptions { /// equivalent there. final String? acceptLabel; + /// Whether the iOS photo library picker numbers selected media and returns + /// it in the order the user selected it. + /// + /// Ordered selection disables selecting several items by swiping across + /// the grid, so users have to tap each one. Set it to `false` to restore + /// swipe to select when the selection order does not matter, in which case + /// the order of the results is not guaranteed to follow the selection. + /// + /// Only applies to `FileType.image`, `FileType.video` and `FileType.media`, + /// which use the photo library picker. Other types use the Files picker, + /// which has no ordered selection. Requires iOS 15 or newer and has no + /// effect on macOS. + final bool orderedSelection; + /// Throws an [ArgumentError] if this configuration conflicts with /// [compressionQuality]. /// diff --git a/packages/file_picker_platform_interface/pubspec.yaml b/packages/file_picker_platform_interface/pubspec.yaml index 3a8a03185..b31ac2de3 100644 --- a/packages/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_picker_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: file_picker_platform_interface description: A common platform interface for the file_picker plugin, defining the shared API surface and data contracts across platforms. -version: 4.0.0 +version: 4.1.0 homepage: https://github.com/vicajilau/flutter_file_picker/tree/main/packages/file_picker_platform_interface repository: https://github.com/vicajilau/flutter_file_picker/tree/main/packages/file_picker_platform_interface topics: diff --git a/packages/file_picker_platform_interface/test/darwin_options_test.dart b/packages/file_picker_platform_interface/test/darwin_options_test.dart index 9ac5b4f63..1997423d5 100644 --- a/packages/file_picker_platform_interface/test/darwin_options_test.dart +++ b/packages/file_picker_platform_interface/test/darwin_options_test.dart @@ -38,4 +38,17 @@ void main() { expect(options.acceptLabel, equals('Choose')); }); }); + + group('DarwinOptions.orderedSelection', () { + test('defaults to true', () { + expect(const DarwinOptions().orderedSelection, isTrue); + }); + + test('can be disabled', () { + expect( + const DarwinOptions(orderedSelection: false).orderedSelection, + isFalse, + ); + }); + }); }