Skip to content

Release 14.0.0: migrate to cross_file 0.4.0 and remove web reading options - #2236

Draft
vicajilau wants to merge 10 commits into
mainfrom
feat/v14-cross-file-0.4
Draft

vicajilau wants to merge 10 commits into
mainfrom
feat/v14-cross-file-0.4

Conversation

@vicajilau

@vicajilau vicajilau commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Fixes #2224

Major release for every package. This is a breaking change on all platforms, so please review it carefully. The points that most deserve attention are marked with ⚠️.

Changes

Migrate to cross_file 0.4.0

PlatformFile.xFile returns an XFile, so its API is part of ours. cross_file 0.4.0 removes XFile(path, name:, bytes:), XFile.path, mimeType, saveTo() and fromData, and turns name into Future<String?> name().

  • Android, Darwin, Linux and Windows build their XFile lazily with XFile.fileSystem for file URIs and XFile.scopedStorage otherwise. It used to be built eagerly in the factories, but in 0.4.0 every XFile needs the cross_file plugin to be registered, so it is now only created when requested.
  • The unused bytes parameter of LinuxPlatformFile.fromPath() and WindowsPlatformFile.fromPath() is removed.

⚠️ cross_file 0.4.0 is now a federated plugin with native code (cross_file_android, cross_file_darwin, cross_file_io, cross_file_web), and it raises the minimums:

Before After
Flutter 3.38 3.41
Dart 3.10 3.11
Android SDK 21 24
macOS 10.13 10.15
iOS 14 14 (unchanged)

minSdk, Package.swift and the podspec follow.

Web: remove withData, withReadStream and readSequential

