āĻ¸ā§āĻ•āĻŋāĻĒ āĻ•āϰ⧇ āĻŽā§‚āϞ āĻ•āĻ¨ā§āĻŸā§‡āĻ¨ā§āϟ āĻ āϝāĻžāύ

🔐 Authentication Feature Example

FLX CLI āĻĻāĻŋāϝāĻŧ⧇ āϤ⧈āϰāĻŋ āĻšāĻ“āϝāĻŧāĻž āĻāĻ•āϟāĻŋ complete authentication feature-āĻāϰ boilerplate code exampleāĨ¤

🚀 Command to Generate​

flx gen feature auth

📁 Generated Structure​

lib/features/auth/
├── data/
│ ├── datasources/
│ │ └── auth_remote_data_source.dart
│ ├── models/
│ │ └── auth_model.dart
│ └── repositories/
│ └── auth_repository_impl.dart
├── domain/
│ ├── entities/
│ │ └── auth_entity.dart
│ ├── repositories/
│ │ └── auth_repository.dart
│ └── usecases/
│ └── auth_usecase.dart
└── presentation/
├── bindings/
│ └── auth_binding.dart
├── controllers/ # GetX
│ └── auth_controller.dart
├── bloc/ # BLoC (alternative)
│ ├── auth_bloc.dart
│ ├── auth_event.dart
│ └── auth_state.dart
└── pages/
└── auth_page.dart

đŸ—ī¸ Domain Layer​

Auth Entity​

class AuthEntity {
final String id;
final String name;
final String email;
final String? profilePicture;
final bool isEmailVerified;
final DateTime createdAt;
final DateTime lastLoginAt;

const AuthEntity({
required this.id,
required this.name,
required this.email,
this.profilePicture,
required this.isEmailVerified,
required this.createdAt,
required this.lastLoginAt,
});

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is AuthEntity &&
other.id == id &&
other.name == name &&
other.email == email &&
other.profilePicture == profilePicture &&
other.isEmailVerified == isEmailVerified &&
other.createdAt == createdAt &&
other.lastLoginAt == lastLoginAt;
}

@override
int get hashCode {
return id.hashCode ^
name.hashCode ^
email.hashCode ^
profilePicture.hashCode ^
isEmailVerified.hashCode ^
createdAt.hashCode ^
lastLoginAt.hashCode;
}

@override
String toString() {
return 'AuthEntity(id: $id, name: $name, email: $email, profilePicture: $profilePicture, isEmailVerified: $isEmailVerified, createdAt: $createdAt, lastLoginAt: $lastLoginAt)';
}
}

Auth Repository Interface​

import '../entities/auth_entity.dart';

abstract class AuthRepository {
/// User login with email and password
Future<AuthEntity> login({
required String email,
required String password,
});

/// User registration
Future<AuthEntity> register({
required String name,
required String email,
required String password,
});

/// Logout current user
Future<void> logout();

/// Get current user from local storage
Future<AuthEntity?> getCurrentUser();

/// Check if user is logged in
Future<bool> isLoggedIn();

/// Refresh access token
Future<String> refreshToken();

/// Send password reset email
Future<void> sendPasswordResetEmail(String email);

/// Verify email with OTP
Future<void> verifyEmail(String otp);

/// Update user profile
Future<AuthEntity> updateProfile({
String? name,
String? profilePicture,
});

/// Change password
Future<void> changePassword({
required String currentPassword,
required String newPassword,
});

/// Delete account
Future<void> deleteAccount();
}

Auth Use Case​

import '../entities/auth_entity.dart';
import '../repositories/auth_repository.dart';

