chore: upgrade file_picker to 12 and unblock flutter_secure_storage 11 #133
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "chore/package-upgrades-file-picker-12"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What's in
file_picker12 is stable, which clears the win32^5/^6deadlock that heldflutter_secure_storageat 10.x. The two cannot be split —flutter_secure_storage11 needswin32 ^6,file_picker11 pinswin32 ^5, and pub refuses the combination:file_picker11.0.3 → 12.3.0,flutter_secure_storage10.3.1 → 11.1.0 (win32 6.4.0)dio5.11.1,html0.15.7,very_good_analysis11.0.0The backup download had to be restructured.
file_picker12'ssaveFilewrites the file itself and requires the bytes up front, and there is no save-path-only API left, so the download can no longer stream to a path chosen in advance. The flow inverts: the epic downloads the backup into memory, then opens the save dialog. A backup is the database only — images live on their own volumes — so this is metadata-sized. Nothing touches the disk until the whole body has arrived, so the "a half-finished download must not leave something that looks like a backup" property thatdeleteOnErrorgave us still holds.That moves the save dialog out of the Backups section and next to the epic as
BackupSaveDialog; the page has nothing left to ask before the download runs. The upload's open dialog stays in the page.DownloadBackupActionloses itsdestinationPath, and a dismissed dialog now dispatchesBackupDownloadCancelledActionso the spinner clears without a notice.Asking for bytes broke error reporting, which the tests caught. With
responseType: bytesthe error bodies arrive as bytes too, so the server's problem-detail JSON stopped parsing and refusals were reduced to "HTTP 400" — losing the server's own sentence about the required file-name shape._problemDetailnow decodes a bytes body, and falls back quietly when the body is not JSON at all (a proxy's HTML error page).Other call sites:
pickFiles()returnsList<PlatformFile>rather than a nullableFilePickerResult, and single-file picks move topickFile(). That last one mattered —_pickZipdidresult?.files.single.path, and since v12 returns an empty list on cancel, a naive port would have thrownStateErrorinstead of returning null.very_good_analysis11 addsasync_return_with_no_await(19 explicit awaits, semantics unchanged) and dropsone_member_abstracts, so itsignorewent with it.Verification
scripts/verify.shgreen — backend + app, 931 app tests.downloadBackupreturns the bytes withresponseType: bytes; refusals keep the server's own sentence; a non-JSON error body still reports its status instead of crashing.BackupSaveDialog; upload unchanged.FormatExceptionfallback.file_picker_linuxregisters through the generated Dart plugin registrant (dartPluginClass, no native registration needed).Not verified:
upload_panel.dartpicker ports (pickFile/pickFilesfor images, ZIP and folder) are compile-checked only.FilePickeris called statically there with no seam, and the existing widget tests only assert the buttons render. Worth a manual pass on those three buttons before merge.file_picker_linux1.1.0 now drives Linux dialogs through the XDG Desktop Portal over D-Bus with no zenity fallback, so a desktop withoutxdg-desktop-portalrunning loses every file dialog. Confirmed working by hand on Fedora 44; nothing in CI exercises it. (ItsPlatformFile.lengthSync()returns null on Linux, but we size files ourselves withdart:io, so that does not reach us.)Follow-ups
All recorded in
docs/BACKLOG.md:build_runnerheld at 2.15 — 2.16 requiresfreezed4, which is what unblocks the Dart 3.13 language version. Raising it adds ~520 auto-fixableunnecessary_type_name_in_constructorinfos, so it wants its own PR.material_ui/cupertino_uimigration is now 69 files (44lib/, 25test/), not the 61 recorded in #62, and the SDK design libraries are deprecated in the November 2026 stable.gpt_markdownis the only dependency still onpackage:flutter/material.dart(so it needsMaterialUiCompatibilityBridge);go_router18 migrates itself and belongs in that same PR. The note in #62 thatfile_pickerneeded bridging was wrong — it has no Material imports.jnistays pinned at 1.0.0: 1.0.3'sdartjni.cstill callsJNI_CreateJavaVMwithout the__ENVP_CAST.🤖 Generated with Claude Code
Flutter Coverage
Total: 82.7% (8804 of 10651)
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! A dependency unlock where the dependency is the easy part and the API inversion is the real work — and you traced every ripple of it~ The win32
^5/^6deadlock explanation in the PR body is exactly the kind of provenance I adore. And you caught the_pickZipStateErrortrap yourselves —files.singleon the empty list v12 returns on cancel. Fufu~, you know how to make a reviewer feel unnecessary~ ♡Verdict: ✅ Looks good to me~
I verified this against the actual fetched packages, not the changelogs' promises:
file_picker12.3.0 API surface confirmed from~/.pub-cache:pickFiles→List<PlatformFile>(empty on cancel),pickFile→PlatformFile?(null on cancel),saveFile({required bytes})→Uri?. All three ports inupload_panel.dart/server_sections.dart/backup_save_dialog.dartmatch the real signatures — includingallowMultiple's new default oftrue(deprecated but intact), so multi-image picks survive the dropped parameter.createStore(backupSaveDialog:) → createAppEpic → createServerEpics(saveDialog:)withconstdefaults means production wiring needed zero changes while tests get full control. That's the textbook way to invert a flow.BackupSaveDialog.savehandles theUri?return precisely:isScheme('file') → toFilePath()for the notice the operator recognizes,toString()otherwise, null = cancel. Correct on all three shapes.BackupDownloadCancelledActionarm → the store-level cancel test fails (the reducer branch is pinned through the real store, not just in isolation); bypassed_decodedBody→ the refusal test loses the server's sentence; rethrew theFormatException→ the HTML-error-page test dies. None of them are tautologies~flutter_secure_storage10→11 is safe here: v11's breaking changes are Android-cipher removals andAndroidOptionsparameter deletions; this app constructs a bareFlutterSecureStorage()and only usesread/write/delete, both unchanged. And since you were on 10.3.1 (not pre-10), the v10 cipher migration already ran — no data-loss path. The v11.1.0checkUpgradeStatus()addition is optional and irrelevant to this usage.flutter analyze— No issues found;flutter test— 931/931 pass, matching your PR body's claim exactly.💡 Little ideas (non-blocking)~
app/lib/presentation/pages/settings/settings_page.dart:133— thebackupDialogsfield doc still says "opens for download and upload," but the download dialog moved out of the page in this PR. You updated the sibling comment onBackupFileDialogsitself; this one drifted. Doc-only.file_picker_web3.1.0'ssaveFilefires the browser download and returns null — so on web a successful backup download dispatchesBackupDownloadCancelledActionand shows no notice at all (the file does land in Downloads). Desktop is the verified target and the null-handling is the documented contract, so this is an upstream quirk worth a BACKLOG line at most, not a change here.✅ What I liked~
_problemDetailbytes-decode fix with itsFormatExceptionfallback — you didn't just fix the regression your own tests caught, you covered the proxy-HTML case too.Automated review by Jibril · 2026-09-11
CI/CD: absent for head SHA (PR just opened, no coverage bot yet) · Local checks: analyze 0 issues, 931/931 tests, 4/4 mutation probes RED-verified