Authentication
DWallet includes a user authentication system that manages user identity, session state, and profile information.
Overview
Section titled “Overview”The authentication module handles:
- User identity management
- Session state persistence
- User profile data (name, email, currency, balance)
- Authentication state throughout the app
Architecture
Section titled “Architecture”AuthUserService
Section titled “AuthUserService”The core service managing user state:
class AuthUserService extends AsyncNotifier<User> { @override Future<User> build() async { final prefs = await SharedPreferences.getInstance(); return _loadUser(prefs); }
Future<void> updateUser(User newUser) async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { await _saveUser(newUser); return newUser; }); }
Future<void> updateCurrency(Currency currency) async { final currentUser = state.value!; await updateUser(currentUser.copyWith(currency: currency)); }
Future<void> updateInitialBalance(double balance) async { final currentUser = state.value!; await updateUser(currentUser.copyWith(initialBalance: balance)); }}Located in: lib/app/di/services/_auth_user_service.dart
User Model
Section titled “User Model”The User model contains:
class UserModel implements User { final String id; final String? fullName; final String? email; final double initialBalance; final Currency currency; final String? sessionToken; final bool hasCompletedOnboarding;
const UserModel({ required this.id, this.fullName, this.email, this.initialBalance = 0, required this.currency, this.sessionToken, this.hasCompletedOnboarding = false, });}Provider Usage
Section titled “Provider Usage”Watching User State
Section titled “Watching User State”class HomeView extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final userAsync = ref.watch(authUserServiceProvider);
return userAsync.when( data: (user) => Text('Welcome, ${user.fullName ?? 'Guest'}'), loading: () => CircularProgressIndicator(), error: (err, _) => Text('Error: $err'), ); }}Accessing Currency
Section titled “Accessing Currency”final currency = ref.watch(userCurrencyProvider);Text('${currency.symbol}100.00');Updating User Data
Section titled “Updating User Data”// Update currencyref.read(authUserServiceProvider.notifier).updateCurrency(CurrencyMock.eur);
// Update balanceref.read(authUserServiceProvider.notifier).updateInitialBalance(1000.00);Mock Data
Section titled “Mock Data”For development, mock users are available:
// Guest userfinal guest = UserMock.guest();
// Default USD userfinal usdUser = UserMock.withCurrency(CurrencyMock.usd);
// Custom userfinal customUser = UserModel( id: 'user_123', fullName: 'John Doe', email: 'john@example.com', currency: CurrencyMock.eur, initialBalance: 5000.00, hasCompletedOnboarding: true,);Integration with App Lock
Section titled “Integration with App Lock”The authentication system works alongside the security features:
- User state persists across app restarts
- App Lock provides additional security layer
- Biometric auth can be enabled per-user preference
Route Protection
Section titled “Route Protection”Routes are protected using OnboardRouteGuard:
AutoRoute( page: ClientRoute.page, path: '/client', guards: [OnboardRouteGuard], children: [...],)Session Management
Section titled “Session Management”Currently, DWallet uses a simplified session model suitable for a single-user offline app:
// Check if user has sessionfinal hasSession = user.sessionToken != null;
// In a full implementation, you would:// 1. Validate token on app start// 2. Refresh tokens as needed// 3. Handle session expirationExtending for Backend Integration
Section titled “Extending for Backend Integration”To connect to a real backend:
- Add API client to services:
class AuthApiService { final Dio _dio;
Future<User> login(String email, String password) async { final response = await _dio.post('/auth/login', data: { 'email': email, 'password': password, }); return UserModel.fromJson(response.data); }
Future<void> logout() async { await _dio.post('/auth/logout'); }}- Update AuthUserService:
class AuthUserService extends AsyncNotifier<User> { late final AuthApiService _api;
Future<void> login(String email, String password) async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() => _api.login(email, password) ); }
Future<void> logout() async { await _api.logout(); state = AsyncValue.data(UserMock.guest()); }}- Add login/logout UI in auth pages.
Best Practices
Section titled “Best Practices”- Always handle loading states with
AsyncValue.when() - Validate session on app start for real backends
- Secure storage for tokens (use
flutter_secure_storage) - Clear sensitive data on logout
- Handle errors gracefully with user-friendly messages