This document outlines the plan to update the onboarding flow to route users to the Registration page instead of the Login page after completing onboarding. This change improves the user experience for new users by guiding them to create an account immediately after learning about the app.
Current Flow: Onboarding → Login Screen → Manual navigation to Registration
Target Flow: Onboarding → Registration Screen → Login (after successful registration)
Redirect new users from onboarding completion directly to the registration flow to streamline the account creation process.
- Improve user onboarding experience
- Reduce friction in the registration funnel
- Provide clear navigation between registration and login
- Maintain backward compatibility for existing users
App Start
↓
Onboarding Check (SharedPreferences)
↓ (not completed)
OnboardingScreen
↓ (_completeOnboarding callback)
app.dart → _completeOnboarding() → setState() → rebuild
↓
AuthProvider Check (isAuthenticated)
↓ (not authenticated)
LoginScreen
↓ (manual tap on "Create Account")
RegistrationFlowScreen
-
lib/app.dart
- Main app widget with routing logic
_completeOnboarding()callback_onboardingCompletedstate flag- Auth state consumer
-
lib/presentation/screens/onboarding/onboarding_screen.dart
- Onboarding slides (5 pages)
_completeOnboarding()method- SharedPreferences persistence
- Callback to parent widget
-
lib/presentation/screens/auth/login_screen.dart
- Login form
- Navigation to RegistrationFlowScreen (line 336)
- "Create Account" button
-
lib/presentation/screens/auth/registration_flow_screen.dart
- Multi-step registration flow
- Email → OTP → Profile → Account → Address → Phone → Review → Success
- ❌ New users must see login screen before registration
- ❌ Extra navigation step reduces conversion
- ❌ Inconsistent user journey (onboarding teaches features, then shows login)
- ❌ No direct path from onboarding to registration
File: lib/app.dart
Priority: HIGH
Estimated Time: 30 minutes
- Add new state variable to track if user came from onboarding
- Modify routing logic to show registration screen after onboarding
- Provide navigation between registration and login screens
// Add new state variable
bool _showRegistrationAfterOnboarding = false;
// Update _completeOnboarding method
void _completeOnboarding() {
setState(() {
_onboardingCompleted = true;
_showRegistrationAfterOnboarding = true; // NEW FLAG
});
}
// Update build method routing logic
if (!_onboardingCompleted) {
return OnboardingScreen(onCompleted: _completeOnboarding);
}
// NEW: Check if should show registration after onboarding
if (_showRegistrationAfterOnboarding && !authProvider.isAuthenticated) {
return RegistrationFlowScreen(
onNavigateToLogin: () {
setState(() {
_showRegistrationAfterOnboarding = false;
});
},
);
}
// Show login screen if not authenticated
if (!authProvider.isAuthenticated) {
return LoginScreen(
onNavigateToRegistration: () {
setState(() {
_showRegistrationAfterOnboarding = true;
});
},
);
}- Fresh install routes to onboarding
- Onboarding completion routes to registration
- "Skip" button on onboarding routes to registration
- Registration success routes to login
- Login "Create Account" button routes to registration
- Existing users bypass onboarding to login
File: lib/presentation/screens/auth/registration_flow_screen.dart
Priority: HIGH
Estimated Time: 20 minutes
- Add optional
onNavigateToLogincallback parameter - Update success page to use callback or default navigation
- Add "Already have an account?" link on first page
class RegistrationFlowScreen extends StatefulWidget {
final VoidCallback? onNavigateToLogin; // NEW PARAMETER
const RegistrationFlowScreen({
super.key,
this.onNavigateToLogin,
});
}
// In success page navigation:
if (widget.onNavigateToLogin != null) {
widget.onNavigateToLogin!();
} else {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (_) => const LoginScreen()),
(route) => false,
);
}
// Add "Already registered?" link on EmailPage
TextButton(
onPressed: () {
if (widget.onNavigateToLogin != null) {
widget.onNavigateToLogin!();
} else {
Navigator.pop(context);
}
},
child: Text('Already have an account? Login'),
)- Registration success navigates correctly
- "Already registered" link works from first page
- Callback is properly invoked
- Default navigation works when callback is null
File: lib/presentation/screens/auth/login_screen.dart
Priority: MEDIUM
Estimated Time: 15 minutes
- Add optional
onNavigateToRegistrationcallback parameter - Update "Create Account" button to use callback
- Maintain backward compatibility
class LoginScreen extends StatefulWidget {
final VoidCallback? onNavigateToRegistration; // NEW PARAMETER
const LoginScreen({
super.key,
this.onNavigateToRegistration,
});
}
// Update "Create Account" button (around line 336):
onPressed: () {
if (widget.onNavigateToRegistration != null) {
widget.onNavigateToRegistration!();
} else {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const RegistrationFlowScreen()),
);
}
},- "Create Account" button navigates correctly
- Callback is invoked when provided
- Default navigation works when callback is null
- Back button behavior is correct
File: lib/presentation/screens/onboarding/onboarding_screen.dart
Priority: LOW
Estimated Time: 15 minutes
- Update final page button text from "Get Started" to "Create Account"
- Add "Already registered? Login" text button
- Update skip button to route to registration
// Update final page button text (line ~169):
Text(
_currentPage == _pages.length - 1
? 'Create Account' // Changed from 'Get Started'
: 'Next',
)
// Add login option at the bottom:
if (_currentPage == _pages.length - 1)
Padding(
padding: const EdgeInsets.only(top: 16),
child: TextButton(
onPressed: () {
// Save onboarding as completed but don't trigger registration
SharedPreferences.getInstance().then((prefs) {
prefs.setBool('onboarding_completed', true);
prefs.setBool('skip_to_login', true); // NEW FLAG
widget.onCompleted();
});
},
child: Text('Already have an account? Login'),
),
),- Final page button shows "Create Account"
- "Already registered?" link appears on final page
- Skip button routes to registration
- Login link routes to login screen
File: lib/app.dart
Priority: LOW
Estimated Time: 10 minutes
- Check for
skip_to_loginflag in SharedPreferences - Route to login instead of registration when flag is set
- Clear flag after use
void _checkOnboardingStatus() async {
final prefs = await SharedPreferences.getInstance();
final completed = prefs.getBool('onboarding_completed') ?? false;
final skipToLogin = prefs.getBool('skip_to_login') ?? false; // NEW CHECK
setState(() {
_onboardingCompleted = completed;
_onboardingChecked = true;
_showRegistrationAfterOnboarding = completed && !skipToLogin; // UPDATED LOGIC
});
// Clear the flag
if (skipToLogin) {
await prefs.remove('skip_to_login');
}
}- "Already registered" from onboarding routes to login
- Flag is cleared after use
- Normal flow routes to registration
Priority: MEDIUM
Impact: High (Better maintainability)
- Direct widget instantiation makes navigation complex
- Difficult to pass parameters between screens
- Hard to implement deep linking
// In app.dart or new routes.dart file:
class AppRoutes {
static const String splash = '/';
static const String onboarding = '/onboarding';
static const String login = '/login';
static const String registration = '/registration';
static const String home = '/home';
}
// In MaterialApp:
MaterialApp(
initialRoute: AppRoutes.splash,
routes: {
AppRoutes.splash: (context) => const SplashScreen(),
AppRoutes.onboarding: (context) => OnboardingScreen(onCompleted: ...),
AppRoutes.login: (context) => const LoginScreen(),
AppRoutes.registration: (context) => const RegistrationFlowScreen(),
AppRoutes.home: (context) => const HomeScreen(),
},
)- ✅ Centralized route management
- ✅ Easier deep linking support
- ✅ Type-safe navigation
- ✅ Better testing capabilities
Priority: MEDIUM
Impact: High (Better auth flow control)
class NavigationService {
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
static Future<dynamic> navigateTo(String route, {Object? arguments}) {
return navigatorKey.currentState!.pushNamed(route, arguments: arguments);
}
static Future<dynamic> replaceWith(String route, {Object? arguments}) {
return navigatorKey.currentState!.pushReplacementNamed(route, arguments: arguments);
}
static void goBack() {
return navigatorKey.currentState!.pop();
}
static Future<void> navigateBasedOnAuthState(AuthProvider authProvider) async {
final prefs = await SharedPreferences.getInstance();
final onboardingCompleted = prefs.getBool('onboarding_completed') ?? false;
if (!onboardingCompleted) {
navigateTo(AppRoutes.onboarding);
} else if (!authProvider.isAuthenticated) {
navigateTo(AppRoutes.registration);
} else {
replaceWith(AppRoutes.home);
}
}
}- ✅ Centralized navigation logic
- ✅ Testable navigation
- ✅ Consistent navigation behavior
- ✅ Easier to implement navigation guards
Priority: LOW
Impact: Medium (Better state management)
enum AuthState {
initial,
onboardingRequired,
registrationRequired,
loginRequired,
authenticated,
loading,
}
class AuthStateManager extends ChangeNotifier {
AuthState _state = AuthState.initial;
AuthState get state => _state;
Future<void> initialize() async {
_state = AuthState.loading;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final onboardingCompleted = prefs.getBool('onboarding_completed') ?? false;
final token = await _secureStorage.read(key: 'auth_token');
if (!onboardingCompleted) {
_state = AuthState.onboardingRequired;
} else if (token == null) {
_state = AuthState.registrationRequired;
} else {
_state = AuthState.authenticated;
}
notifyListeners();
}
void completeOnboarding() {
_state = AuthState.registrationRequired;
notifyListeners();
}
void navigateToLogin() {
_state = AuthState.loginRequired;
notifyListeners();
}
}- ✅ Clear state transitions
- ✅ Predictable behavior
- ✅ Easier testing
- ✅ Better error handling
Priority: LOW
Impact: Medium (Better insights)
class OnboardingAnalytics {
static void trackPageView(int pageIndex) {
// Firebase Analytics or custom analytics
Logger.info('Onboarding page viewed: $pageIndex');
}
static void trackSkip(int fromPage) {
Logger.info('Onboarding skipped from page: $fromPage');
}
static void trackCompletion() {
Logger.info('Onboarding completed');
}
static void trackNavigationToLogin() {
Logger.info('User navigated to login from onboarding');
}
static void trackNavigationToRegistration() {
Logger.info('User navigated to registration from onboarding');
}
}- ✅ Track user behavior
- ✅ Identify drop-off points
- ✅ Measure conversion rates
- ✅ Data-driven improvements
Priority: LOW
Impact: Low (Better personalization)
class OnboardingConfig {
static Future<List<OnboardingPageData>> getPages() async {
// Could fetch from remote config or local JSON
final remoteConfig = await RemoteConfig.instance;
return [
OnboardingPageData(
title: remoteConfig.getString('onboarding_page_1_title'),
description: remoteConfig.getString('onboarding_page_1_description'),
imagePath: remoteConfig.getString('onboarding_page_1_image'),
backgroundColor: Color(remoteConfig.getInt('onboarding_page_1_color')),
),
// ... more pages
];
}
static Future<bool> shouldShowOnboarding() async {
final prefs = await SharedPreferences.getInstance();
final completed = prefs.getBool('onboarding_completed') ?? false;
final appVersion = prefs.getString('onboarding_version');
final currentVersion = '1.0.0';
// Show onboarding if not completed or version changed
return !completed || appVersion != currentVersion;
}
}- ✅ Update content without app updates
- ✅ A/B testing capabilities
- ✅ Localized content
- ✅ Version-based onboarding
// test/presentation/screens/onboarding_test.dart
testWidgets('Onboarding completion routes to registration', (tester) async {
bool registrationCalled = false;
await tester.pumpWidget(
MaterialApp(
home: OnboardingScreen(
onCompleted: () => registrationCalled = true,
),
),
);
// Navigate to last page
for (int i = 0; i < 4; i++) {
await tester.tap(find.byKey(const Key('next_onboarding_button')));
await tester.pumpAndSettle();
}
// Tap "Create Account" button
await tester.tap(find.text('Create Account'));
await tester.pumpAndSettle();
expect(registrationCalled, true);
});// integration_test/onboarding_registration_flow_test.dart
testWidgets('Complete onboarding to registration flow', (tester) async {
app.main();
await tester.pumpAndSettle();
// Should show onboarding for new users
expect(find.byType(OnboardingScreen), findsOneWidget);
// Complete onboarding
for (int i = 0; i < 4; i++) {
await tester.tap(find.byKey(const Key('next_onboarding_button')));
await tester.pumpAndSettle();
}
await tester.tap(find.text('Create Account'));
await tester.pumpAndSettle();
// Should navigate to registration
expect(find.byType(RegistrationFlowScreen), findsOneWidget);
});- ✅ Install fresh app
- ✅ View onboarding slides (1-5)
- ✅ Tap "Create Account" on final slide
- ✅ Verify registration screen appears
- ✅ Complete registration
- ✅ Verify login screen appears
- ✅ Login successfully
- ✅ Verify home screen appears
- ✅ Install fresh app
- ✅ Tap "Skip" on onboarding
- ✅ Verify registration screen appears
- ✅ Navigate back
- ✅ Verify app behavior
- ✅ Open app with onboarding already completed
- ✅ Verify onboarding is skipped
- ✅ Verify login/home screen appears based on auth state
- ✅ Install fresh app
- ✅ Navigate to last onboarding page
- ✅ Tap "Already have an account? Login"
- ✅ Verify login screen appears
- ✅ Login successfully
- ✅ Verify home screen appears
- ✅ Task 1: Update App Routing Logic (30 min)
- ✅ Task 2: Add Navigation Callbacks to RegistrationFlowScreen (20 min)
- ✅ Task 3: Update LoginScreen Navigation (15 min)
- ✅ Testing: Core flow tests (30 min)
Total: 1.5 hours
- ✅ Task 4: Update OnboardingScreen UI/UX (15 min)
- ✅ Task 5: Add Skip-to-Login Flag Handling (10 min)
- ✅ Testing: UI interaction tests (20 min)
Total: 45 minutes
- ⏳ Improvement 1: Named Routes Implementation (2 hours)
- ⏳ Improvement 2: Route Guards / Navigation Service (2 hours)
- ⏳ Improvement 3: Authentication State Machine (1.5 hours)
- ⏳ Improvement 4: Onboarding Analytics Tracking (1 hour)
- ⏳ Improvement 5: Dynamic Onboarding Content (2 hours)
Total: 8.5 hours
- All tasks completed and tested
- Unit tests pass
- Integration tests pass
- Manual testing completed
- Code review approved
- Documentation updated
- Merge to Build-apk branch
- Build APK:
flutter build apk --release - Test APK on physical device
- Create release notes
- Tag release in Git
- Monitor crash reports
- Track analytics (onboarding completion rate)
- Gather user feedback
- Document any issues
-
Onboarding Completion Rate
- Target: > 80% of users complete onboarding
-
Registration Conversion Rate
- Target: > 60% of users who complete onboarding start registration
-
Registration Success Rate
- Target: > 70% of users who start registration complete it
-
Time to Registration
- Target: < 5 minutes from app open to registration completion
-
User Drop-off Points
- Track: Which onboarding page users skip from
- Track: Which registration step has highest abandonment
- Breaking existing user flows: Existing users who expect login screen
- Mitigation: Add skip-to-login flag handling
- Navigation stack issues: Back button behavior confusion
- Mitigation: Proper route management with pushAndRemoveUntil
- SharedPreferences conflicts: Multiple flags causing issues
- Mitigation: Clear documentation and flag cleanup logic
- Why registration over login?: New users are more valuable; guide them to account creation first
- Why keep login option?: Some users may have already registered via web/other device
- Why use callbacks?: More flexible than direct navigation, easier to test
- Consider adding email verification before allowing login
- Add social sign-in options (Google, Facebook, Apple)
- Implement deep linking for registration invites
- Add multi-language support for onboarding
- Consider animated transitions between onboarding and registration
REGISTRATION_FLOW_GUIDE.md- Detailed registration implementationAUTH_FLOW_FIX_SUMMARY.md- Authentication flow fixesLOGIN_REGISTRATION_VALIDATION_PLAN.md- Validation requirements.github/copilot-instructions.md- Project architecture guidelines
- Development Team: Implementation and testing
- UI/UX Team: Design review and user flow validation
- Product Team: Feature prioritization and success metrics
- QA Team: Testing and quality assurance
Document Version: 1.0
Last Updated: November 12, 2025
Status: Ready for Implementation
Branch: Build-apk