From 8d9e2cb8ce0399c2f8dae12d36c98e9c3c9e2fd4 Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:34:22 +0200 Subject: [PATCH 1/5] Make ordered selection optional in the iOS photo picker The photo library picker always used PHPickerConfiguration.Selection.ordered, added in #2047 to keep results in selection order. Ordered selection also disables selecting several items by swiping across the grid, so users of apps that pick many photos have to tap each one. Add DarwinOptions.orderedSelection, true by default so the order keeps being guaranteed, and only apply .ordered when it is enabled. Apps that do not care about the order can set it to false to restore swipe to select. The example gets a toggle for it. Bumps file_picker_platform_interface to 4.1.0 and file_picker_darwin to 2.2.0. Fixes #2241 --- example/lib/src/file_picker_demo.dart | 22 ++++++++++++-- packages/file_picker/README.md | 14 +++++++++ packages/file_picker_darwin/CHANGELOG.md | 5 ++++ .../IOSFilePickerHandler.swift | 6 +++- .../lib/src/file_picker_darwin.dart | 1 + packages/file_picker_darwin/pubspec.yaml | 4 +-- .../test/file_picker_darwin_test.dart | 30 +++++++++++++++++++ .../CHANGELOG.md | 4 +++ .../file_picker_options/darwin_options.dart | 13 ++++++++ .../pubspec.yaml | 2 +- .../test/darwin_options_test.dart | 13 ++++++++ 11 files changed, 108 insertions(+), 6 deletions(-) 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..a051f6d9d 100644 --- a/packages/file_picker/README.md +++ b/packages/file_picker/README.md @@ -163,6 +163,20 @@ 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. + #### Confirm button text ```dart diff --git a/packages/file_picker_darwin/CHANGELOG.md b/packages/file_picker_darwin/CHANGELOG.md index 5670db10e..b98662dee 100644 --- a/packages/file_picker_darwin/CHANGELOG.md +++ b/packages/file_picker_darwin/CHANGELOG.md @@ -1,3 +1,8 @@ +## 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) +- Raised the `file_picker_platform_interface` lower bound to `^4.1.0`. + ## 2.1.3 - Fixed `saveFile()` on macOS appending a second, lowercased extension to file names with a custom extension, such as `j.nme2mbAllpreset`. [#2238](https://github.com/vicajilau/flutter_file_picker/issues/2238) 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 aaa999f97..5d78384fb 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": @@ -248,7 +250,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 e966def72..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.3 +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..84ace55e8 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,36 @@ void main() { expect(receivedLabel, isNull); }); + test('pickFile and pickFiles send orderedSelection', () async { + final picker = FilePickerDarwin(); + bool? received; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(picker.methodChannel, (call) async { + received = (call.arguments as Map)['orderedSelection'] as bool?; + return >[]; + }); + addTearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(picker.methodChannel, null); + }); + + await picker.pickFiles(type: FileType.media); + expect(received, isTrue); + + await picker.pickFiles( + type: FileType.media, + darwinOptions: const DarwinOptions(orderedSelection: false), + ); + expect(received, isFalse); + + await picker.pickFile( + type: FileType.media, + darwinOptions: const DarwinOptions(orderedSelection: false), + ); + expect(received, 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..d96b3a1b7 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,18 @@ 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. + /// + /// Requires iOS 15 or newer. It has no effect on macOS, on older iOS + /// versions, or when selecting files with the iOS document picker. + 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, + ); + }); + }); } From 79efba3523b58790953213ba7e9b8430e824d57b Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:54:02 +0200 Subject: [PATCH 2/5] Name the file types that orderedSelection applies to The doc said it had no effect with the iOS document picker, which does not tell which FileType uses which picker. Name image, video and media, the types that go through the photo library picker. --- example/ios/Runner.xcodeproj/project.pbxproj | 3 +-- packages/file_picker/README.md | 4 +++- .../lib/src/file_picker_options/darwin_options.dart | 6 ++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 291555b99..cbae268d8 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -152,7 +152,6 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = KJ6ARNKBG8; LastSwiftMigration = 1250; ProvisioningStyle = Automatic; }; @@ -373,7 +372,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = KJ6ARNKBG8; + DEVELOPMENT_TEAM = 562S5V3538; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/file_picker/README.md b/packages/file_picker/README.md index a051f6d9d..c2158a5ef 100644 --- a/packages/file_picker/README.md +++ b/packages/file_picker/README.md @@ -175,7 +175,9 @@ List files = await FilePicker.pickFiles( 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. +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 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 d96b3a1b7..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 @@ -42,8 +42,10 @@ class DarwinOptions { /// 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. /// - /// Requires iOS 15 or newer. It has no effect on macOS, on older iOS - /// versions, or when selecting files with the iOS document picker. + /// 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 From fc237e5d3fe0cc966c93defb9f995981c3a945f7 Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:59:23 +0200 Subject: [PATCH 3/5] Restore the example's iOS development team 79efba3 picked up a local DEVELOPMENT_TEAM change to the example's Xcode project by accident. --- example/ios/Runner.xcodeproj/project.pbxproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index cbae268d8..291555b99 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -152,6 +152,7 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = KJ6ARNKBG8; LastSwiftMigration = 1250; ProvisioningStyle = Automatic; }; @@ -372,7 +373,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 562S5V3538; + DEVELOPMENT_TEAM = KJ6ARNKBG8; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", From 0165068811ee03fa1b840893fdd3c486c311f536 Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Sun, 11 Oct 2026 09:50:21 +0200 Subject: [PATCH 4/5] Update packages/file_picker_darwin/test/file_picker_darwin_test.dart Co-authored-by: Navaron Bracke --- packages/file_picker_darwin/test/file_picker_darwin_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 84ace55e8..7978336e8 100644 --- a/packages/file_picker_darwin/test/file_picker_darwin_test.dart +++ b/packages/file_picker_darwin/test/file_picker_darwin_test.dart @@ -117,7 +117,7 @@ void main() { test('pickFile and pickFiles send orderedSelection', () async { final picker = FilePickerDarwin(); - bool? received; + bool? orderedSelection; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(picker.methodChannel, (call) async { From 84a0a2591e94587319bbab367754f8b0be20f0ae Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Sun, 11 Oct 2026 09:51:20 +0200 Subject: [PATCH 5/5] Address review feedback on the ordered selection option Rename the remaining uses of the test variable to orderedSelection, following the suggestion applied in 0165068, cover pickFile() with the default options, and drop the redundant interface lower bound line from the changelog. --- packages/file_picker_darwin/CHANGELOG.md | 1 - .../test/file_picker_darwin_test.dart | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/file_picker_darwin/CHANGELOG.md b/packages/file_picker_darwin/CHANGELOG.md index b98662dee..fff12e363 100644 --- a/packages/file_picker_darwin/CHANGELOG.md +++ b/packages/file_picker_darwin/CHANGELOG.md @@ -1,7 +1,6 @@ ## 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) -- Raised the `file_picker_platform_interface` lower bound to `^4.1.0`. ## 2.1.3 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 7978336e8..c8bfd37f1 100644 --- a/packages/file_picker_darwin/test/file_picker_darwin_test.dart +++ b/packages/file_picker_darwin/test/file_picker_darwin_test.dart @@ -121,7 +121,8 @@ void main() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(picker.methodChannel, (call) async { - received = (call.arguments as Map)['orderedSelection'] as bool?; + orderedSelection = + (call.arguments as Map)['orderedSelection'] as bool?; return >[]; }); addTearDown(() { @@ -130,19 +131,22 @@ void main() { }); await picker.pickFiles(type: FileType.media); - expect(received, isTrue); + expect(orderedSelection, isTrue); await picker.pickFiles( type: FileType.media, darwinOptions: const DarwinOptions(orderedSelection: false), ); - expect(received, isFalse); + expect(orderedSelection, isFalse); + + await picker.pickFile(type: FileType.media); + expect(orderedSelection, isTrue); await picker.pickFile( type: FileType.media, darwinOptions: const DarwinOptions(orderedSelection: false), ); - expect(received, isFalse); + expect(orderedSelection, isFalse); }); test(