Skip to content

Latest commit

 

History

History
751 lines (604 loc) · 20.3 KB

File metadata and controls

751 lines (604 loc) · 20.3 KB

Onboarding to Registration Flow - Implementation Plan

📋 Executive Summary

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)


🎯 Project Objectives

Primary Goal

Redirect new users from onboarding completion directly to the registration flow to streamline the account creation process.

Secondary Goals

  • Improve user onboarding experience
  • Reduce friction in the registration funnel
  • Provide clear navigation between registration and login
  • Maintain backward compatibility for existing users

📊 Current System Analysis

Current Architecture

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

Key Files Involved

  1. lib/app.dart

    • Main app widget with routing logic
    • _completeOnboarding() callback
    • _onboardingCompleted state flag
    • Auth state consumer
  2. lib/presentation/screens/onboarding/onboarding_screen.dart

    • Onboarding slides (5 pages)
    • _completeOnboarding() method
    • SharedPreferences persistence
    • Callback to parent widget
  3. lib/presentation/screens/auth/login_screen.dart

    • Login form
    • Navigation to RegistrationFlowScreen (line 336)
    • "Create Account" button
  4. lib/presentation/screens/auth/registration_flow_screen.dart

    • Multi-step registration flow
    • Email → OTP → Profile → Account → Address → Phone → Review → Success

Current Issues

  1. ❌ New users must see login screen before registration
  2. ❌ Extra navigation step reduces conversion
  3. ❌ Inconsistent user journey (onboarding teaches features, then shows login)
  4. ❌ No direct path from onboarding to registration

🛠️ Implementation Tasks

Task 1: Update App Routing Logic

File: lib/app.dart
Priority: HIGH
Estimated Time: 30 minutes

Changes Required:

  1. Add new state variable to track if user came from onboarding
  2. Modify routing logic to show registration screen after onboarding
  3. Provide navigation between registration and login screens

Implementation Steps:

// 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;
      });
    },
  );
}

Testing Checklist:

  • 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

Task 2: Add Navigation Callbacks to RegistrationFlowScreen

File: lib/presentation/screens/auth/registration_flow_screen.dart
Priority: HIGH
Estimated Time: 20 minutes

Changes Required:

  1. Add optional onNavigateToLogin callback parameter
  2. Update success page to use callback or default navigation
  3. Add "Already have an account?" link on first page

Implementation Steps:

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'),
)

Testing Checklist:

  • Registration success navigates correctly
  • "Already registered" link works from first page
  • Callback is properly invoked
  • Default navigation works when callback is null

Task 3: Update LoginScreen Navigation

File: lib/presentation/screens/auth/login_screen.dart
Priority: MEDIUM
Estimated Time: 15 minutes

Changes Required:

  1. Add optional onNavigateToRegistration callback parameter
  2. Update "Create Account" button to use callback
  3. Maintain backward compatibility

Implementation Steps:

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()),
    );
  }
},

Testing Checklist:

  • "Create Account" button navigates correctly
  • Callback is invoked when provided
  • Default navigation works when callback is null
  • Back button behavior is correct

Task 4: Update OnboardingScreen UI/UX

File: lib/presentation/screens/onboarding/onboarding_screen.dart
Priority: LOW
Estimated Time: 15 minutes

Changes Required:

  1. Update final page button text from "Get Started" to "Create Account"
  2. Add "Already registered? Login" text button
  3. Update skip button to route to registration

Implementation Steps:

// 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'),
    ),
  ),

Testing Checklist:

  • Final page button shows "Create Account"
  • "Already registered?" link appears on final page
  • Skip button routes to registration
  • Login link routes to login screen

Task 5: Add Skip-to-Login Flag Handling

File: lib/app.dart
Priority: LOW
Estimated Time: 10 minutes

Changes Required:

  1. Check for skip_to_login flag in SharedPreferences
  2. Route to login instead of registration when flag is set
  3. Clear flag after use

Implementation Steps:

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');
  }
}

Testing Checklist:

  • "Already registered" from onboarding routes to login
  • Flag is cleared after use
  • Normal flow routes to registration

📈 System Improvements

Improvement 1: Named Routes Implementation

Priority: MEDIUM
Impact: High (Better maintainability)

Current Issue

  • Direct widget instantiation makes navigation complex
  • Difficult to pass parameters between screens
  • Hard to implement deep linking

Proposed Solution

// 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(),
  },
)

Benefits

  • ✅ Centralized route management
  • ✅ Easier deep linking support
  • ✅ Type-safe navigation
  • ✅ Better testing capabilities

Improvement 2: Route Guards / Navigation Service

Priority: MEDIUM
Impact: High (Better auth flow control)

Proposed Solution

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);
    }
  }
}

