# Identity photo preview correction Task [#69](https://git.sendico.io/tech/sxchnge/issues/69), branch `kyc-69`. This frontend change builds on the [identity verification journey](kyc.md) and is independent of the birthday controls in task #67. ## Cause and correction A synthetic valid PNG reproduced this Flutter 3.47.7 browser error: ```text TypeError: Instance of '_UnmodifiableUint8ListView': type '_UnmodifiableUint8ListView' is not a subtype of type 'NativeUint8List' ``` The stack reached `Uint8ListToJSUint8Array.toJS` from CanvasKit's browser image decoder through `MemoryImage` and `ResizeImage`. File selection had successfully decoded the original typed bytes. The `Photo` constructor then copied them into an unmodifiable view, which was passed directly into the preview's web decoder. Native decoding accepted that view, hiding the bug in native-only tests. The generic image error builder presented the failure as a damaged document even though the selected bytes were valid. `PhotoPreview` now creates a native `Uint8List` copy for `MemoryImage` once per selected photo. The shared domain model stays immutable, and validation, providers, services and transport contracts remain unchanged. The preview still requests a decode width of at most 1000 pixels and evicts its decoded cache entry on replacement or disposal. The extra encoded-byte copy belongs to the preview and is released with its provider/cache references; no secure-erasure guarantee is made. An actual preview failure now says that the preview could not be displayed and suggests selecting the photo again. The English/Russian fallback keeps at least the preview's usual height, can grow for long text, and has a separate live-region semantic node. Existing replacement/removal controls stay available and selected input is retained. This is distinct from validation's corrupt-file message. ## UX and measurement review The outcome remains selecting readable ID and selfie photos, checking both on review, and submitting for confirmed service acceptance. Existing KYC journey attempts, retries, failure, submission and abandonment signals are reused. No control-level events, image bytes, filenames, form values or raw exceptions are added to telemetry. A selection success measures validation; it does not establish that pixels were painted. Preview-only rendering failures are not separately counted by existing telemetry. Server review/approval measurement and native screen-reader QA retain the limitations documented in the original journey. Regression tests use synthetic JPEG/PNG content only. They start and await real image decoding outside the widget test's fake async zone, inspect the actual rendered images, and surface stream errors so an error fallback cannot conceal a failure. Checks cover portrait/landscape decoding, model immutability, bounded decode width, stable provider reuse, replacement/removal cache eviction, both review slots, returning to review, localized fallback/replacement, a 320px view at 1.6 text scale, and error announcements isolated from surrounding instructions. The `image` package is already a KYC runtime dependency; its direct app test dependency is used only to create synthetic fixtures in memory. ## Validation and delivery The original browser regression failed before the fix while its native equivalent passed. The complete task diff was reviewed against frontend architecture, UX, measurement, lifecycle and privacy requirements. Final validation passed: - In `frontend/app`, `dart format --output=none --set-exit-if-changed lib/kyc/preview.dart test/preview_test.dart`: two files, no changes. - In `frontend/app`, `flutter analyze --no-pub`: no issues. - In `frontend/app`, `flutter test --platform chrome test/preview_test.dart --reporter expanded --no-pub --verbose`: all six regression tests passed. - At the repository root, `make test-frontend`: all 228 tests passed across the shared packages, theme example and app, including the six native preview regressions and existing KYC journey/analytics tests. - In `frontend/app`, `flutter build web --no-pub`: release build and Wasm compatibility dry run succeeded. Browser regression coverage uses Chrome; Safari and native assistive-technology testing remain release checks. Generated localization and build code stays ignored; the tracked app lockfile changes only `image` from transitive to direct test dependency. The separate birthday worktree was preserved.