Expense Registration Screen — scaffold and widget integration
epic-travel-expense-registration-ui-task-010 — Implement the primary expense registration screen that hosts the ExpenseTypeSelectorWidget and ReceiptCaptureWidget within the multi-step activity registration flow. Use SingleActionScreenLayout from the cognitive accessibility component library. Provide a scrollable form body, a persistent submit button in the bottom safe area, and a step progress indicator. Integrate with the ExpenseFormBloc for state-driven rendering.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Model the multi-step flow as a PageController or an IndexedStack so each step widget remains alive (preserving scroll position and field values). SingleActionScreenLayout should receive the step body as a scrollable child slot and the action button as a bottom slot — do not nest another Scaffold. The step progress indicator should be a simple LinearProgressIndicator or dot indicator above the form body, not a bottom navigation bar. Integrate ExpenseFormBloc with BlocProvider above the route (in the router) rather than inside the screen widget so the BLoC survives step navigation.
Use addPostFrameCallback to request focus on the first field of each step after the step transition animation completes — this aids keyboard and screen-reader users. Follow the cognitive accessibility guideline: one primary action per screen, no floating action buttons, no side-swipe navigation between steps.
Testing Requirements
Widget tests: screen renders with correct initial step; advancing to next step updates progress indicator label; back navigation on first step triggers route pop; back navigation on later steps returns to previous step without route pop. BLoC integration tests: loading state disables submit button and shows spinner; error state does not advance step. Golden tests for each step at 1.0x and 2.0x font scale and 320 dp screen width. Accessibility: Semantics tree contains step label 'Step N of M'; submit button Semantics includes disabled state when loading.
Test that keyboard appearance does not clip the submit button.
The image_picker Flutter plugin requires platform-specific permissions (NSPhotoLibraryUsageDescription, camera permission) and behaves differently across iOS and Android versions. Permission denial or plugin misconfiguration can silently prevent receipt attachment.
Mitigation & Contingency
Mitigation: Configure all required permission strings in Info.plist and AndroidManifest.xml during initial plugin setup. Use the permission_handler package to check and request permissions before launching the picker, with clear user-facing explanations. Test on both platforms across at least two OS versions.
Contingency: If image_picker proves unreliable on a specific platform version, fall back to file_picker as an alternative that uses the OS document picker interface, which requires fewer permissions on some Android versions.
The expense form BLoC manages interconnected state across expense type selection, field visibility, receipt requirement, threshold evaluation, and submission flow. Incorrect state transitions can cause UI inconsistencies such as required receipt indicator not updating after amount change, or form appearing valid when mutual exclusion is violated.
Mitigation & Contingency
Mitigation: Model BLoC states as sealed classes with exhaustive pattern matching. Write state transition unit tests covering every combination of: type selection change, amount field change above/below threshold, receipt attachment/removal, and offline mode toggle. Use bloc_test for comprehensive state sequence assertions.
Contingency: If BLoC complexity becomes unmanageable, split into two BLoCs — one for type selection/exclusion state and one for field values/submission — coordinating via a parent provider, accepting the small overhead of inter-BLoC communication.
The expense type selector must enforce mutual exclusion visually by disabling options and showing conflict tooltips, while remaining fully accessible to screen reader users who cannot perceive visual disable states. Incorrect semantics labelling will fail WCAG 2.2 AA requirements critical for Blindeforbundet and HLF users.
Mitigation & Contingency
Mitigation: Use Flutter Semantics widgets to explicitly set disabled state and provide conflict explanations as semanticLabel strings on disabled options. Run accessibility audits with TalkBack and VoiceOver during widget development, not post-completion. Reference the project's accessibility test harness for required test coverage.
Contingency: If custom widget accessibility is difficult to certify, implement the selector as a standard Flutter Radio/Checkbox group with built-in accessibility semantics and an explanatory Text widget below each conflicting option, sacrificing visual elegance for guaranteed WCAG compliance.