00 · Scope
A native-first, privacy-first scanner
FOSScanner is a Flutter application for capturing or importing document images, correcting their geometry, applying visual processing, retaining editable page state, and exporting a multi-page PDF. Processing is local to the device. The application has no account system, backend, analytics, or cloud storage.
AGENTS.md, CONTRIBUTING.md, and the tests in test/.01 · Architecture
Keep responsibilities explicit
The application is organized around a small number of stateful screens and platform-isolated services. UI code should depend on stable service entry points rather than platform-specific implementations.
ScannedPage retains original and processed bytes plus all editing parameters.DocumentProcessor exposes matching native and web APIs.The native pipeline uses OpenCV through opencv_dart. The web implementation is API-compatible but intentionally does not perform native document processing because the bindings are FFI-only.
02 · Data flow
Follow bytes and ownership
- Intake: capture or import encoded image bytes. Gallery originals remain untouched.
- Validation: check encoded size, decoded dimensions, corner geometry, page count, and memory limits before expensive work.
- Editing: retain original bytes and user parameters while producing processed output for preview and export.
- Composition: reorder in-memory pages and generate the final PDF from processed output, not a reduced preview.
- Sharing: pass PDF bytes to the platform share mechanism. OS/share caches may materialize temporary copies.
- Cleanup: remove app-owned camera and OCR temporary files on both success and failure.
Expensive native processing must remain off the UI isolate. Asynchronous screen code must preserve busy guards and verify mounted before updating state.
03 · Code map
Where to make a change
| Area | Primary files | Contract |
|---|---|---|
| App entry and theme | lib/main.dart | Material 3 light/dark configuration. |
| Page lifecycle and export | lib/screens/scanner_home_page.dart | Intake, in-memory pages, reorder, PDF, sharing, About. |
| Corner editor | lib/screens/corner_adjust_screen.dartlib/widgets/corner_overlay.dart | Corner editing, previews, and final processing. |
| Geometry and limits | lib/services/corner_geometry.dartimage_metadata.dart | Ordering, convexity, finite values, warp bounds, memory checks. |
| Native processing | document_processor_native.dart | OpenCV lifecycle and filters. |
| Web processing | document_processor_web.dart | API-compatible pass-through behavior. |
| Android OCR | ocr_service.dartMainActivity.ktassets/tessdata/ | MethodChannel, worker execution, Tesseract data, searchable PDF. |
04 · Development setup
Use the pinned toolchain
Use Flutter 3.44.0 from .flutter-version, Dart >=3.12.0 <4.0.0, and Java 17 for Android CI. Keep SDK pins consistent when deliberately upgrading the toolchain.
flutter pub get
flutter run
Docker alternatives are defined in docker-compose.yml. The APK service uses an amd64 toolchain and may require emulation on non-amd64 hosts.
05 · Testing matrix
Validate behavior at the right layer
dart format --output=none --set-exit-if-changed lib test
flutter analyze
flutter test
flutter build web
flutter build apk --debug --target-platform android-arm64
| Change | Required verification |
|---|---|
| Geometry or image limits | Focused service tests plus capture, corner adjustment, filters, and large-image checks. |
| Native processing | Native tests plus Android/iOS or desktop device validation; web is not sufficient. |
| OCR or PDF export | Android emulator/device: selectable text, search, cancellation, cleanup, dimensions, and multi-page output. |
| UI or accessibility | Flutter tests plus semantic actions, keyboard/screen-reader behavior where applicable, and responsive layout checks. |
| Platform configuration | Relevant platform build and CI workflow checks; do not hand-edit generated registrants. |
Native service tests do not replace an emulator or physical-device check. Report environment limitations precisely instead of treating a web build as native validation.
06 · Platform boundaries
Preserve matching implementations
- Android, iOS, and desktop native builds use OpenCV for edge detection, perspective correction, filters, rotation, and brightness/contrast.
- The web processor is a pass-through implementation; the web target is a preview/testing target rather than native-processing validation.
- Desktop camera availability comes from picker capability. Import must remain available where camera UI is unsupported.
- Guard unsupported platform calls and keep web compilation green when adding native services.
- Platform folders contain permissions, identifiers, runners, and build configuration; change their source configuration rather than generated output.
07 · Android OCR bridge
Keep the Dart and Kotlin contracts synchronized
Android searchable-PDF work spans lib/services/ocr_service.dart, the com.fosscanner.app/ocr MethodChannel in MainActivity.kt, Android Gradle dependencies, and the bundled English/Latin Tesseract data under assets/tessdata/.
- Run native OCR on a worker executor and return channel results on the main thread.
- Keep cancellation valid through preparation, recognition, and export; do not fall back after a confirmed cancellation.
- Wait for native work to finish before deleting temporary files.
- Verify searchable text, page dimensions, multi-page output, cancellation, and failure cleanup on an Android emulator or device.
08 · Safety invariants
Protect correctness and privacy
- Preserve original image bytes, editing parameters, and re-editability.
- Reject invalid, non-finite, non-convex, incorrectly ordered, or out-of-bounds corners before warping.
- Bound encoded bytes, decoded dimensions, page count, retained memory, and preview/export allocations.
- Release owned OpenCV Mats, vectors, and Flutter image resources in
finally; do not dispose borrowed contour elements. - Never log document contents or extracted text. Do not add telemetry, uploads, remote processing, or incidental network dependencies.
- Do not promise immediate deletion of OS/share caches; they are managed by the platform.
09 · Contribution and release
Make changes easy to review
Base new work on dev. Use Conventional Commits such as fix:, feat:, and docs:. Add regression coverage for changed behavior, avoid unrelated formatting or dependency upgrades, and preserve pubspec.lock when dependencies change.
Read CONTRIBUTING.md, inspect the CI results, and open an issue when behavior or platform support is unclear.
Release Please manages versions and changelogs when changes are promoted to main. Release Android artifacts require the configured signing properties and keystore; never commit signing credentials or replace release signing with debug signing.