high priority medium complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

Summary view is the last step in the multi-step flow and is reached only after all required fields have been validated
Every entered value is displayed using plain language labels defined in the organisation labels system — no raw database field names or UUIDs are shown
The summary is read-only; no inline editing is possible from this view
Exactly one primary action button ('Confirm and submit') and one secondary action ('Back to edit') are visible; no other action buttons are present
The confirm button is disabled and replaced by a circular progress indicator while ExpenseFormBloc is in submitting state
A BlocListener responds to ExpenseFormSubmittedState and navigates to the success screen using GoRouter.push, passing the new expense ID
After navigation to the success screen, draft state in the BLoC is cleared (reset event dispatched or BLoC closed and re-provided)
If navigation to the success screen fails, the user remains on the summary view with an error message — no silent data loss
Screen reader announces 'Submitting your expense claim' when the loading state begins and 'Expense claim submitted successfully' on success
All monetary values in the summary are formatted using the locale-aware currency formatter (Norwegian krone, two decimal places)

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Dart
GoRouter
apis
ExpenseFormBloc (SubmitExpenseEvent, ExpenseFormSubmittedState)
GoRouter navigation
Organisation labels system
Intl (NumberFormat.currency for NOK)
data models
DraftExpenseClaim (read-only snapshot)
ExpenseFormState
ExpenseSummaryRow (label + value pair for display)
performance requirements
Summary view must render synchronously from already-loaded BLoC state — no async fetch on mount
Navigation to success screen must complete within one frame after state emission
security requirements
Draft state cleared from memory immediately after successful navigation; not persisted to disk
The success screen must not display the full expense claim details in a way that can be screenshot-scraped from the navigation stack
ui components
SummaryRowWidget (label/value pair)
ConfirmSubmitButton (loading-aware)
BackToEditTextButton
SemanticsAnnouncement wrapper

Execution Context

Execution Tier
Tier 5

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.

Component
Expense Registration Screen
ui high
Epic Risks (3)
medium impact medium prob dependency

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.

high impact medium prob technical

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.

high impact medium prob technical

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.