Expense Registration Screen — summary confirmation view
epic-travel-expense-registration-ui-task-011 — Add the summary confirmation view as the final step before submission. Display all entered values in plain language (no jargon, no IDs). Apply the cognitive accessibility pattern: one primary action (confirm and submit), one secondary action (back to edit). Disable the submit button while the BLoC is in submitting state and show a loading indicator. After successful submission navigate to a success screen and clear draft state.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Build summary rows dynamically from a list of SummaryRowViewModel objects derived from the draft claim — this makes the summary resilient to future field additions without UI code changes. Use the organisation labels system for all field labels rather than hardcoded strings. For the loading/disabled button state, use a single LoadingButton widget that accepts isLoading and onPressed parameters — disable onPressed (set to null) when isLoading is true; Flutter's ElevatedButton automatically styles as disabled when onPressed is null. Use BlocConsumer (combining BlocBuilder for UI + BlocListener for side effects) on the summary view to handle both the button state and the navigation side effect in one widget.
Ensure the GoRouter transition to the success screen uses a fade or slide animation matching the multi-step flow direction to avoid spatial disorientation for cognitively accessible users.
Testing Requirements
Widget tests: summary renders all draft values with correct plain-language labels; confirm button is enabled when BLoC is in idle state; confirm button is disabled and spinner shown when BLoC emits loading state; back-to-edit button pops step without route navigation. BLoC integration: dispatching SubmitExpenseEvent from summary → BloC emits submitting → widget disables button → BLoC emits submitted → BlocListener triggers GoRouter push. Navigation test: after successful submission GoRouter navigates to success route with correct expense ID param; BLoC reset event is dispatched. Golden tests at 1.0x and 2.0x font scale.
Currency formatting test: 1234.5 NOK renders as 'kr 1 234,50' per Norwegian locale.
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.