feat: Implement initial authentication, multi-environment support, and core app infrastructure.

This commit is contained in:
pradeepkumar
2026-02-23 19:23:30 +05:30
parent 0f07a16132
commit 4f8ad7fe49
55 changed files with 3836 additions and 118 deletions

View File

@@ -0,0 +1,159 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:real_estate_mobile/core/network/api_exceptions.dart';
import 'package:real_estate_mobile/features/auth/data/auth_repository.dart';
import 'package:real_estate_mobile/features/auth/data/models/register_request.dart';
import 'package:real_estate_mobile/features/auth/data/models/user_model.dart';
// Auth state
enum AuthStatus { initial, loading, success, error }
class AuthState {
final AuthStatus status;
final UserModel? user;
final String? errorMessage;
final List<FieldError> fieldErrors;
final String registeredEmail;
const AuthState({
this.status = AuthStatus.initial,
this.user,
this.errorMessage,
this.fieldErrors = const [],
this.registeredEmail = '',
});
AuthState copyWith({
AuthStatus? status,
UserModel? user,
String? errorMessage,
List<FieldError>? fieldErrors,
String? registeredEmail,
}) {
return AuthState(
status: status ?? this.status,
user: user ?? this.user,
errorMessage: errorMessage,
fieldErrors: fieldErrors ?? this.fieldErrors,
registeredEmail: registeredEmail ?? this.registeredEmail,
);
}
String? getFieldError(String fieldName) {
final match = fieldErrors.where((e) => e.field == fieldName);
if (match.isEmpty) return null;
return match.first.errors.isNotEmpty ? match.first.errors.first : null;
}
}
// Auth notifier
class AuthNotifier extends StateNotifier<AuthState> {
final AuthRepository _repository;
AuthNotifier(this._repository) : super(const AuthState());
Future<void> login({
required String email,
required String password,
}) async {
state = state.copyWith(
status: AuthStatus.loading,
errorMessage: null,
fieldErrors: [],
);
try {
final result = await _repository.login(
email: email,
password: password,
);
if (result['requiresTwoFactor'] == true) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: '2FA verification required',
);
return;
}
state = state.copyWith(
status: AuthStatus.success,
user: result['user'] as UserModel,
);
} on ApiException catch (e) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: e.fieldErrors.isNotEmpty
? 'Please fix the errors below'
: e.message,
fieldErrors: e.fieldErrors,
);
} catch (e) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: 'An unexpected error occurred',
);
}
}
Future<void> register({
required String name,
required String email,
required String password,
required String role,
}) async {
state = state.copyWith(
status: AuthStatus.loading,
errorMessage: null,
fieldErrors: [],
);
// Split name into firstName and lastName
final nameParts = name.trim().split(RegExp(r'\s+'));
final firstName = nameParts.first;
final lastName = nameParts.length > 1 ? nameParts.sublist(1).join(' ') : null;
try {
final user = await _repository.register(
RegisterRequest(
email: email,
password: password,
role: role,
firstName: firstName,
lastName: lastName,
),
);
state = state.copyWith(
status: AuthStatus.success,
user: user,
registeredEmail: email,
);
} on ApiException catch (e) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: e.fieldErrors.isNotEmpty
? 'Please fix the errors below'
: e.message,
fieldErrors: e.fieldErrors,
);
} catch (e) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: 'An unexpected error occurred',
);
}
}
void reset() {
state = const AuthState();
}
}
// Providers
final authRepositoryProvider = Provider<AuthRepository>((ref) {
return AuthRepository();
});
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
return AuthNotifier(ref.watch(authRepositoryProvider));
});