class AuthUseCase {
final AuthRepository _repository;

AuthUseCase(this._repository);

/// Login use case
Future<AuthEntity> login({
required String email,
required String password,
}) async {
// Email validation
if (!_isValidEmail(email)) {
throw Exception('Invalid email format');
}

// Password validation
if (password.length < 6) {
throw Exception('Password must be at least 6 characters');
}

try {
final user = await _repository.login(
email: email.trim().toLowerCase(),
password: password,
);
return user;
} catch (e) {
throw Exception('Login failed: ${e.toString()}');
}
}

/// Register use case
Future<AuthEntity> register({
required String name,
required String email,
required String password,
required String confirmPassword,
}) async {
// Name validation
if (name.trim().isEmpty) {
throw Exception('Name is required');
}

// Email validation
if (!_isValidEmail(email)) {
throw Exception('Invalid email format');
}

// Password validation
if (password.length < 6) {
throw Exception('Password must be at least 6 characters');
}

// Confirm password validation
if (password != confirmPassword) {
throw Exception('Passwords do not match');
}

try {
final user = await _repository.register(
name: name.trim(),
email: email.trim().toLowerCase(),
password: password,
);
return user;
} catch (e) {
throw Exception('Registration failed: ${e.toString()}');
}
}

/// Logout use case
Future<void> logout() async {
try {
await _repository.logout();
} catch (e) {
throw Exception('Logout failed: ${e.toString()}');
}
}

/// Get current user use case
Future<AuthEntity?> getCurrentUser() async {
try {
return await _repository.getCurrentUser();
} catch (e) {
return null;
}
}

/// Check if logged in use case
Future<bool> isLoggedIn() async {
try {
return await _repository.isLoggedIn();
} catch (e) {
return false;
}
}

/// Send password reset email use case
Future<void> sendPasswordResetEmail(String email) async {
if (!_isValidEmail(email)) {
throw Exception('Invalid email format');
}

try {
await _repository.sendPasswordResetEmail(email.trim().toLowerCase());
} catch (e) {
throw Exception('Failed to send reset email: ${e.toString()}');
}
}

/// Verify email use case
Future<void> verifyEmail(String otp) async {
if (otp.trim().isEmpty) {
throw Exception('OTP is required');
}

if (otp.length != 6) {
throw Exception('OTP must be 6 digits');
}

try {
await _repository.verifyEmail(otp);
} catch (e) {
throw Exception('Email verification failed: ${e.toString()}');
}
}

/// Private method to validate email
bool _isValidEmail(String email) {
return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email);
}
}

📊 Data Layer​

Auth Model​

import '../../domain/entities/auth_entity.dart';

class AuthModel extends AuthEntity {
const AuthModel({
required super.id,
required super.name,
required super.email,
super.profilePicture,
required super.isEmailVerified,
required super.createdAt,
required super.lastLoginAt,
});

/// Create AuthModel from JSON
factory AuthModel.fromJson(Map<String, dynamic> json) {
return AuthModel(
id: json['id'] as String,
name: json['name'] as String,
email: json['email'] as String,
profilePicture: json['profile_picture'] as String?,
isEmailVerified: json['is_email_verified'] as bool? ?? false,
createdAt: DateTime.parse(json['created_at'] as String),
lastLoginAt: json['last_login_at'] != null
? DateTime.parse(json['last_login_at'] as String)
: DateTime.now(),
);
}

/// Convert AuthModel to JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'profile_picture': profilePicture,
'is_email_verified': isEmailVerified,
'created_at': createdAt.toIso8601String(),
'last_login_at': lastLoginAt.toIso8601String(),
};
}

/// Create a copy with updated fields
AuthModel copyWith({
String? id,
String? name,
String? email,
String? profilePicture,
bool? isEmailVerified,
DateTime? createdAt,
DateTime? lastLoginAt,
}) {
return AuthModel(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
profilePicture: profilePicture ?? this.profilePicture,
isEmailVerified: isEmailVerified ?? this.isEmailVerified,
createdAt: createdAt ?? this.createdAt,
lastLoginAt: lastLoginAt ?? this.lastLoginAt,
);
}

@override
String toString() {
return 'AuthModel(id: $id, name: $name, email: $email, profilePicture: $profilePicture, isEmailVerified: $isEmailVerified, createdAt: $createdAt, lastLoginAt: $lastLoginAt)';
}
}

Auth Remote Data Source​

import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/auth_model.dart';

