Skip to content

Promote dev to main: QR/barcode scanning, corner-adjust fixes, about screen - #23

Merged
lorenzocamilli merged 17 commits into
mainfrom
dev
Aug 28, 2026
Merged

Promote dev to main: QR/barcode scanning, corner-adjust fixes, about screen#23
lorenzocamilli merged 17 commits into
mainfrom
dev

Conversation

@lorenzocamilli

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • CI (flutter analyze + flutter test + debug APK build) green on every commit in this range
  • Manually verified on-device: QR scanning, 1D barcode scanning, corner-adjust validation

Adds an info button to the home screen AppBar that shows the running
version (via package_info_plus) and a link to the source repo (via
url_launcher).
Several platforms still showed the app name as 'fosscanner' or
'Fosscanner' (Android's home-screen label was already fixed
separately in the previous commit): iOS bundle display name, macOS
product name (plus the Xcode project/scheme references to the
resulting .app bundle), Linux window title, and Windows window title.

Deliberately left as lowercase: the Dart package name and every
package:fosscanner/... import (Dart forbids uppercase in package
names), the com.fosscanner.app bundle identifiers (reverse-DNS
convention), and build-artifact filenames.
CONTRIBUTING.md never actually said to base pull requests on dev
rather than main. Also adds the gallery-import feature (shipped since
1.2.0 but never listed) and the new about screen to the README's
feature list.
The version was shown as a bare '1.2.2+5', which reads like a typo
rather than "version 1.2.2, build 5". Also gives the dialog an actual
layout (icon, name, clear version line, a short description of what
the app is) instead of relying on the default AboutDialog's plain
stacked children, and demotes the open-source licenses list to a
secondary button rather than the dialog's main content — it's still
one tap away (required for license compliance with bundled
dependencies), just not the first thing shown.
A convex, consistently-wound quad can still have its points in the
wrong array positions (e.g. two adjacent handles dragged past each
other, or all four roles shifted by one) without tripping the
existing self-intersection/winding checks, since the shape itself
stays valid. That silently warped the page into a flipped or rotated
output instead of being caught.

calculateWarpSize now re-canonicalizes the corners with orderCorners
(already used at detection time) and rejects if it disagrees with the
given order.

Fixes #20
… preview

Validation/processing errors were rendered as an inline Text widget
stacked above the image inside the same Column, so showing an error
visibly shrank the photo preview the user was trying to work with.

Errors now surface via a SnackBar, matching the pattern already used
on the home screen. The inline _error field is now only used for the
one case that legitimately owns the whole screen: a fatal read
failure during initialization.

Fixes #21
The Android ABI build in this job was previously discard-on-exit,
existing only to catch Gradle/NDK/native-asset regressions. Uploading
it makes it possible to actually grab a dev-branch build for manual
testing without needing a local Flutter/Android toolchain.
Adds a "Scan QR/barcode" action to the home screen, separate from the
document-scan flow: a live camera view decodes a code and shows the
result with copy/open actions, rather than treating the code as a
document page.

Uses flutter_zxing (MIT, on-device via zxing-cpp/FFI) rather than
mobile_scanner/ML Kit, to avoid pulling in Google's proprietary,
Play-Services-dependent barcode backend for a privacy-first app.

flutter_zxing has no web decoding backend, so the entry point is
hidden on web, matching the existing kIsWeb convention used for the
opencv-dependent detect/adjust flow.

Also allows launching plain http (not just https) links decoded from
a scanned code, via the existing url_launcher package-visibility
queries block.

Note: pubspec.lock was not regenerated with this commit (no local
Flutter toolchain available) — needs a `flutter pub get` run and
commit before this is fully in sync.
Video of the scan screen showed a well-lit, correctly-framed QR code
sitting inside the on-screen crop guide for several seconds without
ever being picked up.

This screen embeds ReaderWidget below an AppBar rather than
full-screen, which is exactly the scenario flutter_zxing's maintainers
flag in khoren93/flutter_zxing#196: the letterboxed preview makes the
drawn crop-indicator square visually line up with the code while the
actual pixel region handed to the native decoder is offset elsewhere,
so nothing ever gets read despite looking correctly framed.

Their recommended fix for non-fullscreen placement is cropPercent: 0,
which decodes the full frame instead of the misaligned crop window.
…lickable

Two issues reported after the crop fix:

- No way to dismiss a scanned result and resume scanning without
  leaving the screen — added a close button on the result card.
- Disabling ReaderWidget's built-in crop guide (needed for actual
  detection to work) also switched its overlay to a "tap the
  highlighted code" mode, which read as scanning requiring a click
  when onScan already fires automatically on decode. Turned that off
  (showScannerOverlay: false) and replaced it with a plain,
  non-interactive centered square drawn locally, restoring the aiming
  hint without tying it to the real decode region again.
Video showed a QR code decoding almost instantly while several 1D
barcodes on a physical product box were never picked up at all,
despite Format.any already covering those formats.

tryHarder enables a more exhaustive per-frame decode pass, which 1D
formats need much more than QR: they carry far less error-correction
redundancy and are far more sensitive to being held at a slight skew,
which is how a barcode is realistically framed by hand.
Found in review: calculateWarpSize ran the new role-order check twice
(once on the caller's corners, once on a rounded copy for OpenCV's
integer source points), but orderCorners' tie-break on edge-midpoint y
can flip once coordinates are rounded, even when the unrounded quad
was unambiguous. That made a perfectly valid, correctly-labeled crop
get rejected whenever that tie happened to land on opposite sides of
the rounding boundary — a rounding artifact, not a real mislabeling.

Split the role check out of _validateConvexQuad into its own function,
called once on the original un-rounded corners only. The convexity/
degenerate checks still run on both the raw and rounded copies, same
as before.
…aunchUrl failures

Found in review:

- scanner_home_page.dart's _showMessage and corner_adjust_screen.dart's
  _showError were identical deferred-SnackBar implementations copied
  verbatim. Extracted the shared logic into
  lib/widgets/transient_message.dart so a future fix to the
  postFrameCallback/mounted-race pattern only needs to happen once.
- BarcodeScanScreen._handleScan could call setState after the widget
  was disposed: ReaderWidget's onScan fires from an async camera-frame
  decode that can complete after the user has already navigated away.
  Added the same mounted guard every other callback in this file uses.
- The About dialog's "View source code" link and the barcode scanner's
  "Open" button both called launchUrl and ignored the result, unlike
  every other user-triggered action in these files, which shows a
  SnackBar on failure. Now checks the returned bool and catches
  exceptions, consistent with the rest of the app.
Found in review: pubspec.yaml added flutter_zxing but pubspec.lock was
never regenerated to include it (no Flutter SDK was available to run
pub get while making that change). CI's flutter pub get silently
regenerates the missing entries in its own ephemeral checkout each
run, which is why this didn't surface as a build failure, but the
lockfile actually committed to the repo is inconsistent with
pubspec.yaml — a problem for anything that installs strictly from the
committed lockfile, including the F-Droid reproducible-build effort
already on the roadmap.

Uploading it here so the correct, tool-generated lockfile can be
pulled down and committed as a follow-up, rather than hand-editing it.
flutter analyze correctly flagged context use after await without a
local mounted guard — showTransientMessage's own internal check isn't
visible to the analyzer at the call site.
@lorenzocamilli
lorenzocamilli merged commit 56f222d into main Aug 28, 2026
2 checks passed
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.

1 participant