Registration Wizard — V2 Enhancements
Date: 2026-04-27 Branch: current dev branch Files changed:
lib/features/auth/screens/registration_wizard_screen.dartlib/features/auth/notifiers/registration_wizard_notifier.dartlib/shared/widgets/flow_date_wheel_picker.dartlib/core/services/notification_service.dartlib/main.dartlib/l10n/app_en.arb,lib/l10n/app_it.arb
What changed and why
1. Crash fix — BoxConstraints forces infinite height
Symptom: App crashed immediately on opening the registration screen.
Root cause: _buildTypeCard used Row(crossAxisAlignment: CrossAxisAlignment.stretch) inside a SingleChildScrollView, which propagates height = ∞. stretch requires a bounded cross-axis.
Fix: Wrapped the Row in IntrinsicHeight. This performs a two-pass layout: first it measures natural child heights, then supplies that bounded max to the row so stretch works.
2. Firebase guard — no-app errors in DB
Symptom: [core/no-app] Firebase exceptions appearing in app_issues when running without google-services.json.
Fix: Added static bool _firebaseAvailable flag to NotificationService. Set only after successful Firebase.initializeApp() in main.dart. All FCM calls (getToken, unregisterToken) are gated behind this flag.
3. Date picker — keyboard & TV remote support
Widget: FlowDateWheelPicker (lib/shared/widgets/flow_date_wheel_picker.dart)
Two modes toggled by a top-right button:
- Wheel mode (default): Three
ListWheelScrollViewcolumns (DD / MMM / YYYY). Each wrapped in aFocusnode. Arrow Up/Down scrolls the column; Arrow Right/Tab moves focus to the next column; Arrow Left goes back. Focused column shows coral border. - Keyboard mode: Three
TextFieldfields withFilteringTextInputFormatter.digitsOnly. Day and month auto-advance focus after 2 digits.
4. Step-by-step validation + uniqueness checks
Notifier: validateThenCall(AppLocalizations l10n, void Function() onValid)
Called by every “Continua” button. Flow:
- Sets
isValidating = true, clearsfieldError - Runs
_validateCurrentStep(l10n)— async so it can hit Supabase - If error → sets
fieldError, clearsisValidating, returns (CTA re-enables) - If OK → clears
isValidating, callsonValid()
Screen: _buildWizardPage shows:
- Inline error banner (coral background,
Icons.error_outline_rounded) above CTA whenprovider.fieldError != null - Spinner instead of CTA when
provider.isValidating || provider.isSubmitting
Validation by step:
| Step | Check |
|---|---|
| Personal name | First name not empty |
| Birthday | Age >= 18 |
| Personal identity / username | Format (3–30 chars, [a-zA-Z0-9_.]) + Supabase uniqueness query on profiles.username |
| Creator identity | Stage name not empty |
| Business info | Business name + category not empty |
| Business location | Address not empty |
| Auth | Email format + password >= 8 chars |
| Submit | Re-validates auth; catches email already registered as fieldError (not SnackBar) |
Username uniqueness: queries profiles table with .maybeSingle(). Network errors are silently swallowed — uniqueness will surface later at submit if truly duplicate.
Email uniqueness: deferred to submit — Supabase Auth doesn’t expose a public email-check endpoint; duplicate is detected from the auth error message and shown as fieldError.
5. Smart tag ordering
Static method: RegistrationWizardNotifier.orderedTags(allTags, selectedNames, lastSelectedName)
Order: [selected (in selection order)] → [similar to last selected (same parentId, or same category)] → [rest sorted by usageCount desc]
“Similar” = same parentId if the tag has a parent, otherwise same category. This groups sibling tags (e.g. sub-genres under “Electronic”) near each other after selection.
Screen usage:
_buildPersonalVibeStep: passes all tags fromtagListProvider_buildCreatorGenreStep: pre-filters toTagCategory.musicfirst, then applies ordering
Both steps show a CircularProgressIndicator while tagState.isLoading && allTags.isEmpty, and fall back to hardcoded string chips if the DB returns empty.
6. AnimatedSwitcher sub-step transitions
Fix: KeyedSubtree(key: ValueKey('${step.name}_$pageIndex')) wrapping AnimatedSwitcher’s child. Without a key, two consecutive steps with the same widget type wouldn’t re-animate.
7. Auto-dispose wizard state
Changed registrationWizardNotifierProvider to StateNotifierProvider.autoDispose. State is reset every time the user navigates away, ensuring the welcome screen is always shown fresh on re-entry.
8. Tab focus escape from date wheel year column
Bug: Pressing Tab on the year column did nothing — KeyEventResult.handled was returned unconditionally, blocking Flutter’s built-in tab order.
Fix (flow_date_wheel_picker.dart _handleWheelKey): The Tab / ArrowRight handler now returns KeyEventResult.handled only when focus actually moves within the picker (day→month, month→year). When Tab is pressed on the year column it returns KeyEventResult.ignored, which lets Flutter advance focus naturally to the next widget in the tree (the “Continua” button).
9. Live username availability indicator
Trigger: User reported that no feedback was shown while typing a username — the only check was on “Continua” press.
New enum: UsernameCheckStatus { idle, checking, available, taken } in the notifier file.
New state field: RegistrationWizardState.usernameCheckStatus (default idle).
Notifier changes (registration_wizard_notifier.dart):
setUsername()now cancels any pendingTimer?(_usernameDebounce), resets status toidleimmediately, then schedules a 450 ms debounce that calls_runUsernameAvailabilityCheck()— only when the trimmed value is ≥ 3 chars and matches the valid pattern._runUsernameAvailabilityCheck()setschecking, queriesprofiles.usernamewith.maybeSingle(), then setsavailableortaken. Network failures silently reset toidle— the hard check on “Continua” is the authoritative guard.dispose()override cancels the timer to avoid state updates on disposed notifier.
Screen changes (registration_wizard_screen.dart):
_buildUsernameStatusIcon(UsernameCheckStatus)→Widget?: returns a 16 pxCircularProgressIndicatorforchecking, greencheck_circle_outline_roundedforavailable, coralcancel_outlinedfortaken, null foridle.- Wired as
suffixIconof the usernameTextFormField.
Key state fields added to RegistrationWizardState
final String? fieldError; // inline per-step error message
final bool isValidating; // async check in progress (CTA spinner)
final String? lastSelectedInterest; // drives tag similarity ordering
final UsernameCheckStatus usernameCheckStatus; // live availability (idle|checking|available|taken)l10n keys added
All in app_en.arb and app_it.arb:
wizardValidationFirstNameRequiredwizardValidationAgeRestrictionwizardValidationUsernameRequiredwizardValidationUsernameFormatwizardValidationUsernameTakenwizardValidationEmailRequiredwizardValidationEmailFormatwizardValidationPasswordTooShortwizardValidationBusinessNameRequiredwizardValidationBusinessCategoryRequiredwizardValidationStageNameRequiredwizardValidationLocationRequiredwizardValidationEmailTakendateInputDay,dateInputMonth,dateInputYeardateInputModeKeyboard,dateInputModeWheel