abstract class AuthRemoteDataSource {
Future<AuthModel> login({
required String email,
required String password,
});

Future<AuthModel> register({
required String name,
required String email,
required String password,
});

Future<void> logout();
Future<String> refreshToken();
Future<void> sendPasswordResetEmail(String email);
Future<void> verifyEmail(String otp);
Future<AuthModel> updateProfile({
String? name,
String? profilePicture,
});
Future<void> changePassword({
required String currentPassword,
required String newPassword,
});
Future<void> deleteAccount();
}

class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
final http.Client client;
final String baseUrl;

AuthRemoteDataSourceImpl({
required this.client,
required this.baseUrl,
});

@override
Future<AuthModel> login({
required String email,
required String password,
}) async {
final response = await client.post(
Uri.parse('$baseUrl/auth/login'),
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'email': email,
'password': password,
}),
);

if (response.statusCode == 200) {
final data = json.decode(response.body);
return AuthModel.fromJson(data['user']);
} else if (response.statusCode == 401) {
throw Exception('Invalid credentials');
} else if (response.statusCode == 404) {
throw Exception('User not found');
} else {
throw Exception('Login failed: ${response.body}');
}
}

@override
Future<AuthModel> register({
required String name,
required String email,
required String password,
}) async {
final response = await client.post(
Uri.parse('$baseUrl/auth/register'),
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'name': name,
'email': email,
'password': password,
}),
);

if (response.statusCode == 201) {
final data = json.decode(response.body);
return AuthModel.fromJson(data['user']);
} else if (response.statusCode == 409) {
throw Exception('Email already exists');
} else {
throw Exception('Registration failed: ${response.body}');
}
}

@override
Future<void> logout() async {
final response = await client.post(
Uri.parse('$baseUrl/auth/logout'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${await _getAccessToken()}',
},
);

if (response.statusCode != 200) {
throw Exception('Logout failed: ${response.body}');
}
}

@override
Future<String> refreshToken() async {
final response = await client.post(
Uri.parse('$baseUrl/auth/refresh'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${await _getRefreshToken()}',
},
);

if (response.statusCode == 200) {
final data = json.decode(response.body);
return data['access_token'];
} else {
throw Exception('Token refresh failed: ${response.body}');
}
}

@override
Future<void> sendPasswordResetEmail(String email) async {
final response = await client.post(
Uri.parse('$baseUrl/auth/password-reset'),
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'email': email,
}),
);

if (response.statusCode != 200) {
throw Exception('Failed to send reset email: ${response.body}');
}
}

@override
Future<void> verifyEmail(String otp) async {
final response = await client.post(
Uri.parse('$baseUrl/auth/verify-email'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${await _getAccessToken()}',
},
body: json.encode({
'otp': otp,
}),
);

if (response.statusCode != 200) {
throw Exception('Email verification failed: ${response.body}');
}
}

@override
Future<AuthModel> updateProfile({
String? name,
String? profilePicture,
}) async {
final response = await client.patch(
Uri.parse('$baseUrl/auth/profile'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${await _getAccessToken()}',
},
body: json.encode({
if (name != null) 'name': name,
if (profilePicture != null) 'profile_picture': profilePicture,
}),
);

if (response.statusCode == 200) {
final data = json.decode(response.body);
return AuthModel.fromJson(data['user']);
} else {
throw Exception('Profile update failed: ${response.body}');
}
}

@override
Future<void> changePassword({
required String currentPassword,
required String newPassword,
}) async {
final response = await client.patch(
Uri.parse('$baseUrl/auth/change-password'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${await _getAccessToken()}',
},
body: json.encode({
'current_password': currentPassword,
'new_password': newPassword,
}),
);

if (response.statusCode != 200) {
throw Exception('Password change failed: ${response.body}');
}
}

@override
Future<void> deleteAccount() async {
final response = await client.delete(
Uri.parse('$baseUrl/auth/account'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${await _getAccessToken()}',
},
);

if (response.statusCode != 200) {
throw Exception('Account deletion failed: ${response.body}');
}
}

// Helper methods to get tokens (implement based on your storage strategy)
Future<String> _getAccessToken() async {
// Get access token from secure storage
// This is a placeholder implementation
return 'access_token_here';
}

