Feat: done features sign up
This commit is contained in:
parent
7a2b8db157
commit
6516cc7c39
|
@ -680,7 +680,7 @@
|
|||
"languageVersion": "3.4"
|
||||
}
|
||||
],
|
||||
"generated": "2025-05-09T09:48:57.570155Z",
|
||||
"generated": "2025-05-10T18:42:15.590215Z",
|
||||
"generator": "pub",
|
||||
"generatorVersion": "3.5.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 {
|
||||
static String? validatorEmail(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
|
@ -13,6 +15,20 @@ class Validators {
|
|||
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) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Nama Lengkap tidak boleh kosong';
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:e_porter/domain/models/user_entity.dart';
|
||||
import 'package:e_porter/domain/repositories/auth_repository.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
|
||||
import '../../_core/service/logger_service.dart';
|
||||
|
||||
class AuthException implements Exception {
|
||||
final String message;
|
||||
AuthException(this.message);
|
||||
|
@ -24,11 +24,15 @@ class AuthRepositoryImpl implements AuthRepository {
|
|||
password: password,
|
||||
);
|
||||
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 ?? "");
|
||||
} on FirebaseAuthException catch (e) {
|
||||
logger.w("FirebaseAuthException code: ${e.code}");
|
||||
logger.w("FirebaseAuthException message: ${e.message}");
|
||||
|
||||
switch (e.code) {
|
||||
case 'invalid-email':
|
||||
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
|
||||
Future<void> signOut() async {
|
||||
await _firebaseAuth.signOut();
|
||||
|
|
|
@ -13,12 +13,16 @@ class AuthBinding extends Bindings {
|
|||
final loginUseCase = LoginUseCase(authRepository);
|
||||
final getUserRoleUseCase = GetUserRoleUseCase(authRepository);
|
||||
final getUserDataUseCase = GetUserDataUseCase(authRepository);
|
||||
final registerUseCase = RegisterUseCase(authRepository);
|
||||
final saveUserDataUseCase = SaveUserDataUseCase(authRepository);
|
||||
|
||||
Get.put<AuthController>(
|
||||
AuthController(
|
||||
loginUseCase: loginUseCase,
|
||||
getUserRoleUseCase: getUserRoleUseCase,
|
||||
getUserDataUseCase: getUserDataUseCase,
|
||||
registerUseCase: registerUseCase,
|
||||
saveUserDataUseCase: saveUserDataUseCase,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,17 +26,17 @@ class UserData {
|
|||
|
||||
UserData({
|
||||
required this.uid,
|
||||
required this.tipeId,
|
||||
required this.noId,
|
||||
required this.name,
|
||||
required this.email,
|
||||
required this.phone,
|
||||
required this.birthDate,
|
||||
required this.gender,
|
||||
required this.work,
|
||||
required this.city,
|
||||
required this.address,
|
||||
required this.role,
|
||||
this.tipeId,
|
||||
this.noId,
|
||||
this.name,
|
||||
this.email,
|
||||
this.phone,
|
||||
this.birthDate,
|
||||
this.gender,
|
||||
this.work,
|
||||
this.city,
|
||||
this.address,
|
||||
this.role,
|
||||
});
|
||||
|
||||
factory UserData.fromMap(Map<String, dynamic> map) {
|
||||
|
|
|
@ -7,4 +7,6 @@ abstract class AuthRepository {
|
|||
Future<String?> getUserRole(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);
|
||||
}
|
||||
}
|
||||
|
||||
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/domain/models/user_entity.dart';
|
||||
import 'package:e_porter/domain/usecases/auth_usecase.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
|
@ -11,6 +14,8 @@ class AuthController extends GetxController {
|
|||
final LoginUseCase loginUseCase;
|
||||
final GetUserRoleUseCase getUserRoleUseCase;
|
||||
final GetUserDataUseCase getUserDataUseCase;
|
||||
final RegisterUseCase registerUseCase;
|
||||
final SaveUserDataUseCase saveUserDataUseCase;
|
||||
|
||||
final emailController = TextEditingController();
|
||||
final passwordController = TextEditingController();
|
||||
|
@ -22,13 +27,15 @@ class AuthController extends GetxController {
|
|||
required this.loginUseCase,
|
||||
required this.getUserRoleUseCase,
|
||||
required this.getUserDataUseCase,
|
||||
required this.registerUseCase,
|
||||
required this.saveUserDataUseCase,
|
||||
});
|
||||
|
||||
Future<void> login({String? roleFromOnboarding}) async {
|
||||
errorMessage.value = '';
|
||||
|
||||
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
|
||||
_showErrorSnackbar("Error", "Email/Password tidak boleh kosong");
|
||||
SnackbarHelper.showError("Error", "Email/Password tidak boleh kosong");
|
||||
return;
|
||||
}
|
||||
isLoading.value = true;
|
||||
|
@ -44,7 +51,7 @@ class AuthController extends GetxController {
|
|||
logger.d("roleFromDB: $roleFromDB, roleFromOnboarding: $roleFromOnboarding, UID: $uid");
|
||||
|
||||
if (roleFromDB != null && roleFromOnboarding != null && roleFromDB != roleFromOnboarding) {
|
||||
_showErrorSnackbar(
|
||||
SnackbarHelper.showError(
|
||||
"Role Tidak Sesuai", "Akun ini terdaftar sebagai '$roleFromDB', bukan '$roleFromOnboarding'.");
|
||||
return;
|
||||
}
|
||||
|
@ -53,12 +60,12 @@ class AuthController extends GetxController {
|
|||
|
||||
final userData = await getUserDataUseCase(uid);
|
||||
if (userData == null) {
|
||||
_showErrorSnackbar("Login Gagal", "Data user tidak ditemukan.");
|
||||
SnackbarHelper.showError("Login Gagal", "Data user tidak ditemukan.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (userData.role!.toLowerCase() != effectiveRole.toLowerCase()) {
|
||||
_showErrorSnackbar(
|
||||
SnackbarHelper.showError(
|
||||
"Role Tidak Sesuai", "Data user menunjukkan role '${userData.role}', bukan '$effectiveRole'.");
|
||||
return;
|
||||
}
|
||||
|
@ -66,21 +73,140 @@ class AuthController extends GetxController {
|
|||
await PreferencesService.saveUserData(userData);
|
||||
Get.offAllNamed(Routes.NAVBAR, arguments: effectiveRole);
|
||||
} 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) {
|
||||
_showErrorSnackbar("Terjadi Kesalahan", e.toString());
|
||||
SnackbarHelper.showError("Terjadi Kesalahan", e.toString());
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _showErrorSnackbar(String title, String message) {
|
||||
Get.snackbar(
|
||||
title,
|
||||
message,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
Future<void> register({
|
||||
required String name,
|
||||
required String email,
|
||||
required String password,
|
||||
required String role,
|
||||
}) async {
|
||||
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/constants/colors.dart';
|
||||
import 'package:e_porter/_core/constants/typography.dart';
|
||||
|
@ -128,7 +130,8 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
firstText: 'Belum punya akun?',
|
||||
secondText: 'Daftar',
|
||||
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/routes/app_rountes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:zoom_tap_animation/zoom_tap_animation.dart';
|
||||
|
@ -19,6 +25,37 @@ class RegisterScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -28,13 +65,13 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
HeaderText(
|
||||
firstText: 'Daftar',
|
||||
secondText:
|
||||
'Segera daftarkan diri anda ke aplikasi ini untuk akses penuh ke fitur kami!',
|
||||
secondText: 'Segera daftarkan diri anda ke aplikasi ini untuk akses penuh ke fitur kami!',
|
||||
),
|
||||
SizedBox(height: 50.h),
|
||||
Padding(
|
||||
|
@ -47,8 +84,15 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
),
|
||||
SizedBox(height: 16.h),
|
||||
InputForm(
|
||||
hintText: 'Suparjo',
|
||||
controller: _name,
|
||||
hintText: 'SUPARJO',
|
||||
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),
|
||||
Padding(
|
||||
|
@ -61,8 +105,11 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
),
|
||||
SizedBox(height: 16.h),
|
||||
InputForm(
|
||||
controller: _email,
|
||||
hintText: 'example@gmail.com',
|
||||
svgIconPath: 'assets/icons/ic_email.svg',
|
||||
validator: Validators.validatorEmail,
|
||||
textInputType: TextInputType.emailAddress,
|
||||
),
|
||||
SizedBox(height: 20.h),
|
||||
Padding(
|
||||
|
@ -75,8 +122,10 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
),
|
||||
SizedBox(height: 16.h),
|
||||
InputPassword(
|
||||
controller: _password,
|
||||
hintText: '••••••••••',
|
||||
svgIconPath: 'assets/icons/ic_padlock.svg',
|
||||
validator: Validators.validatorPassword,
|
||||
),
|
||||
SizedBox(height: 20.h),
|
||||
Padding(
|
||||
|
@ -89,8 +138,10 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
),
|
||||
SizedBox(height: 16.h),
|
||||
InputPassword(
|
||||
controller: _verifPassword,
|
||||
hintText: '••••••••••',
|
||||
svgIconPath: 'assets/icons/ic_padlock.svg',
|
||||
validator: Validators.validatorConfirmPassword(_password),
|
||||
),
|
||||
],
|
||||
)),
|
||||
|
@ -102,13 +153,13 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ZoomTapAnimation(
|
||||
Obx(
|
||||
() => ZoomTapAnimation(
|
||||
child: ButtonFill(
|
||||
text: 'Daftar',
|
||||
text: _authController.isLoading.value ? 'Loading...' : 'Daftar',
|
||||
textColor: Colors.white,
|
||||
onTap: () {
|
||||
Get.toNamed(Routes.STATESUCCES);
|
||||
},
|
||||
onTap: _authController.isLoading.value ? null : _handleRegister,
|
||||
),
|
||||
),
|
||||
),
|
||||
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/register_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/detail_history_porter_screen.dart';
|
||||
import 'package:e_porter/presentation/screens/boarding_pass/pages/detail_ticket_screen.dart';
|
||||
|
@ -66,6 +67,10 @@ class AppRoutes {
|
|||
page: () => LoginScreen(),
|
||||
binding: AuthBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: Routes.VERIFICATION,
|
||||
page: () => VerifikasiScreen(),
|
||||
),
|
||||
GetPage(
|
||||
name: Routes.HOME,
|
||||
page: () => MainNavigation(),
|
||||
|
@ -196,6 +201,7 @@ class Routes {
|
|||
static const SPLASH = '/splash';
|
||||
static const ONBOARDING = '/onboarding';
|
||||
static const LOGIN = '/login';
|
||||
static const VERIFICATION = '/verification';
|
||||
static const HOME = '/home';
|
||||
static const BOARDINGPASS = '/boarding_pass';
|
||||
static const PROFILE = '/profile';
|
||||
|
|
Loading…
Reference in New Issue