critical priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

ActivityRegistrationBottomSheet opens as a modal bottom sheet using showModalBottomSheet with a DraggableScrollableSheet as the builder child
The sheet has an initial size of 0.6, minimum size of 0.4, and maximum size of 0.95 of screen height
A step progress indicator (linear or segmented, 5 segments) is visible at the top of the sheet content, updating when currentStep changes in Cubit state
The active step widget is determined by currentStep in Cubit state — no local step index variable in the container widget
Step transitions use AnimatedSwitcher with a combined slide + fade transition (implemented in task-009, but the container must scaffold the AnimatedSwitcher slot correctly)
A back button (chevron-left icon, not swipe gesture) is shown for steps 1-4; tapping it dispatches StepBackward event to the Cubit
A close button (X icon) is always visible; tapping it dismisses the bottom sheet via Navigator.pop after showing a discard-changes confirmation dialog if any fields have been filled
When Cubit state transitions to ActivityRegistrationSuccess, the sheet content is replaced with RegistrationConfirmationView without closing the sheet — sheet auto-closes after 2 seconds or on user tap
When Cubit state transitions to ActivityRegistrationError, a dismissible error snackbar or inline error message is displayed without navigating away from the current step
The BlocProvider for ActivityRegistrationCubit is scoped to this widget — it is created and closed here

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
BlocProvider
DraggableScrollableSheet
data models
ActivityRegistration
performance requirements
Sheet open animation must complete within 300ms
Step content swap must not cause a full rebuild of the BlocProvider subtree
security requirements
Cubit must be closed (BlocProvider autoDispose or explicit close in dispose) when sheet is dismissed to prevent memory leaks
ui components
DraggableScrollableSheet
StepProgressIndicator
RegistrationConfirmationView
ActivityRegistrationBottomSheet

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use BlocProvider(create: (context) => ActivityRegistrationCubit(registrationRepository: context.read())) at the root of the bottom sheet widget, not at the call site — this ensures proper scoping and disposal. Use BlocConsumer at the container level to handle success/error side effects (auto-close timer, error snackbar) while BlocBuilder handles step rendering. For the discard confirmation dialog, check whether any InProgress state field differs from defaults before showing — avoid showing it for a completely empty wizard. The DraggableScrollableSheet's child must be a ListView or SingleChildScrollView to allow content scrolling within the sheet.

Wrap the step slot in a ConstrainedBox with a minimum height to prevent layout jank when switching between steps of different heights.

Testing Requirements

Widget tests: verify sheet renders with correct initial step, verify back button absent on step 0 and present on steps 1-4, verify close button triggers discard dialog when fields filled, verify RegistrationConfirmationView appears when Cubit emits success state, verify BlocProvider is properly scoped (cubit not accessible outside sheet). Integration test: full happy path from open to confirmation. Golden test for the confirmation view layout.

Epic Risks (4)
high impact medium prob scope

As wizard steps accumulate additional features (duplicate warning, retroactive date chips, custom duration entry), the two-tap happy path may inadvertently require extra interactions. A step that previously auto-advanced may start requiring a confirmation tap, breaking the core promise of the feature and increasing friction for high-frequency users like HLF's 380-registration peer mentor.

Mitigation & Contingency

Mitigation: Define and automate a regression test that performs the complete two-tap happy path (open bottom sheet → confirm → confirm) and asserts the confirmation view is reached in exactly two tap events. Run this test in CI on every PR touching the wizard. Treat any failure as a blocking defect.

Contingency: If a new feature unavoidably adds a tap to the happy path, provide a 'quick mode' toggle in user settings that collapses the wizard to a single-confirmation screen for users who never change defaults.

medium impact medium prob technical

Flutter bottom sheets are dismissed on back-button press or background tap by default. If the wizard state is not preserved, a peer mentor who accidentally dismisses mid-flow loses all their entered data and must start over — a significant frustration for users with cognitive disabilities or motor impairments who take longer to fill each step.

Mitigation & Contingency

Mitigation: Implement the wizard state as a persistent Cubit that outlives the bottom sheet widget's lifecycle, scoped to the registration feature route. On re-open, the Cubit restores the previous step and field values. Add a 'discard changes?' confirmation dialog when the user explicitly dismisses a partially filled wizard.

Contingency: If persistent state proves difficult to implement with the chosen routing strategy, implement draft auto-save to a local draft repository every time a field value changes, and restore from draft on the next open.

high impact high prob technical

Multi-step wizard bottom sheets are among the most complex accessibility scenarios in Flutter. Screen readers (TalkBack, VoiceOver) may not announce step transitions, focus may land on the wrong element after advancing, and animated transitions can interfere with the accessibility tree update cycle — making the feature unusable for Blindeforbundet users who rely on screen readers.

Mitigation & Contingency

Mitigation: Assign each wizard step a unique Semantics container with a live region announcement on mount. Use ExcludeSemantics on inactive steps during transition animations. Test each step transition manually with TalkBack and VoiceOver as part of the definition of done for each step component.

Contingency: If animated transitions cause accessibility tree corruption, disable step transition animations entirely in accessibility mode (detected via MediaQuery.accessibleNavigation) and use instant step replacement instead.

medium impact medium prob dependency

The NotesStep relies on the OS keyboard's built-in dictation button for speech-to-text input. This button's availability, position, and behaviour varies significantly between iOS (reliable, visible dictation key) and Android (varies by keyboard, OEM skin, and language settings). HLF and Blindeforbundet specifically requested this capability; if it is unreliable on Android, it fails a SHOULD HAVE requirement for a significant portion of users.

Mitigation & Contingency

Mitigation: Document that the notes dictation feature depends on the device's native keyboard dictation and requires no in-app microphone permission. Add explicit placeholder copy informing users they can use their keyboard's dictation button. Test on a minimum of three Android OEM keyboards (Gboard, Samsung, Swiftkey) and two iOS versions.

Contingency: If native keyboard dictation is too unreliable on Android, implement a fallback in-app microphone button in the NotesStep that triggers the platform's SpeechRecognition API directly via a method channel, scoped only to the notes field with no session recording capability.