Benefits

  • ✅ Centralized navigation logic
  • ✅ Testable navigation
  • ✅ Consistent navigation behavior
  • ✅ Easier to implement navigation guards

Improvement 3: Authentication State Machine

Priority: LOW
Impact: Medium (Better state management)

Proposed Solution

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();
  }
}

Benefits

  • ✅ Clear state transitions
  • ✅ Predictable behavior
  • ✅ Easier testing
  • ✅ Better error handling

Improvement 4: Onboarding Analytics Tracking

Priority: LOW
Impact: Medium (Better insights)

Proposed Solution

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');
  }
}

Benefits

  • ✅ Track user behavior
  • ✅ Identify drop-off points
  • ✅ Measure conversion rates
  • ✅ Data-driven improvements

Improvement 5: Dynamic Onboarding Content

Priority: LOW
Impact: Low (Better personalization)

Proposed Solution

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;
  }
}

Benefits

  • ✅ Update content without app updates
  • ✅ A/B testing capabilities
  • ✅ Localized content
  • ✅ Version-based onboarding

🧪 Testing Strategy

Unit Tests

// 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 Tests

// 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);
});

Manual Test Cases

Test Case 1: New User Onboarding Flow

  1. ✅ Install fresh app
  2. ✅ View onboarding slides (1-5)
  3. ✅ Tap "Create Account" on final slide
  4. ✅ Verify registration screen appears
  5. ✅ Complete registration
  6. ✅ Verify login screen appears
  7. ✅ Login successfully
  8. ✅ Verify home screen appears

Test Case 2: Skip Onboarding Flow

  1. ✅ Install fresh app
  2. ✅ Tap "Skip" on onboarding
  3. ✅ Verify registration screen appears
  4. ✅ Navigate back
  5. ✅ Verify app behavior

Test Case 3: Existing User Flow

  1. ✅ Open app with onboarding already completed
  2. ✅ Verify onboarding is skipped
  3. ✅ Verify login/home screen appears based on auth state

Test Case 4: Login from Onboarding

  1. ✅ Install fresh app
  2. ✅ Navigate to last onboarding page
  3. ✅ Tap "Already have an account? Login"
  4. ✅ Verify login screen appears
  5. ✅ Login successfully
  6. ✅ Verify home screen appears

📅 Implementation Timeline

Phase 1: Core Routing Changes (Day 1)

  • ✅ 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

Phase 2: UI/UX Improvements (Day 1)

  • ✅ 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

Phase 3: System Improvements (Optional - Day 2-3)

  • ⏳ 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


🚀 Deployment Checklist

Pre-Deployment

  • All tasks completed and tested
  • Unit tests pass
  • Integration tests pass
  • Manual testing completed
  • Code review approved
  • Documentation updated

Deployment

  • Merge to Build-apk branch
  • Build APK: flutter build apk --release
  • Test APK on physical device
  • Create release notes
  • Tag release in Git

Post-Deployment

  • Monitor crash reports
  • Track analytics (onboarding completion rate)
  • Gather user feedback
  • Document any issues

📊 Success Metrics

Key Performance Indicators (KPIs)

  1. Onboarding Completion Rate

    • Target: > 80% of users complete onboarding
  2. Registration Conversion Rate

    • Target: > 60% of users who complete onboarding start registration
  3. Registration Success Rate

    • Target: > 70% of users who start registration complete it
  4. Time to Registration

    • Target: < 5 minutes from app open to registration completion
  5. User Drop-off Points

    • Track: Which onboarding page users skip from
    • Track: Which registration step has highest abandonment

🔒 Risk Assessment

High Risk

  • Breaking existing user flows: Existing users who expect login screen
    • Mitigation: Add skip-to-login flag handling

Medium Risk

  • Navigation stack issues: Back button behavior confusion
    • Mitigation: Proper route management with pushAndRemoveUntil

Low Risk

  • SharedPreferences conflicts: Multiple flags causing issues
    • Mitigation: Clear documentation and flag cleanup logic

📝 Notes

Design Decisions

  1. Why registration over login?: New users are more valuable; guide them to account creation first
  2. Why keep login option?: Some users may have already registered via web/other device
  3. Why use callbacks?: More flexible than direct navigation, easier to test

Future Considerations

  1. Consider adding email verification before allowing login
  2. Add social sign-in options (Google, Facebook, Apple)
  3. Implement deep linking for registration invites
  4. Add multi-language support for onboarding
  5. Consider animated transitions between onboarding and registration

📚 Related Documents

  • REGISTRATION_FLOW_GUIDE.md - Detailed registration implementation
  • AUTH_FLOW_FIX_SUMMARY.md - Authentication flow fixes
  • LOGIN_REGISTRATION_VALIDATION_PLAN.md - Validation requirements
  • .github/copilot-instructions.md - Project architecture guidelines

👥 Stakeholders

  • 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