Developing Multi-Step Registration Wizard for Mobile App
Multi-step registration solves specific problem: user not ready to fill 15 fields at once. Wizard breaks process into sequential screens, reduces cognitive load and allows saving progress. But it creates technical tasks — state management between steps, step-by-step validation, recovery after interruption.
State architecture
Main error — store each step's data in separate ViewModel. On "back" navigation state lost, user re-enters data.
Correct: single RegistrationViewModel (or RegistrationStore in Redux/MVI) holds all wizard state:
data class RegistrationState(
val currentStep: RegistrationStep = RegistrationStep.PERSONAL_INFO,
val personalInfo: PersonalInfoForm = PersonalInfoForm(),
val contactDetails: ContactDetailsForm = ContactDetailsForm(),
val accountSetup: AccountSetupForm = AccountSetupForm(),
val isLoading: Boolean = false,
val error: String? = null
)
sealed class RegistrationStep {
object PersonalInfo : RegistrationStep()
object ContactDetails : RegistrationStep()
object AccountSetup : RegistrationStep()
object EmailVerification : RegistrationStep()
object Completed : RegistrationStep()
}
Each step — separate Composable/UIViewController getting state fragment and callbacks. Navigation managed from ViewModel via currentStep, not NavController directly.
Navigation between steps
On iOS — UIPageViewController (horizontal swipe) or custom ContainerViewController. In SwiftUI — TabView with .tabViewStyle(.page) and hidden indicators, or custom ZStack with slide animation.
In Jetpack Compose — AnimatedContent by currentStep:
AnimatedContent(
targetState = state.currentStep,
transitionSpec = {
slideInHorizontally { width -> width } + fadeIn() with
slideOutHorizontally { width -> -width } + fadeOut()
}
) { step ->
when (step) {
is RegistrationStep.PersonalInfo -> PersonalInfoStep(...)
is RegistrationStep.ContactDetails -> ContactDetailsStep(...)
// ...
}
}
"Back" button — not system back-button (though we handle it), explicit button in header transitions to previous step without data reset.
Progress indicator
Linear progress bar (LinearProgressIndicator) with animated fill — minimum. Additionally — step N of M, text current step name. Don't show steps user hasn't seen yet — only completed and current.
Step-by-step validation
Each step validated before moving to next. If user returns to previous step and changes data — re-validate dependent subsequent steps (e.g., phone changed — OTP invalid, email verification reset).
// iOS — validation before transition
func proceedToNextStep() {
guard case .success = validateCurrentStep() else {
showValidationErrors()
return
}
currentStep = currentStep.next
}
Progress recovery
If user closed app on step 3 of 5 — what happens on return? Options:
- Reset: simplest, fits short wizard (2–3 steps).
-
Recover: save
RegistrationStatetoUserDefaults/Room/Core Data (no sensitive data — don't save password). On startup check for incomplete registration. - Server stores progress: create draft on first step, patch draft each step. On any device user continues from same place.
For large onboarding forms (entity verification, medical data) — third option mandatory.
Email/Phone verification within wizard
OTP step — separate screen with code field. Countdown timer (60 seconds), "Resend" button appears after timeout. Auto-read OTP from SMS (iOS: .textContentType(.oneTimeCode), Android: SMS Retriever API via SmsRetrieverClient).
SMS Retriever on Android needs no READ_SMS permission — important for Play Store review. Works via app hash (11-symbol string token, generated from signing certificate), included in SMS text.
Completion
After last step — not immediately to main screen. "Welcome" screen with animation (Lottie or SF Symbols animation) and single CTA. Save "registration completed" state to UserDefaults — to not show onboarding again next startup.
Timeframe
Wizard 3–4 steps with validation, progress indicator, OTP verification, and progress recovery — 10–16 business days per platform. If cross-platform needed (React Native/Flutter) — 14–20 days. Server-side progress storage — separate estimate.