Future<String> _getRefreshToken() async {
// Get refresh token from secure storage
// This is a placeholder implementation
return 'refresh_token_here';
}
}

Auth Repository Implementation​

import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';

import '../../domain/entities/auth_entity.dart';
import '../../domain/repositories/auth_repository.dart';
import '../datasources/auth_remote_data_source.dart';
import '../models/auth_model.dart';

class AuthRepositoryImpl implements AuthRepository {
final AuthRemoteDataSource remoteDataSource;
final SharedPreferences sharedPreferences;

AuthRepositoryImpl({
required this.remoteDataSource,
required this.sharedPreferences,
});

static const String _currentUserKey = 'current_user';
static const String _accessTokenKey = 'access_token';
static const String _refreshTokenKey = 'refresh_token';

@override
Future<AuthEntity> login({
required String email,
required String password,
}) async {
try {
final authModel = await remoteDataSource.login(
email: email,
password: password,
);

// Save user data locally
await _saveUserLocally(authModel);

return authModel;
} catch (e) {
throw Exception('Login failed: ${e.toString()}');
}
}

@override
Future<AuthEntity> register({
required String name,
required String email,
required String password,
}) async {
try {
final authModel = await remoteDataSource.register(
name: name,
email: email,
password: password,
);

// Save user data locally
await _saveUserLocally(authModel);

return authModel;
} catch (e) {
throw Exception('Registration failed: ${e.toString()}');
}
}

@override
Future<void> logout() async {
try {
// Call remote logout
await remoteDataSource.logout();
} catch (e) {
// Even if remote logout fails, clear local data
print('Remote logout failed: $e');
} finally {
// Clear local data
await _clearUserData();
}
}

@override
Future<AuthEntity?> getCurrentUser() async {
try {
final userJson = sharedPreferences.getString(_currentUserKey);
if (userJson != null) {
final userMap = json.decode(userJson) as Map<String, dynamic>;
return AuthModel.fromJson(userMap);
}
return null;
} catch (e) {
return null;
}
}

@override
Future<bool> isLoggedIn() async {
try {
final user = await getCurrentUser();
final accessToken = sharedPreferences.getString(_accessTokenKey);
return user != null && accessToken != null;
} catch (e) {
return false;
}
}

@override
Future<String> refreshToken() async {
try {
final newAccessToken = await remoteDataSource.refreshToken();
await sharedPreferences.setString(_accessTokenKey, newAccessToken);
return newAccessToken;
} catch (e) {
throw Exception('Token refresh failed: ${e.toString()}');
}
}

@override
Future<void> sendPasswordResetEmail(String email) async {
try {
await remoteDataSource.sendPasswordResetEmail(email);
} catch (e) {
throw Exception('Failed to send reset email: ${e.toString()}');
}
}

@override
Future<void> verifyEmail(String otp) async {
try {
await remoteDataSource.verifyEmail(otp);

// Update local user data if available
final currentUser = await getCurrentUser();
if (currentUser != null && currentUser is AuthModel) {
final updatedUser = currentUser.copyWith(isEmailVerified: true);
await _saveUserLocally(updatedUser);
}
} catch (e) {
throw Exception('Email verification failed: ${e.toString()}');
}
}

@override
Future<AuthEntity> updateProfile({
String? name,
String? profilePicture,
}) async {
try {
final updatedUser = await remoteDataSource.updateProfile(
name: name,
profilePicture: profilePicture,
);

// Save updated user data locally
await _saveUserLocally(updatedUser);

return updatedUser;
} catch (e) {
throw Exception('Profile update failed: ${e.toString()}');
}
}

@override
Future<void> changePassword({
required String currentPassword,
required String newPassword,
}) async {
try {
await remoteDataSource.changePassword(
currentPassword: currentPassword,
newPassword: newPassword,
);
} catch (e) {
throw Exception('Password change failed: ${e.toString()}');
}
}

@override
Future<void> deleteAccount() async {
try {
await remoteDataSource.deleteAccount();
await _clearUserData();
} catch (e) {
throw Exception('Account deletion failed: ${e.toString()}');
}
}

// Private helper methods
Future<void> _saveUserLocally(AuthModel user) async {
await sharedPreferences.setString(
_currentUserKey,
json.encode(user.toJson()),
);
}

Future<void> _clearUserData() async {
await sharedPreferences.remove(_currentUserKey);
await sharedPreferences.remove(_accessTokenKey);
await sharedPreferences.remove(_refreshTokenKey);
}
}

