update api
This commit is contained in:
488
lib/features/auth/example_usage.dart
Normal file
488
lib/features/auth/example_usage.dart
Normal file
@@ -0,0 +1,488 @@
|
||||
// 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<RegisterExample> createState() => _RegisterExampleState();
|
||||
}
|
||||
|
||||
class _RegisterExampleState extends ConsumerState<RegisterExample> {
|
||||
final _nameController = TextEditingController();
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _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<bool>(
|
||||
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<void> _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),
|
||||
// use the service locator directly:
|
||||
|
||||
// import 'package:retail/core/di/injection_container.dart';
|
||||
// import 'package:retail/features/auth/domain/repositories/auth_repository.dart';
|
||||
|
||||
// final authRepository = sl<AuthRepository>();
|
||||
//
|
||||
// // Check if authenticated
|
||||
// final isAuthenticated = await authRepository.isAuthenticated();
|
||||
//
|
||||
// // Get token
|
||||
// final token = await authRepository.getAccessToken();
|
||||
//
|
||||
// print('Token: $token');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 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:
|
||||
// final response = await sl<DioClient>().get('/api/products');
|
||||
//
|
||||
// The above request will automatically include:
|
||||
// Headers: {
|
||||
// "Authorization": "Bearer <your-jwt-token>",
|
||||
// "Content-Type": "application/json",
|
||||
// "Accept": "application/json"
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user