Feat: done features sign up
This commit is contained in:
parent
7a2b8db157
commit
6516cc7c39
|
@ -680,7 +680,7 @@
|
||||||
"languageVersion": "3.4"
|
"languageVersion": "3.4"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"generated": "2025-05-09T09:48:57.570155Z",
|
"generated": "2025-05-10T18:42:15.590215Z",
|
||||||
"generator": "pub",
|
"generator": "pub",
|
||||||
"generatorVersion": "3.5.0",
|
"generatorVersion": "3.5.0",
|
||||||
"flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0",
|
"flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0",
|
||||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 56 KiB |
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Validators {
|
class Validators {
|
||||||
static String? validatorEmail(String? value) {
|
static String? validatorEmail(String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
|
@ -13,6 +15,20 @@ class Validators {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String? Function(String?) validatorConfirmPassword(
|
||||||
|
TextEditingController passwordController,
|
||||||
|
) {
|
||||||
|
return (String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Password tidak boleh kosong';
|
||||||
|
}
|
||||||
|
if (value != passwordController.text) {
|
||||||
|
return 'Password tidak cocok';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static String? validatorName(String? value) {
|
static String? validatorName(String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Nama Lengkap tidak boleh kosong';
|
return 'Nama Lengkap tidak boleh kosong';
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:e_porter/domain/models/user_entity.dart';
|
import 'package:e_porter/domain/models/user_entity.dart';
|
||||||
import 'package:e_porter/domain/repositories/auth_repository.dart';
|
import 'package:e_porter/domain/repositories/auth_repository.dart';
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
|
||||||
import '../../_core/service/logger_service.dart';
|
|
||||||
|
|
||||||
class AuthException implements Exception {
|
class AuthException implements Exception {
|
||||||
final String message;
|
final String message;
|
||||||
AuthException(this.message);
|
AuthException(this.message);
|
||||||
|
@ -24,11 +24,15 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||||
password: password,
|
password: password,
|
||||||
);
|
);
|
||||||
final user = userCredential.user!;
|
final user = userCredential.user!;
|
||||||
|
|
||||||
|
await user.reload();
|
||||||
|
if (!user.emailVerified) {
|
||||||
|
await _firebaseAuth.signOut();
|
||||||
|
throw AuthException("email-not-verified");
|
||||||
|
}
|
||||||
|
|
||||||
return UserEntity(uid: user.uid, email: user.email ?? "");
|
return UserEntity(uid: user.uid, email: user.email ?? "");
|
||||||
} on FirebaseAuthException catch (e) {
|
} on FirebaseAuthException catch (e) {
|
||||||
logger.w("FirebaseAuthException code: ${e.code}");
|
|
||||||
logger.w("FirebaseAuthException message: ${e.message}");
|
|
||||||
|
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case 'invalid-email':
|
case 'invalid-email':
|
||||||
throw AuthException("Format email tidak valid.");
|
throw AuthException("Format email tidak valid.");
|
||||||
|
@ -44,6 +48,43 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<UserEntity> registerWithEmailPassword(String email, String password) async {
|
||||||
|
try {
|
||||||
|
UserCredential userCredential = await _firebaseAuth.createUserWithEmailAndPassword(
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
);
|
||||||
|
final user = userCredential.user!;
|
||||||
|
await user.sendEmailVerification();
|
||||||
|
await user.updateDisplayName(email);
|
||||||
|
|
||||||
|
return UserEntity(uid: user.uid, email: user.email ?? "");
|
||||||
|
} on FirebaseAuthException catch (e) {
|
||||||
|
log("FirebaseAuthException code: ${e.code}");
|
||||||
|
log("FirebaseAuthException message: ${e.message}");
|
||||||
|
|
||||||
|
throw AuthException(e.code);
|
||||||
|
} catch (e) {
|
||||||
|
throw AuthException(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveUserData(UserData userData) async {
|
||||||
|
try {
|
||||||
|
await _firestore.collection('users').doc(userData.uid).set(
|
||||||
|
userData.toMap(),
|
||||||
|
SetOptions(merge: true),
|
||||||
|
);
|
||||||
|
|
||||||
|
log("User data berhasil disimpan ke Firestore");
|
||||||
|
} catch (e) {
|
||||||
|
log("Error saving user data: $e");
|
||||||
|
throw AuthException("Gagal menyimpan data pengguna.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> signOut() async {
|
Future<void> signOut() async {
|
||||||
await _firebaseAuth.signOut();
|
await _firebaseAuth.signOut();
|
||||||
|
|
|
@ -13,12 +13,16 @@ class AuthBinding extends Bindings {
|
||||||
final loginUseCase = LoginUseCase(authRepository);
|
final loginUseCase = LoginUseCase(authRepository);
|
||||||
final getUserRoleUseCase = GetUserRoleUseCase(authRepository);
|
final getUserRoleUseCase = GetUserRoleUseCase(authRepository);
|
||||||
final getUserDataUseCase = GetUserDataUseCase(authRepository);
|
final getUserDataUseCase = GetUserDataUseCase(authRepository);
|
||||||
|
final registerUseCase = RegisterUseCase(authRepository);
|
||||||
|
final saveUserDataUseCase = SaveUserDataUseCase(authRepository);
|
||||||
|
|
||||||
Get.put<AuthController>(
|
Get.put<AuthController>(
|
||||||
AuthController(
|
AuthController(
|
||||||
loginUseCase: loginUseCase,
|
loginUseCase: loginUseCase,
|
||||||
getUserRoleUseCase: getUserRoleUseCase,
|
getUserRoleUseCase: getUserRoleUseCase,
|
||||||
getUserDataUseCase: getUserDataUseCase,
|
getUserDataUseCase: getUserDataUseCase,
|
||||||
|
registerUseCase: registerUseCase,
|
||||||
|
saveUserDataUseCase: saveUserDataUseCase,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,17 +26,17 @@ class UserData {
|
||||||
|
|
||||||
UserData({
|
UserData({
|
||||||
required this.uid,
|
required this.uid,
|
||||||
required this.tipeId,
|
this.tipeId,
|
||||||
required this.noId,
|
this.noId,
|
||||||
required this.name,
|
this.name,
|
||||||
required this.email,
|
this.email,
|
||||||
required this.phone,
|
this.phone,
|
||||||
required this.birthDate,
|
this.birthDate,
|
||||||
required this.gender,
|
this.gender,
|
||||||
required this.work,
|
this.work,
|
||||||
required this.city,
|
this.city,
|
||||||
required this.address,
|
this.address,
|
||||||
required this.role,
|
this.role,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory UserData.fromMap(Map<String, dynamic> map) {
|
factory UserData.fromMap(Map<String, dynamic> map) {
|
||||||
|
|
|
@ -7,4 +7,6 @@ abstract class AuthRepository {
|
||||||
Future<String?> getUserRole(String uid);
|
Future<String?> getUserRole(String uid);
|
||||||
Future<UserData?> getUserData(String uid);
|
Future<UserData?> getUserData(String uid);
|
||||||
|
|
||||||
|
Future<UserEntity> registerWithEmailPassword(String email, String password);
|
||||||
|
Future<void> saveUserData(UserData userData);
|
||||||
}
|
}
|
|
@ -28,3 +28,23 @@ class GetUserDataUseCase {
|
||||||
return await authRepository.getUserData(uid);
|
return await authRepository.getUserData(uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RegisterUseCase {
|
||||||
|
final AuthRepository authRepository;
|
||||||
|
|
||||||
|
RegisterUseCase(this.authRepository);
|
||||||
|
|
||||||
|
Future<UserEntity> call(String email, String password) async {
|
||||||
|
return await authRepository.registerWithEmailPassword(email, password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SaveUserDataUseCase {
|
||||||
|
final AuthRepository authRepository;
|
||||||
|
|
||||||
|
SaveUserDataUseCase(this.authRepository);
|
||||||
|
|
||||||
|
Future<void> call(UserData userData) async {
|
||||||
|
return await authRepository.saveUserData(userData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import 'package:e_porter/_core/utils/snackbar/snackbar_helper.dart';
|
||||||
import 'package:e_porter/data/repositories/auth_repository_impl.dart';
|
import 'package:e_porter/data/repositories/auth_repository_impl.dart';
|
||||||
|
import 'package:e_porter/domain/models/user_entity.dart';
|
||||||
import 'package:e_porter/domain/usecases/auth_usecase.dart';
|
import 'package:e_porter/domain/usecases/auth_usecase.dart';
|
||||||
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
@ -11,6 +14,8 @@ class AuthController extends GetxController {
|
||||||
final LoginUseCase loginUseCase;
|
final LoginUseCase loginUseCase;
|
||||||
final GetUserRoleUseCase getUserRoleUseCase;
|
final GetUserRoleUseCase getUserRoleUseCase;
|
||||||
final GetUserDataUseCase getUserDataUseCase;
|
final GetUserDataUseCase getUserDataUseCase;
|
||||||
|
final RegisterUseCase registerUseCase;
|
||||||
|
final SaveUserDataUseCase saveUserDataUseCase;
|
||||||
|
|
||||||
final emailController = TextEditingController();
|
final emailController = TextEditingController();
|
||||||
final passwordController = TextEditingController();
|
final passwordController = TextEditingController();
|
||||||
|
@ -22,13 +27,15 @@ class AuthController extends GetxController {
|
||||||
required this.loginUseCase,
|
required this.loginUseCase,
|
||||||
required this.getUserRoleUseCase,
|
required this.getUserRoleUseCase,
|
||||||
required this.getUserDataUseCase,
|
required this.getUserDataUseCase,
|
||||||
|
required this.registerUseCase,
|
||||||
|
required this.saveUserDataUseCase,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> login({String? roleFromOnboarding}) async {
|
Future<void> login({String? roleFromOnboarding}) async {
|
||||||
errorMessage.value = '';
|
errorMessage.value = '';
|
||||||
|
|
||||||
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
|
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
|
||||||
_showErrorSnackbar("Error", "Email/Password tidak boleh kosong");
|
SnackbarHelper.showError("Error", "Email/Password tidak boleh kosong");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
@ -44,7 +51,7 @@ class AuthController extends GetxController {
|
||||||
logger.d("roleFromDB: $roleFromDB, roleFromOnboarding: $roleFromOnboarding, UID: $uid");
|
logger.d("roleFromDB: $roleFromDB, roleFromOnboarding: $roleFromOnboarding, UID: $uid");
|
||||||
|
|
||||||
if (roleFromDB != null && roleFromOnboarding != null && roleFromDB != roleFromOnboarding) {
|
if (roleFromDB != null && roleFromOnboarding != null && roleFromDB != roleFromOnboarding) {
|
||||||
_showErrorSnackbar(
|
SnackbarHelper.showError(
|
||||||
"Role Tidak Sesuai", "Akun ini terdaftar sebagai '$roleFromDB', bukan '$roleFromOnboarding'.");
|
"Role Tidak Sesuai", "Akun ini terdaftar sebagai '$roleFromDB', bukan '$roleFromOnboarding'.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -53,12 +60,12 @@ class AuthController extends GetxController {
|
||||||
|
|
||||||
final userData = await getUserDataUseCase(uid);
|
final userData = await getUserDataUseCase(uid);
|
||||||
if (userData == null) {
|
if (userData == null) {
|
||||||
_showErrorSnackbar("Login Gagal", "Data user tidak ditemukan.");
|
SnackbarHelper.showError("Login Gagal", "Data user tidak ditemukan.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userData.role!.toLowerCase() != effectiveRole.toLowerCase()) {
|
if (userData.role!.toLowerCase() != effectiveRole.toLowerCase()) {
|
||||||
_showErrorSnackbar(
|
SnackbarHelper.showError(
|
||||||
"Role Tidak Sesuai", "Data user menunjukkan role '${userData.role}', bukan '$effectiveRole'.");
|
"Role Tidak Sesuai", "Data user menunjukkan role '${userData.role}', bukan '$effectiveRole'.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -66,21 +73,140 @@ class AuthController extends GetxController {
|
||||||
await PreferencesService.saveUserData(userData);
|
await PreferencesService.saveUserData(userData);
|
||||||
Get.offAllNamed(Routes.NAVBAR, arguments: effectiveRole);
|
Get.offAllNamed(Routes.NAVBAR, arguments: effectiveRole);
|
||||||
} on AuthException catch (e) {
|
} on AuthException catch (e) {
|
||||||
_showErrorSnackbar("Login Gagal", e.message);
|
if (e.message == 'email-not-verified') {
|
||||||
|
SnackbarHelper.showError(
|
||||||
|
"Verifikasi Diperlukan",
|
||||||
|
"Silakan cek email Anda dan klik link verifikasi sebelum login.",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
SnackbarHelper.showError(
|
||||||
|
"Login Gagal",
|
||||||
|
"Email atau password anda salah.",
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_showErrorSnackbar("Terjadi Kesalahan", e.toString());
|
SnackbarHelper.showError("Terjadi Kesalahan", e.toString());
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showErrorSnackbar(String title, String message) {
|
Future<void> register({
|
||||||
Get.snackbar(
|
required String name,
|
||||||
title,
|
required String email,
|
||||||
message,
|
required String password,
|
||||||
snackPosition: SnackPosition.TOP,
|
required String role,
|
||||||
backgroundColor: Colors.red,
|
}) async {
|
||||||
colorText: Colors.white,
|
errorMessage.value = '';
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final userEntity = await registerUseCase(
|
||||||
|
email.trim(),
|
||||||
|
password,
|
||||||
|
);
|
||||||
|
|
||||||
|
final userData = UserData(
|
||||||
|
uid: userEntity.uid,
|
||||||
|
tipeId: null,
|
||||||
|
noId: null,
|
||||||
|
name: name.trim(),
|
||||||
|
email: email.trim(),
|
||||||
|
phone: null,
|
||||||
|
birthDate: null,
|
||||||
|
gender: null,
|
||||||
|
work: null,
|
||||||
|
city: null,
|
||||||
|
address: null,
|
||||||
|
role: role,
|
||||||
|
);
|
||||||
|
|
||||||
|
await saveUserDataUseCase(userData);
|
||||||
|
|
||||||
|
SnackbarHelper.showSuccess(
|
||||||
|
"Berhasil",
|
||||||
|
"Akun berhasil dibuat. Silakan cek email Anda untuk verifikasi terlebih dahulu.",
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.offNamed(Routes.VERIFICATION);
|
||||||
|
} on AuthException catch (e) {
|
||||||
|
switch (e.message) {
|
||||||
|
case 'weak-password':
|
||||||
|
SnackbarHelper.showError("Error", "Password terlalu lemah.");
|
||||||
|
break;
|
||||||
|
case 'email-already-in-use':
|
||||||
|
SnackbarHelper.showError("Error", "Email sudah terdaftar.");
|
||||||
|
break;
|
||||||
|
case 'invalid-email':
|
||||||
|
SnackbarHelper.showError("Error", "Format email tidak valid.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SnackbarHelper.showError("Registrasi Gagal", "Terjadi kesalahan saat registrasi.");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
SnackbarHelper.showError("Terjadi Kesalahan", e.toString());
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> resendEmailVerification() async {
|
||||||
|
final user = FirebaseAuth.instance.currentUser;
|
||||||
|
if (user == null) {
|
||||||
|
SnackbarHelper.showError("Error", "User tidak ditemukan.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (user.emailVerified) {
|
||||||
|
SnackbarHelper.showSuccess("Sudah Terverifikasi", "Email Anda sudah terverifikasi.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await user.sendEmailVerification();
|
||||||
|
SnackbarHelper.showSuccess(
|
||||||
|
"Terkirim",
|
||||||
|
"Link verifikasi telah dikirim ulang ke email Anda.",
|
||||||
|
);
|
||||||
|
} on FirebaseException catch (e) {
|
||||||
|
if (e.message?.contains('No AppCheckProvider') == true) {
|
||||||
|
SnackbarHelper.showSuccess(
|
||||||
|
"Terkirim",
|
||||||
|
"Link verifikasi telah dikirim ulang ke email Anda.",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
SnackbarHelper.showError(
|
||||||
|
"Gagal",
|
||||||
|
"Gagal mengirim ulang verifikasi. Silakan coba lagi.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
SnackbarHelper.showError(
|
||||||
|
"Gagal",
|
||||||
|
"Gagal mengirim ulang verifikasi. Silakan coba lagi.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> completeEmailVerification() async {
|
||||||
|
final user = FirebaseAuth.instance.currentUser;
|
||||||
|
if (user == null) return;
|
||||||
|
await user.reload();
|
||||||
|
|
||||||
|
if (user.emailVerified) {
|
||||||
|
final userData = await getUserDataUseCase(user.uid);
|
||||||
|
if (userData != null) {
|
||||||
|
await PreferencesService.saveUserData(userData);
|
||||||
|
}
|
||||||
|
Get.offAllNamed(Routes.NAVBAR, arguments: userData?.role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// void _showErrorSnackbar(String title, String message) {
|
||||||
|
// Get.snackbar(
|
||||||
|
// title,
|
||||||
|
// message,
|
||||||
|
// snackPosition: SnackPosition.TOP,
|
||||||
|
// backgroundColor: Colors.red,
|
||||||
|
// colorText: Colors.white,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:e_porter/_core/component/button/button_fill.dart';
|
import 'package:e_porter/_core/component/button/button_fill.dart';
|
||||||
import 'package:e_porter/_core/constants/colors.dart';
|
import 'package:e_porter/_core/constants/colors.dart';
|
||||||
import 'package:e_porter/_core/constants/typography.dart';
|
import 'package:e_porter/_core/constants/typography.dart';
|
||||||
|
@ -128,7 +130,8 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||||
firstText: 'Belum punya akun?',
|
firstText: 'Belum punya akun?',
|
||||||
secondText: 'Daftar',
|
secondText: 'Daftar',
|
||||||
onTab: () {
|
onTab: () {
|
||||||
Get.toNamed(Routes.REGISTER);
|
log('Role Login: $role');
|
||||||
|
Get.toNamed(Routes.REGISTER, arguments: role);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:e_porter/_core/utils/formatter/uppercase_helper.dart';
|
||||||
|
import 'package:e_porter/_core/validators/validators.dart';
|
||||||
|
import 'package:e_porter/presentation/controllers/auth_controller.dart';
|
||||||
import 'package:e_porter/presentation/screens/auth/component/header_text.dart';
|
import 'package:e_porter/presentation/screens/auth/component/header_text.dart';
|
||||||
import 'package:e_porter/presentation/screens/routes/app_rountes.dart';
|
import 'package:e_porter/presentation/screens/routes/app_rountes.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:zoom_tap_animation/zoom_tap_animation.dart';
|
import 'package:zoom_tap_animation/zoom_tap_animation.dart';
|
||||||
|
@ -19,6 +25,37 @@ class RegisterScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RegisterScreenState extends State<RegisterScreen> {
|
class _RegisterScreenState extends State<RegisterScreen> {
|
||||||
|
final String? role = Get.arguments as String;
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
final TextEditingController _name = TextEditingController();
|
||||||
|
final TextEditingController _email = TextEditingController();
|
||||||
|
final TextEditingController _password = TextEditingController();
|
||||||
|
final TextEditingController _verifPassword = TextEditingController();
|
||||||
|
final AuthController _authController = Get.find<AuthController>();
|
||||||
|
|
||||||
|
void _handleRegister() {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
if (_password.text != _verifPassword.text) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Error',
|
||||||
|
'Password tidak cocok',
|
||||||
|
snackPosition: SnackPosition.TOP,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_authController.register(
|
||||||
|
name: _name.text,
|
||||||
|
email: _email.text,
|
||||||
|
password: _password.text,
|
||||||
|
role: role.toString(),
|
||||||
|
);
|
||||||
|
log('Role Registrasi: $role');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -28,13 +65,13 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
|
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Form(
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
HeaderText(
|
HeaderText(
|
||||||
firstText: 'Daftar',
|
firstText: 'Daftar',
|
||||||
secondText:
|
secondText: 'Segera daftarkan diri anda ke aplikasi ini untuk akses penuh ke fitur kami!',
|
||||||
'Segera daftarkan diri anda ke aplikasi ini untuk akses penuh ke fitur kami!',
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 50.h),
|
SizedBox(height: 50.h),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -47,8 +84,15 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||||
),
|
),
|
||||||
SizedBox(height: 16.h),
|
SizedBox(height: 16.h),
|
||||||
InputForm(
|
InputForm(
|
||||||
hintText: 'Suparjo',
|
controller: _name,
|
||||||
|
hintText: 'SUPARJO',
|
||||||
svgIconPath: 'assets/icons/ic_account.svg',
|
svgIconPath: 'assets/icons/ic_account.svg',
|
||||||
|
validator: Validators.validatorName,
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z\s]')),
|
||||||
|
UpperCaseTextFormatter(),
|
||||||
|
],
|
||||||
|
textInputType: TextInputType.text,
|
||||||
),
|
),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -61,8 +105,11 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||||
),
|
),
|
||||||
SizedBox(height: 16.h),
|
SizedBox(height: 16.h),
|
||||||
InputForm(
|
InputForm(
|
||||||
|
controller: _email,
|
||||||
hintText: 'example@gmail.com',
|
hintText: 'example@gmail.com',
|
||||||
svgIconPath: 'assets/icons/ic_email.svg',
|
svgIconPath: 'assets/icons/ic_email.svg',
|
||||||
|
validator: Validators.validatorEmail,
|
||||||
|
textInputType: TextInputType.emailAddress,
|
||||||
),
|
),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -75,8 +122,10 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||||
),
|
),
|
||||||
SizedBox(height: 16.h),
|
SizedBox(height: 16.h),
|
||||||
InputPassword(
|
InputPassword(
|
||||||
|
controller: _password,
|
||||||
hintText: '••••••••••',
|
hintText: '••••••••••',
|
||||||
svgIconPath: 'assets/icons/ic_padlock.svg',
|
svgIconPath: 'assets/icons/ic_padlock.svg',
|
||||||
|
validator: Validators.validatorPassword,
|
||||||
),
|
),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -89,8 +138,10 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||||
),
|
),
|
||||||
SizedBox(height: 16.h),
|
SizedBox(height: 16.h),
|
||||||
InputPassword(
|
InputPassword(
|
||||||
|
controller: _verifPassword,
|
||||||
hintText: '••••••••••',
|
hintText: '••••••••••',
|
||||||
svgIconPath: 'assets/icons/ic_padlock.svg',
|
svgIconPath: 'assets/icons/ic_padlock.svg',
|
||||||
|
validator: Validators.validatorConfirmPassword(_password),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
|
@ -102,13 +153,13 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ZoomTapAnimation(
|
Obx(
|
||||||
|
() => ZoomTapAnimation(
|
||||||
child: ButtonFill(
|
child: ButtonFill(
|
||||||
text: 'Daftar',
|
text: _authController.isLoading.value ? 'Loading...' : 'Daftar',
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
onTap: () {
|
onTap: _authController.isLoading.value ? null : _handleRegister,
|
||||||
Get.toNamed(Routes.STATESUCCES);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:e_porter/_core/constants/colors.dart';
|
||||||
|
import 'package:e_porter/_core/constants/typography.dart';
|
||||||
|
import 'package:e_porter/presentation/controllers/auth_controller.dart';
|
||||||
|
import 'package:e_porter/presentation/screens/auth/component/header_text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:zoom_tap_animation/zoom_tap_animation.dart';
|
||||||
|
|
||||||
|
class VerifikasiScreen extends StatefulWidget {
|
||||||
|
VerifikasiScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<VerifikasiScreen> createState() => _VerifikasiScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VerifikasiScreenState extends State<VerifikasiScreen> {
|
||||||
|
final AuthController _authController = Get.find();
|
||||||
|
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_timer = Timer.periodic(const Duration(seconds: 3), (_) async {
|
||||||
|
await _authController.completeEmailVerification();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
body: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
HeaderText(
|
||||||
|
firstText: 'Verifikasi Email',
|
||||||
|
secondText: 'Kami telah mengirimkan link verifikasi melalui email anda. Silahkan cek email anda',
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 20.h),
|
||||||
|
child: SvgPicture.asset('assets/images/il_email.svg'),
|
||||||
|
),
|
||||||
|
SizedBox(height: 32.h),
|
||||||
|
_buildSendVerification(
|
||||||
|
onTap: _authController.resendEmailVerification,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSendVerification({required VoidCallback onTap}) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
TypographyStyles.body(
|
||||||
|
'Kirim ulang verifikasi?',
|
||||||
|
color: GrayColors.gray600,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
SizedBox(width: 8.w),
|
||||||
|
ZoomTapAnimation(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: TypographyStyles.body('Kirim Ulang', color: Colors.blue.shade600),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import 'package:e_porter/presentation/screens/auth/pages/forget_password_screen.
|
||||||
import 'package:e_porter/presentation/screens/auth/pages/login_screen.dart';
|
import 'package:e_porter/presentation/screens/auth/pages/login_screen.dart';
|
||||||
import 'package:e_porter/presentation/screens/auth/pages/register_screen.dart';
|
import 'package:e_porter/presentation/screens/auth/pages/register_screen.dart';
|
||||||
import 'package:e_porter/presentation/screens/auth/pages/state_succes_screen.dart';
|
import 'package:e_porter/presentation/screens/auth/pages/state_succes_screen.dart';
|
||||||
|
import 'package:e_porter/presentation/screens/auth/pages/verifikasi_screen.dart';
|
||||||
import 'package:e_porter/presentation/screens/boarding_pass/pages/boarding_pass_screen.dart';
|
import 'package:e_porter/presentation/screens/boarding_pass/pages/boarding_pass_screen.dart';
|
||||||
import 'package:e_porter/presentation/screens/boarding_pass/pages/detail_history_porter_screen.dart';
|
import 'package:e_porter/presentation/screens/boarding_pass/pages/detail_history_porter_screen.dart';
|
||||||
import 'package:e_porter/presentation/screens/boarding_pass/pages/detail_ticket_screen.dart';
|
import 'package:e_porter/presentation/screens/boarding_pass/pages/detail_ticket_screen.dart';
|
||||||
|
@ -66,6 +67,10 @@ class AppRoutes {
|
||||||
page: () => LoginScreen(),
|
page: () => LoginScreen(),
|
||||||
binding: AuthBinding(),
|
binding: AuthBinding(),
|
||||||
),
|
),
|
||||||
|
GetPage(
|
||||||
|
name: Routes.VERIFICATION,
|
||||||
|
page: () => VerifikasiScreen(),
|
||||||
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: Routes.HOME,
|
name: Routes.HOME,
|
||||||
page: () => MainNavigation(),
|
page: () => MainNavigation(),
|
||||||
|
@ -196,6 +201,7 @@ class Routes {
|
||||||
static const SPLASH = '/splash';
|
static const SPLASH = '/splash';
|
||||||
static const ONBOARDING = '/onboarding';
|
static const ONBOARDING = '/onboarding';
|
||||||
static const LOGIN = '/login';
|
static const LOGIN = '/login';
|
||||||
|
static const VERIFICATION = '/verification';
|
||||||
static const HOME = '/home';
|
static const HOME = '/home';
|
||||||
static const BOARDINGPASS = '/boarding_pass';
|
static const BOARDINGPASS = '/boarding_pass';
|
||||||
static const PROFILE = '/profile';
|
static const PROFILE = '/profile';
|
||||||
|
|
Loading…
Reference in New Issue