🎨 Presentation Layer (GetX)​

Auth Controller​

import 'package:get/get.dart';
import 'package:flutter/material.dart';

import '../../domain/entities/auth_entity.dart';
import '../../domain/usecases/auth_usecase.dart';

class AuthController extends GetxController {
final AuthUseCase _authUseCase;

AuthController(this._authUseCase);

// Observable states
final _isLoading = false.obs;
final _currentUser = Rxn<AuthEntity>();
final _errorMessage = ''.obs;

// Form controllers
final emailController = TextEditingController();
final passwordController = TextEditingController();
final nameController = TextEditingController();
final confirmPasswordController = TextEditingController();
final otpController = TextEditingController();

// Form keys
final loginFormKey = GlobalKey<FormState>();
final registerFormKey = GlobalKey<FormState>();

// Getters
bool get isLoading => _isLoading.value;
AuthEntity? get currentUser => _currentUser.value;
String get errorMessage => _errorMessage.value;
bool get isLoggedIn => _currentUser.value != null;

@override
void onInit() {
super.onInit();
_checkCurrentUser();
}

@override
void onClose() {
emailController.dispose();
passwordController.dispose();
nameController.dispose();
confirmPasswordController.dispose();
otpController.dispose();
super.onClose();
}

/// Check if user is already logged in
Future<void> _checkCurrentUser() async {
try {
_isLoading.value = true;
final user = await _authUseCase.getCurrentUser();
_currentUser.value = user;
} catch (e) {
_errorMessage.value = e.toString();
} finally {
_isLoading.value = false;
}
}

/// Login functionality
Future<void> login() async {
if (!loginFormKey.currentState!.validate()) return;

try {
_isLoading.value = true;
_errorMessage.value = '';

final user = await _authUseCase.login(
email: emailController.text,
password: passwordController.text,
);

_currentUser.value = user;
_clearLoginForm();

Get.snackbar(
'āϏāĻĢāϞ!',
'āϏāĻĢāϞāĻ­āĻžāĻŦ⧇ āϞāĻ—āχāύ āĻšāϝāĻŧ⧇āϛ⧇',
backgroundColor: Colors.green,
colorText: Colors.white,
);

// Navigate to home or dashboard
Get.offAllNamed('/dashboard');
} catch (e) {
_errorMessage.value = e.toString();
Get.snackbar(
'āĻ¤ā§āϰ⧁āϟāĻŋ!',
e.toString(),
backgroundColor: Colors.red,
colorText: Colors.white,
);
} finally {
_isLoading.value = false;
}
}

/// Register functionality
Future<void> register() async {
if (!registerFormKey.currentState!.validate()) return;

try {
_isLoading.value = true;
_errorMessage.value = '';

final user = await _authUseCase.register(
name: nameController.text,
email: emailController.text,
password: passwordController.text,
confirmPassword: confirmPasswordController.text,
);

_currentUser.value = user;
_clearRegisterForm();

Get.snackbar(
'āϏāĻĢāϞ!',
'āϏāĻĢāϞāĻ­āĻžāĻŦ⧇ āϰ⧇āϜāĻŋāĻ¸ā§āϟāĻžāϰ āĻšāϝāĻŧ⧇āϛ⧇',
backgroundColor: Colors.green,
colorText: Colors.white,
);

// Navigate to email verification or dashboard
if (!user.isEmailVerified) {
Get.toNamed('/verify-email');
} else {
Get.offAllNamed('/dashboard');
}
} catch (e) {
_errorMessage.value = e.toString();
Get.snackbar(
'āĻ¤ā§āϰ⧁āϟāĻŋ!',
e.toString(),
backgroundColor: Colors.red,
colorText: Colors.white,
);
} finally {
_isLoading.value = false;
}
}

/// Logout functionality
Future<void> logout() async {
try {
_isLoading.value = true;
await _authUseCase.logout();
_currentUser.value = null;

Get.snackbar(
'āϏāĻĢāϞ!',
'āϏāĻĢāϞāĻ­āĻžāĻŦ⧇ āϞāĻ—āφāωāϟ āĻšāϝāĻŧ⧇āϛ⧇',
backgroundColor: Colors.green,
colorText: Colors.white,
);

Get.offAllNamed('/login');
} catch (e) {
Get.snackbar(
'āĻ¤ā§āϰ⧁āϟāĻŋ!',
e.toString(),
backgroundColor: Colors.red,
colorText: Colors.white,
);
} finally {
_isLoading.value = false;
}
}

/// Send password reset email
Future<void> sendPasswordResetEmail() async {
if (emailController.text.isEmpty) {
Get.snackbar(
'āĻ¤ā§āϰ⧁āϟāĻŋ!',
'āχāĻŽā§‡āχāϞ āĻĒā§āϰāϝāĻŧā§‹āϜāύ',
backgroundColor: Colors.red,
colorText: Colors.white,
);
return;
}

try {
_isLoading.value = true;
await _authUseCase.sendPasswordResetEmail(emailController.text);

Get.snackbar(
'āϏāĻĢāϞ!',
'āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āϰāĻŋāϏ⧇āϟ āχāĻŽā§‡āχāϞ āĻĒāĻžāĻ āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇',
backgroundColor: Colors.green,
colorText: Colors.white,
);
} catch (e) {
Get.snackbar(
'āĻ¤ā§āϰ⧁āϟāĻŋ!',
e.toString(),
backgroundColor: Colors.red,
colorText: Colors.white,
);
} finally {
_isLoading.value = false;
}
}

/// Verify email with OTP
Future<void> verifyEmail() async {
if (otpController.text.isEmpty) {
Get.snackbar(
'āĻ¤ā§āϰ⧁āϟāĻŋ!',
'OTP āĻĒā§āϰāϝāĻŧā§‹āϜāύ',
backgroundColor: Colors.red,
colorText: Colors.white,
);
return;
}

try {
_isLoading.value = true;
await _authUseCase.verifyEmail(otpController.text);

// Update current user
if (_currentUser.value != null) {
_currentUser.refresh();
}

Get.snackbar(
'āϏāĻĢāϞ!',
'āχāĻŽā§‡āχāϞ āϏāĻĢāϞāĻ­āĻžāĻŦ⧇ āϝāĻžāϚāĻžāχ āĻšāϝāĻŧ⧇āϛ⧇',
backgroundColor: Colors.green,
colorText: Colors.white,
);

Get.offAllNamed('/dashboard');
} catch (e) {
Get.snackbar(
'āĻ¤ā§āϰ⧁āϟāĻŋ!',
e.toString(),
backgroundColor: Colors.red,
colorText: Colors.white,
);
} finally {
_isLoading.value = false;
}
}

/// Clear login form
void _clearLoginForm() {
emailController.clear();
passwordController.clear();
}

/// Clear register form
void _clearRegisterForm() {
nameController.clear();
emailController.clear();
passwordController.clear();
confirmPasswordController.clear();
}

/// Validators
String? validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'āχāĻŽā§‡āχāϞ āĻĒā§āϰāϝāĻŧā§‹āϜāύ';
}
if (!GetUtils.isEmail(value)) {
return 'āϏāĻ āĻŋāĻ• āχāĻŽā§‡āχāϞ āĻĢāϰāĻŽā§āϝāĻžāϟ āĻĒā§āϰāϝāĻŧā§‹āϜāύ';
}
return null;
}

String? validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āĻĒā§āϰāϝāĻŧā§‹āϜāύ';
}
if (value.length < 6) {
return 'āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āĻ•āĻŽāĻĒāĻ•ā§āώ⧇ ā§Ŧ āĻ…āĻ•ā§āώāϰ⧇āϰ āĻšāϤ⧇ āĻšāĻŦ⧇';
}
return null;
}

String? validateName(String? value) {
if (value == null || value.isEmpty) {
return 'āύāĻžāĻŽ āĻĒā§āϰāϝāĻŧā§‹āϜāύ';
}
if (value.length < 2) {
return 'āύāĻžāĻŽ āĻ•āĻŽāĻĒāĻ•ā§āώ⧇ ⧍ āĻ…āĻ•ā§āώāϰ⧇āϰ āĻšāϤ⧇ āĻšāĻŦ⧇';
}
return null;
}

String? validateConfirmPassword(String? value) {
if (value == null || value.isEmpty) {
return 'āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āύāĻŋāĻļā§āϚāĻŋāϤāĻ•āϰāĻŖ āĻĒā§āϰāϝāĻŧā§‹āϜāύ';
}
if (value != passwordController.text) {
return 'āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āĻŽāĻŋāϞāϛ⧇ āύāĻž';
}
return null;
}
}

