// ignore_for_file: unused_local_variable, avoid_print /// Example usage of the authentication system /// /// This file demonstrates how to use the authentication feature /// in your Flutter app. Copy the patterns shown here into your /// actual implementation. import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'presentation/providers/auth_provider.dart'; // ============================================================================ // EXAMPLE 1: Login Flow // ============================================================================ class LoginExample extends ConsumerWidget { final TextEditingController emailController; final TextEditingController passwordController; const LoginExample({ super.key, required this.emailController, required this.passwordController, }); @override Widget build(BuildContext context, WidgetRef ref) { // Watch auth state final authState = ref.watch(authProvider); return Column( children: [ // Show loading indicator if (authState.isLoading) const CircularProgressIndicator(), // Login button ElevatedButton( onPressed: authState.isLoading ? null : () async { // Call login final success = await ref.read(authProvider.notifier).login( email: emailController.text.trim(), password: passwordController.text, ); // Handle result if (success) { // Login successful - navigate to home if (context.mounted) { Navigator.pushReplacementNamed(context, '/home'); } } else { // Login failed - show error final error = ref.read(authProvider).errorMessage; if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(error ?? 'Login failed'), backgroundColor: Colors.red, ), ); } } }, child: const Text('Login'), ), ], ); } } // ============================================================================ // EXAMPLE 2: Register Flow // ============================================================================ class RegisterExample extends ConsumerStatefulWidget { const RegisterExample({super.key}); @override ConsumerState createState() => _RegisterExampleState(); } class _RegisterExampleState extends ConsumerState { final _nameController = TextEditingController(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); @override void dispose() { _nameController.dispose(); _emailController.dispose(); _passwordController.dispose(); super.dispose(); } Future _handleRegister() async { final success = await ref.read(authProvider.notifier).register( name: _nameController.text.trim(), email: _emailController.text.trim(), password: _passwordController.text, roles: ['user'], // Optional, defaults to ['user'] ); if (!mounted) return; if (success) { // Registration successful Navigator.pushReplacementNamed(context, '/home'); } else { // Registration failed final error = ref.read(authProvider).errorMessage; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(error ?? 'Registration failed'), backgroundColor: Colors.red, ), ); } } @override Widget build(BuildContext context) { final authState = ref.watch(authProvider); return ElevatedButton( onPressed: authState.isLoading ? null : _handleRegister, child: const Text('Register'), ); } } // ============================================================================ // EXAMPLE 3: Check Authentication Status // ============================================================================ class AuthStatusExample extends ConsumerWidget { const AuthStatusExample({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // Method 1: Watch entire auth state final authState = ref.watch(authProvider); // Method 2: Use convenience providers final isAuthenticated = ref.watch(isAuthenticatedProvider); final currentUser = ref.watch(currentUserProvider); return Column( children: [ // Check if authenticated if (authState.isAuthenticated) ...[ Text('Welcome ${authState.user?.name}!'), Text('Email: ${authState.user?.email}'), Text('Roles: ${authState.user?.roles.join(", ")}'), // Check user roles if (currentUser?.isAdmin ?? false) const Text('You are an admin'), if (currentUser?.isManager ?? false) const Text('You are a manager'), if (currentUser?.isCashier ?? false) const Text('You are a cashier'), ] else ...[ const Text('Not authenticated'), ], ], ); } } // ============================================================================ // EXAMPLE 4: Protected Route Guard // ============================================================================ class AuthGuard extends ConsumerWidget { final Widget child; const AuthGuard({super.key, required this.child}); @override Widget build(BuildContext context, WidgetRef ref) { final isAuthenticated = ref.watch(isAuthenticatedProvider); final isLoading = ref.watch(authProvider.select((s) => s.isLoading)); // Show loading while checking auth status if (isLoading) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } // If not authenticated, show login page if (!isAuthenticated) { return const Scaffold( body: Center( child: Text('Please login to continue'), ), ); // In real app: return const LoginPage(); } // User is authenticated, show protected content return child; } } // Usage in main app: // MaterialApp( // home: AuthGuard( // child: HomePage(), // ), // ); // ============================================================================ // EXAMPLE 5: Logout // ============================================================================ class LogoutExample extends ConsumerWidget { const LogoutExample({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton( onPressed: () async { // Show confirmation dialog final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Logout'), content: const Text('Are you sure you want to logout?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Cancel'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('Logout'), ), ], ), ); if (confirmed == true) { // Perform logout await ref.read(authProvider.notifier).logout(); // Navigate to login if (context.mounted) { Navigator.pushReplacementNamed(context, '/login'); } } }, child: const Text('Logout'), ); } } // ============================================================================ // EXAMPLE 6: Get Profile (Refresh User Data) // ============================================================================ class ProfileExample extends ConsumerWidget { const ProfileExample({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentUser = ref.watch(currentUserProvider); return Column( children: [ if (currentUser != null) ...[ Text('Name: ${currentUser.name}'), Text('Email: ${currentUser.email}'), Text('Roles: ${currentUser.roles.join(", ")}'), ], // Refresh profile button ElevatedButton( onPressed: () async { await ref.read(authProvider.notifier).getProfile(); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Profile refreshed')), ); } }, child: const Text('Refresh Profile'), ), ], ); } } // ============================================================================ // EXAMPLE 7: Refresh Token // ============================================================================ class RefreshTokenExample extends ConsumerWidget { const RefreshTokenExample({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton( onPressed: () async { final success = await ref.read(authProvider.notifier).refreshToken(); if (!context.mounted) return; if (success) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Token refreshed successfully')), ); } else { // Token refresh failed - user was logged out ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Session expired. Please login again.'), backgroundColor: Colors.red, ), ); Navigator.pushReplacementNamed(context, '/login'); } }, child: const Text('Refresh Token'), ); } } // ============================================================================ // EXAMPLE 8: Role-Based UI // ============================================================================ class RoleBasedUIExample extends ConsumerWidget { const RoleBasedUIExample({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentUser = ref.watch(currentUserProvider); return Column( children: [ // Show to all authenticated users const Text('Dashboard'), // Show only to admins if (currentUser?.isAdmin ?? false) ...[ const Text('Admin Panel'), ElevatedButton( onPressed: () { // Navigate to admin panel }, child: const Text('Manage Users'), ), ], // Show only to managers if (currentUser?.isManager ?? false) ...[ const Text('Manager Tools'), ElevatedButton( onPressed: () { // Navigate to manager tools }, child: const Text('View Reports'), ), ], // Show only to cashiers if (currentUser?.isCashier ?? false) ...[ const Text('POS Terminal'), ElevatedButton( onPressed: () { // Navigate to POS }, child: const Text('Start Transaction'), ), ], ], ); } } // ============================================================================ // EXAMPLE 9: Error Handling // ============================================================================ class ErrorHandlingExample extends ConsumerWidget { const ErrorHandlingExample({super.key}); Future _handleLogin(BuildContext context, WidgetRef ref) async { final success = await ref.read(authProvider.notifier).login( email: 'test@example.com', password: 'password', ); if (!context.mounted) return; if (!success) { final error = ref.read(authProvider).errorMessage; // Different error messages result in different UI feedback String userMessage; Color backgroundColor; if (error?.contains('Invalid email or password') ?? false) { userMessage = 'Incorrect email or password. Please try again.'; backgroundColor = Colors.red; } else if (error?.contains('Network') ?? false) { userMessage = 'No internet connection. Please check your network.'; backgroundColor = Colors.orange; } else if (error?.contains('Server') ?? false) { userMessage = 'Server error. Please try again later.'; backgroundColor = Colors.red[700]!; } else { userMessage = error ?? 'Login failed. Please try again.'; backgroundColor = Colors.red; } ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(userMessage), backgroundColor: backgroundColor, action: SnackBarAction( label: 'Retry', textColor: Colors.white, onPressed: () => _handleLogin(context, ref), ), ), ); } } @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton( onPressed: () => _handleLogin(context, ref), child: const Text('Login with Error Handling'), ); } } // ============================================================================ // EXAMPLE 10: Using Auth in Non-Widget Code // ============================================================================ void nonWidgetExample() { // If you need to access auth outside widgets (e.g., in services), // you can pass WidgetRef as a parameter or use ProviderContainer: // Method 1: Pass WidgetRef as parameter // Future myService(WidgetRef ref) async { // final authRepository = ref.read(authRepositoryProvider); // final isAuthenticated = await authRepository.isAuthenticated(); // print('Is authenticated: $isAuthenticated'); // } // Method 2: Use ProviderContainer (for non-Flutter code) // final container = ProviderContainer(); // final authRepository = container.read(authRepositoryProvider); // final isAuthenticated = await authRepository.isAuthenticated(); // container.dispose(); // Don't forget to dispose! } // ============================================================================ // EXAMPLE 11: Automatic Token Injection Test // ============================================================================ void tokenInjectionExample() { // Once logged in, all API requests automatically include the JWT token: // // The DioClient interceptor adds this header to all requests: // Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... // // You don't need to manually add the token - it's automatic! // Example of making an API call after login: // Using Riverpod: // final dioClient = ref.read(dioClientProvider); // final response = await dioClient.get('/api/products'); // // The above request will automatically include: // Headers: { // "Authorization": "Bearer ", // "Content-Type": "application/json", // "Accept": "application/json" // } }