feat(platform_interface): add PlatformFile.size - #2191
Conversation
Restores a synchronous way to get a picked file's size, addressing #2187. Every platform implementation already carries the size the native picker reports when it reports one, length() only falls back to actually reading the file when it doesn't. size exposes that already-known value directly instead of wrapping it in a Future for a case that mostly doesn't apply. Returns null when the size isn't known upfront (Windows and Linux, whose pickers only return a path), callers needing a guaranteed value use length() as before.
…ze override - Accepted the suggested shorter wording for the android_file_picker changelog entry. - Added a dartdoc line on each platform's `size` override describing what it actually returns there (cached native value, browser File.size, or null with a pointer to length() on Linux/Windows), instead of leaving that only in the changelog.
Co-authored-by: Navaron Bracke <brackenavaron@gmail.com>
All six conflicts were the same shape and none of them semantic. This branch bumps each package's version, master rewrote the homepage and repository URLs after the repo moved from miguelpruivo to vicajilau, and the two edits land on adjacent lines. Resolved by keeping both: this branch's version, master's URLs. Checked that the branch introduces no miguelpruivo URLs of its own on lines git merged cleanly, which a conflict marker would never have flagged. It does not. The historical links left in the CHANGELOGs are untouched, matching what the transfer commit itself did.
PlatformFile.size is abstract, so an implementation published before it does not satisfy the interface that declares it. The facade still asked for platform_interface ^3.2.0 alongside implementations at ^1.0.3 and friends, so once this releases pub could pair interface 3.3.0 with an implementation that has no size and produce a resolution that compiles nowhere. That is issue #2186 again, same shape, different member. Raises all six bounds in lockstep with the versions this branch publishes, and takes the facade to 12.2.0 since size is new API reaching its users. The downgrade smoke test in CI cannot see this one. It resolves every dependency to its lowest allowed version, and when the interface bound and the implementation bounds are stale together that minimum set is internally consistent and builds fine, while a real app is still free to pair the new interface with an old implementation. It did catch #2186 only because the interface bound had already been raised there. Adds a test asserting the property the build cannot see: the facade must require at least the versions the workspace is about to publish. It reads the pubspecs off disk, needs no network, and runs in the existing tooling test step. Verified that it reports all four stale bounds this branch arrived with.
Two conflicts, both from #2193 landing on file_picker_android. The version goes to 1.1.0, which supersedes master's 1.0.4, and both CHANGELOG sections stay: they are two different releases. Also makes the downgrade smoke test wait for the release instead of failing on it. A branch that raises the facade's bounds points at versions pub.dev does not have yet, so resolution failed with "every version of file_picker from path depends on windows_file_picker ^1.2.0 which doesn't match any versions", which says nothing about whether the constraints are right. Overriding those to the workspace was the obvious fix and does not work: every package here declares `resolution: workspace`, and pub then drops file_picker itself from the resolution. Verified locally, the build fails with "Couldn't resolve the package 'file_picker'". So the step now detects that case and skips with a notice naming the versions it is waiting on. The invariant it can no longer check on release branches is the one tool/test/facade_constraints_test.dart covers, without needing pub.dev.
Addresses review feedback that the line read oddly. It led with a bare 'null' and packed the reason into a trailing clause. Now it states when the value is known first, then why a picked file has none.
Review feedback: `size` and `length()` named the same thing two different ways. `length()` / `lengthSync()` is the pair `dart:io`'s File already uses, and it is what the issue asked for in the first place. Turns the getter into a method to match, across the interface and all five implementations, their tests and the changelogs. Left the browser's own `File.size` alone in file_picker_web, which is the JS API and not this one.
Same shape as before: this branch bumps each version, main rewrote the tree/master URLs after the default branch rename, and the two land on adjacent lines. Kept this branch's versions and main's URLs.
navaronbracke
left a comment
There was a problem hiding this comment.
LGTM, although I'm beginning to think we should maybe rethink the facade version checks.
Usually what happens is the platform interface package gets published first, on it's own, then a subsequent release of the child packages can depend on the new platform interface.
Is that a solution to the issue?
|
Thanks! And yes, it does solve the CI part, for a reason worth spelling out: with a staged release the facade never references a version that is not on pub.dev yet. That is the only thing the downgrade smoke test was choking on, so the "skip when unpublished" branch I added to it stops being necessary. There is a catch though, and it is mine, not yours. It clashes with the guard I put in this PR. So one of the two has to go. I would keep yours and change what the guard compares against: not "the versions the workspace is about to publish" but "the first published version of each implementation that was built against the interface the facade requires". That works at every stage of a staged release, and it still catches #2186, where the facade asked for interface One thing staged releases do not remove, just so it is on the record: between publishing the interface and publishing the facade, the new interface is live while the old facade still allows implementations that predate it, so the broken pair is resolvable in that window. It exists in the all-at-once flow too, only shorter. The only thing that actually closes it is not adding abstract members in a minor, giving Doing the guard swap here rather than in a follow-up, so the change and the check that backs it land together. |
The previous guard asserted that the facade requires at least the versions the workspace is about to publish. That is lockstep by construction, and it rules out the staged release flow navaronbracke described in review, where the platform interface is published on its own and the implementations catch up in a later release. Simulating that first stage failed the old guard on all six packages. The property checked now holds under either flow: for every implementation, the facade's lower bound must be at least the first published version of that implementation built against the interface the facade requires. It reads that from pub.dev, which is also the pairing the downgrade build in CI can never produce, since resolving everything to its lowest version never puts the newest interface next to the oldest implementation. Verified in three directions: it reports all four stale bounds against the pubspec that caused #2186, it passes on the staged flow's first stage, and on a branch like this one, where the interface version the facade asks for is not published yet, it skips and says so rather than inventing an answer. The arithmetic and parsing have seven offline tests of their own, including a fixture that reproduces #2186.
|
A follow up is fine, thanks! |
file_picker_windows is the Windows implementation, so a '#### Desktop (Windows)' heading above every entry says nothing. It was the only implementation package still carrying one, twice. The facade keeps its per-platform headings, where they do the work of telling apart entries that cover different platforms. Text only, on versions already published, so no version bump. It shows up on pub.dev with the next release. Split out of vicajilau#2191 as agreed there.
Fixes #2187.
The request
PlatformFile.sizewas removed in the v12 federated rewrite without a synchronous replacement, onlyFuture<int> length()remains. That forces anawaitfor a value most callers already have on hand.The fix
Every platform's
PlatformFilealready carries the size the native picker reports, stored in a private_bytesLengthfield.length()already returns it immediately when it's known and only falls back to actually reading the file (xFile.length()) when it isn't, so the async signature exists for that fallback path, not because getting the size is inherently async.Added
int? get sizetoPlatformFile, returning that already known value synchronously,nullwhen it isn't known. Matches the shapeextensionandpathalready use on this class.Per platform, what
sizeactually returns:FileInfo.size)File.size, or loaded bytesnullnullWindows and Linux callers fall back to
await length(), which still works exactly as before.What changed
file_picker_platform_interface3.2.0 → 3.3.0: added the abstract member.file_picker_darwin,android_file_picker,file_picker_webbumped (minor): implementedsizebacked by data they already have.file_picker_linux,windows_file_pickerbumped (minor): implementedsize, returnsnullsince neither picker reports it.file_picker_platform_interfacelower bound raised to^3.3.0, the version that introduces the abstract member they now implement. Learned this the hard way with You forgot to bump platform implementations version! #2186, an implementation built against an older interface won't have this override and will fail to compile if pub resolves it alongside a newer interface.file_picker(the facade) is untouched,PlatformFileis used through the platform interface as-is, no facade code referencessizedirectly.Test plan
flutter analyzeclean across the whole workspacedart format --output=none --set-exit-if-changedcleanflutter testacross the whole workspace, 57 tests pass, including new coverage forsizeon every platform (web's is@TestOn('browser')like the rest of that file, doesn't run under the defaultflutter test)