Auth Binding (GetX)​

import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

import '../../data/datasources/auth_remote_data_source.dart';
import '../../data/repositories/auth_repository_impl.dart';
import '../../domain/repositories/auth_repository.dart';
import '../../domain/usecases/auth_usecase.dart';
import '../controllers/auth_controller.dart';

class AuthBinding extends Bindings {
@override
void dependencies() {
// HTTP Client
Get.lazyPut<http.Client>(() => http.Client());

// Shared Preferences
Get.lazyPutAsync<SharedPreferences>(
() => SharedPreferences.getInstance(),
);

// Data Source
Get.lazyPut<AuthRemoteDataSource>(
() => AuthRemoteDataSourceImpl(
client: Get.find<http.Client>(),
baseUrl: 'https://api.example.com', // Replace with your API base URL
),
);

// Repository
Get.lazyPut<AuthRepository>(
() => AuthRepositoryImpl(
remoteDataSource: Get.find<AuthRemoteDataSource>(),
sharedPreferences: Get.find<SharedPreferences>(),
),
);

// Use Case
Get.lazyPut<AuthUseCase>(
() => AuthUseCase(Get.find<AuthRepository>()),
);

// Controller
Get.lazyPut<AuthController>(
() => AuthController(Get.find<AuthUseCase>()),
);
}
}

đŸŽ¯ Key Features Included​

  1. Complete Authentication Flow: Login, Register, Logout
  2. Email Verification: OTP-based email verification
  3. Password Management: Reset password functionality
  4. Profile Management: Update user profile
  5. Local Storage: Cache user data locally
  6. Token Management: Access and refresh token handling
  7. Error Handling: Comprehensive error handling
  8. Form Validation: Input validation for all forms
  9. State Management: Reactive UI with GetX observables
  10. Security: Secure token storage and API communication

🚀 Usage Example​

// In your main.dart or route configuration
GetPage(
name: '/login',
page: () => const AuthPage(),
binding: AuthBinding(),
),

// In your UI
class LoginForm extends StatelessWidget {

Widget build(BuildContext context) {
final controller = Get.find<AuthController>();

return Form(
key: controller.loginFormKey,
child: Column(
children: [
TextFormField(
controller: controller.emailController,
validator: controller.validateEmail,
decoration: InputDecoration(labelText: 'āχāĻŽā§‡āχāϞ'),
),
TextFormField(
controller: controller.passwordController,
validator: controller.validatePassword,
obscureText: true,
decoration: InputDecoration(labelText: 'āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ'),
),
Obx(() => ElevatedButton(
onPressed: controller.isLoading ? null : controller.login,
child: controller.isLoading
? CircularProgressIndicator()
: Text('āϞāĻ—āχāύ'),
)),
],
),
);
}
}

āĻāχ example-āĻ āϰāϝāĻŧ⧇āϛ⧇: āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ authentication feature āϝāĻž production-ready āĻāĻŦāĻ‚ scalable! 🔐