PlatformFile does not expose preloaded bytes, so these options only decided when the content was read, and the eager paths were the source of the large file failures (#2223). They are removed from FilePickerWebOptions, which keeps cancelUploadOnWindowBlur only.

Picked files are no longer read at pick time. Each File is wrapped in a cross_file_web XFile created from the Blob, and WebPlatformFile delegates readAsBytes(), readAsByteStream() and length() to it. This replaces the hand written FileReader, chunked stream and fetch helpers (about 250 lines and three internal files), and drops the bytes and readStream parameters of WebPlatformFile. Read errors still propagate, as fixed in #2229.

⚠️ The object URL is created with autoRevokeObjectUrl: false, so PlatformFile.uri stays valid for as long as the page is open, as before.

⚠️ readAsByteStream() on web emits evenly sized chunks of WebPlatformFile.streamChunkSize (1 MiB), except the last one, by slicing the picked Blob with slice() and arrayBuffer(). xFile.openRead() goes through Blob.stream(), whose chunk sizes are irregular, and #2223 showed a real consumer (RamFileData.fromStream from the archive package) that needs evenly sized buffers, which it used to get from withReadStream. Removing that option without this would have broken it.

Versions and docs

  • file_picker 14.0.0, file_picker_platform_interface 5.0.0, android_file_picker, file_picker_darwin, file_picker_linux and windows_file_picker 3.0.0, file_picker_web 5.0.0, with short changelogs.
  • New "Migrating to v14" section in the file_picker README, linked from the root README.

Testing

  • Every package's tests pass and flutter analyze is clean across the workspace.
  • New test per native platform reading a real temporary file through cross_file_io.
  • Web tests run in Chrome (19 tests, including the chunk sizes), and now on CI too thanks to Run file_picker_web tests in Chrome on CI #2235.
  • The example builds for web (with CrossFileWeb registered automatically) and macOS locally.
  • The CI agp-8.x Android lane was reproduced locally with Java 17, since cross_file_android declares Kotlin 2.3.0 while that lane uses 2.2.20, and it builds.

The facade check and facade_constraints_test skip themselves until the new majors are published, as designed for release branches.

cross_file 0.4.0 removes the XFile(path, name:, bytes:) constructor and resolves every XFile through a registered CrossFilePlatform. The Android, Darwin, Linux and Windows platform files now build their XFile lazily with XFile.fileSystem for file URIs and XFile.scopedStorage otherwise, instead of eagerly in their factories. The unused bytes parameter of LinuxPlatformFile.fromPath and WindowsPlatformFile.fromPath is removed.

cross_file 0.4.0 also raises the minimums to Flutter 3.41, Dart 3.11, Android SDK 24 and macOS 10.15, so the constraints, minSdk and deployment targets follow.

Each package gets a test that reads a real temporary file through cross_file_io.

Refs #2224
PlatformFile does not expose preloaded bytes, so withData, withReadStream and readSequential only decided when the content was read, and the eager paths were the source of the large file failures. They are removed from FilePickerWebOptions, which keeps cancelUploadOnWindowBlur only.

Picked files are no longer read at pick time. Each File is wrapped in a cross_file_web XFile created from the Blob, and WebPlatformFile delegates readAsBytes(), readAsByteStream() and length() to it. This replaces the hand written FileReader, chunked stream and fetch helpers, and drops the bytes and readStream parameters of WebPlatformFile. Read errors still propagate.

The object URL is created without auto revocation so PlatformFile.uri stays valid for as long as the page is open, as before.

Refs #2224
Bump every package to a new major for the cross_file 0.4.0 migration: file_picker 14.0.0, file_picker_platform_interface 5.0.0, android_file_picker, file_picker_darwin, file_picker_linux and windows_file_picker 3.0.0, and file_picker_web 5.0.0, with their changelogs and raised constraints.

Add the Migrating to v14 guide covering the new minimum versions, the new XFile API and the removed web reading options.

The unreleased 4.0.1, 2.0.1 and 2.0.2 sections are kept so those patches can still be published from main before this lands.

Refs #2224
@vicajilau

Copy link
Copy Markdown
Owner Author

Hey @navaronbracke! 👋 Whenever you have a moment, this one is ready for review. It's the big one, so no rush at all 🙂

The parts I'd love a second pair of eyes on:

  • cross_file 0.4.0 is now a federated plugin with native code, and it raises the minimums to Flutter 3.41, Dart 3.11, Android SDK 24 and macOS 10.15.
  • On web, picked files are no longer read at pick time and WebPlatformFile delegates to a cross_file_web XFile created from the Blob.
  • The object URL is created with autoRevokeObjectUrl: false so PlatformFile.uri keeps working for as long as the page is open, as before.

CI is fully green, including the browser tests from #2235 and both Android lanes. Thank you! 💙

xFile.openRead() reads through Blob.stream(), whose chunk sizes are irregular. Consumers that rely on evenly sized buffers, such as RamFileData.fromStream from the archive package, used to get them from withReadStream, which sliced the file into fixed chunks.

readAsByteStream() now slices the picked Blob into WebPlatformFile.streamChunkSize (1 MiB) pieces with slice() and arrayBuffer(), so every chunk but the last has the same size. It falls back to openRead() for an XFile without a Blob.

Refs #2223
Comment thread packages/file_picker/CHANGELOG.md Outdated
Comment thread packages/file_picker/CHANGELOG.md Outdated
- **BREAKING CHANGE**: Migrated to `cross_file` 0.4.0. `PlatformFile.xFile` now returns the new `XFile` API. See the [migration guide](https://github.com/vicajilau/flutter_file_picker/tree/main/packages/file_picker#migrating-to-v14). [#2224](https://github.com/vicajilau/flutter_file_picker/issues/2224)
- **BREAKING CHANGE**: Requires Flutter 3.41, Dart 3.11, Android SDK 24 and macOS 10.15.
- **BREAKING CHANGE**: Removed `withData`, `withReadStream` and `readSequential` from `FilePickerWebOptions` (`file_picker_web 5.0.0`).
- Raised lower bounds of all platform implementation packages: `file_picker_platform_interface ^5.0.0`, `android_file_picker ^3.0.0`, `file_picker_darwin ^3.0.0`, `file_picker_linux ^3.0.0`, `windows_file_picker ^3.0.0`, and `file_picker_web ^5.0.0`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This last line is redundant? Consumers will pick this up in their lockfiles anyway.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, the pubspecs already say it. Removed in 476d9a4.

Comment thread packages/file_picker/README.md Outdated

2. **`PlatformFile.xFile` Uses `cross_file` 0.4.0**:
* `XFile.path`, `XFile.mimeType`, `XFile.saveTo()` and `XFile.fromData` no longer exist, and `XFile.name` is now `Future<String?> name()`.
* Use `PlatformFile.path`, `PlatformFile.name`, `readAsBytes()` and `readAsByteStream()` when you do not need an `XFile`. For a file system path, check for a `FileSystemXFile`: `final path = switch (file.xFile) { FileSystemXFile(:final path) => path, _ => null };` (`FileSystemXFile` comes from `package:cross_file/cross_file.dart`).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be a multiline code sample in triple backticks?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much more readable, thanks! Done in 476d9a4, with the import in the block too.

_xFile ??
(uri.scheme == 'file'
? XFile.fileSystem(path: uri.toFilePath())
: XFile.scopedStorage(uri: uri.toString()));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latter one is a content URI, right?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly! Anything outside the file scheme is a Storage Access Framework content:// URI. I documented it on the getter in 476d9a4 so it doesn't need explaining again 🙂

const size =
WebPlatformFile.streamChunkSize * 2 +
WebPlatformFile.streamChunkSize ~/ 2;
final large = Uint8List.fromList(List.generate(size, (i) => i % 251));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be 255/256?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! It's 251 on purpose: being prime, the byte pattern doesn't line up with the chunk boundaries, which are a multiple of 256. With % 256 every chunk would start with the same bytes, so swapped or repeated chunks would still pass. I added a comment explaining it in 476d9a4.

vicajilau and others added 3 commits October 8, 2026 08:09
# Conflicts:
#	packages/file_picker_darwin/CHANGELOG.md
#	packages/file_picker_darwin/pubspec.yaml
Co-authored-by: Navaron Bracke <brackenavaron@gmail.com>
Drop the redundant lower bounds line from the file_picker changelog, show the FileSystemXFile path lookup in the migration guide as a code block, document that AndroidPlatformFile.xFile wraps a content URI outside the file scheme, and explain why the chunking test uses a prime modulus.
@vicajilau
vicajilau marked this pull request as draft October 9, 2026 05:09
@vicajilau

Copy link
Copy Markdown
Owner Author

Converting this to draft on purpose, it's ready but should not land yet.

cross_file 0.4.0 is very recent and nothing in the ecosystem has migrated to it yet, including the Flutter team's own plugins: image_picker_platform_interface, file_selector_platform_interface and camera_platform_interface all still require cross_file ^0.3.x, as do share_plus and flutter_image_compress. @androidseb confirmed it in #2223: with this branch, any app that also uses one of those fails version solving.

So the plan is:

  • Ship the web improvements that don't need cross_file 0.4.0 as a minor (file_picker_web 4.1.0): evenly sized 1 MiB chunks from readAsByteStream() and deprecating withData, withReadStream and readSequential.
  • Keep this PR as is, already reviewed, and land 14.0.0 once those plugins support cross_file 0.4.0, bringing main in at that point.

# Conflicts:
#	packages/file_picker_web/CHANGELOG.md
#	packages/file_picker_web/lib/src/file_picker_web.dart
#	packages/file_picker_web/lib/src/file_picker_web_options.dart
#	packages/file_picker_web/lib/src/platform_file_web_fetch.dart
#	packages/file_picker_web/lib/src/web_platform_file.dart
#	packages/file_picker_web/pubspec.yaml
#	packages/file_picker_web/test/file_picker_web_pick_test.dart
@androidseb

Copy link
Copy Markdown

@vicajilau thanks for all your work on that release ❤️ it looks great.


About this:

Keep this PR as is, already reviewed, and land 14.0.0 once those plugins support cross_file 0.4.0, bringing main in at that point.

I'm wondering why you would hold off on 14.0.0 / 5.0.0, is it to save the trouble for users bumping into dependencies problems? Personally I know I'll wait for the cross_file bump in my other dependencies before I upgrade, but maybe some other users who don't use those blocking dependencies would be able to upgrade earlier?

The flutter pub upgrade command or similar tooling-based upgrades already handles these dependencies constraints (I only ran into issues because I did manual overrides), so people looking to upgrade are maybe unlikely to run into trouble, while users looking to upgrade early without constraints will have the ability to? I guess if they add something like share_plus after using your latest version, they might have to downgrade which could be a bit of trouble, but maybe having the choice is preferable?

Just a thought :-)

@navaronbracke

Copy link
Copy Markdown
Collaborator

Personally I don't see why we would hold off on releasing it. There isn't really much new from v13, except for the new cross_file usage

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Next major version (14.0.0)

3 participants