Implement Export Date Range Picker widget
epic-accounting-system-export-ui-task-001 — Build the ExportDateRangePicker Flutter widget with accessible date selection controls. Must support keyboard navigation, VoiceOver/TalkBack screen reader labels, and WCAG 2.2 AA contrast. Expose a callback with the selected DateTimeRange. Include preset options (current month, last month, custom range) and validate that start date is not after end date.
Acceptance Criteria
Technical Requirements
Implementation Notes
Use Flutter's built-in `showDateRangePicker()` for the custom range dialog — do not build a custom calendar from scratch. Wrap all interactive elements with `Semantics(label: ..., button: true)` widgets and use `ExcludeSemantics` for decorative elements. For preset computation, use the Dart `DateTime` class with UTC normalization to avoid DST edge cases: `DateTime.utc(now.year, now.month, 1)` for month start and `DateTime.utc(now.year, now.month + 1, 0)` for month end. Expose the widget's design through a clean API: `ExportDateRangePicker({Key?
key, DateTimeRange? initialRange, required void Function(DateTimeRange) onRangeSelected})`. Store preset selection state using a local `_selectedPreset` enum (`currentMonth`, `lastMonth`, `custom`) to drive the UI clearly. Use the app's existing AppTokens for colors — never hardcode hex values.
If BLoC is used at the parent screen level, emit an ExportRangeSelected event from the callback rather than calling a BLoC method directly from within this widget — keep the widget decoupled.
Testing Requirements
Write flutter_test widget tests covering: (1) renders all three preset options; (2) tapping 'Current month' fires callback with correct DateTimeRange for this month; (3) tapping 'Last month' fires callback with correct DateTimeRange for last month; (4) tapping 'Custom range' opens date picker dialog; (5) selecting valid custom range fires callback; (6) selecting custom range with start after end shows validation error and does NOT fire callback; (7) initialRange parameter pre-selects the matching preset or shows custom range with dates pre-filled; (8) Semantics test — use tester.getSemantics() to assert that all interactive elements have non-empty label properties; (9) contrast test — assert that all text colors from design tokens meet 4.5:1 ratio against their background tokens. Run tests with `flutter test` targeting both iOS and Android configurations.
Export operations may take several seconds, and the UI must handle all intermediate states (loading, partial success, failure, duplicate warning) without leaving the coordinator on a blank or unresponsive screen. Missing state handling causes confusion and potentially double-submissions.
Mitigation & Contingency
Mitigation: Design the BLoC state machine with explicit states for each transition before writing any widget code: ExportIdle, ExportDuplicateWarning, ExportInProgress, ExportSuccess, ExportPartialSuccess, ExportFailed. Each state maps to a distinct UI. Widget tests cover all states.
Contingency: If a loading state is missed in production, surface a generic error state with a retry action rather than leaving the UI stuck. Add a timeout on the Edge Function call (default 30 seconds) that transitions to ExportFailed with a user-readable message.
The custom Export Date Range Picker may not be fully navigable with VoiceOver if the underlying Flutter date widgets do not expose the correct semantic tree. This is a critical accessibility failure for Blindeforbundet users who rely on screen readers.
Mitigation & Contingency
Mitigation: Use Flutter's built-in DateRangePicker as the base and wrap with explicit Semantics nodes for start and end labels. Test with VoiceOver on a physical iOS device as part of the definition of done for this component. Reference the existing AccessibilityTestHarness pattern used elsewhere in the app.
Contingency: If the custom picker fails accessibility audit, replace it with two independent DatePicker fields (start and end) using Flutter's standard accessible date input, which has broader VoiceOver support than range variants.