modify the design of the password reset and account sign up display
This commit is contained in:
parent
e4a12c12d1
commit
d7ea2eb0ef
|
|
@ -5,29 +5,115 @@ import '../services/tts_service.dart';
|
|||
import '../utils/colors.dart';
|
||||
import 'login_screen.dart';
|
||||
|
||||
class ResetPasswordScreen extends StatelessWidget {
|
||||
class ResetPasswordScreen extends StatefulWidget {
|
||||
const ResetPasswordScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ResetPasswordScreen> createState() => _ResetPasswordScreenState();
|
||||
}
|
||||
|
||||
class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||
final TextEditingController newPasswordController = TextEditingController();
|
||||
final TextEditingController confirmPasswordController = TextEditingController();
|
||||
|
||||
final TTSService tts = TTSService();
|
||||
String passwordStrength = ""; // Track password strength
|
||||
|
||||
ResetPasswordScreen({super.key});
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
tts.speak("Halaman reset password");
|
||||
});
|
||||
|
||||
// ============ LISTENER UNTUK PASSWORD REAL-TIME ============
|
||||
newPasswordController.addListener(() {
|
||||
setState(() {
|
||||
passwordStrength = _getPasswordStrength(newPasswordController.text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
newPasswordController.dispose();
|
||||
confirmPasswordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// ============ FUNGSI UNTUK MENGHITUNG PASSWORD STRENGTH ============
|
||||
String _getPasswordStrength(String password) {
|
||||
if (password.isEmpty) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// ✅ Cek ada huruf besar
|
||||
bool hasUpperCase = password.contains(RegExp(r'[A-Z]'));
|
||||
|
||||
// ✅ Cek ada angka
|
||||
bool hasNumber = password.contains(RegExp(r'[0-9]'));
|
||||
|
||||
// ✅ Penentuan strength
|
||||
if (password.length < 6) {
|
||||
return "weak"; // Password lemah
|
||||
} else if (hasUpperCase && hasNumber) {
|
||||
return "strong"; // Password kuat
|
||||
} else {
|
||||
return "medium"; // Password sedang
|
||||
}
|
||||
}
|
||||
|
||||
// ============ FUNGSI UNTUK MENDAPATKAN WARNA PASSWORD STRENGTH ============
|
||||
Color _getPasswordStrengthColor() {
|
||||
switch (passwordStrength) {
|
||||
case "weak":
|
||||
return Colors.red; // Danger
|
||||
case "strong":
|
||||
return Colors.green; // Success
|
||||
case "medium":
|
||||
return Colors.orange; // Warning
|
||||
default:
|
||||
return Colors.transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// ============ FUNGSI UNTUK MENDAPATKAN TEXT PASSWORD STRENGTH ============
|
||||
String _getPasswordStrengthText() {
|
||||
switch (passwordStrength) {
|
||||
case "weak":
|
||||
return "Password lemah";
|
||||
case "strong":
|
||||
return "Password kuat";
|
||||
case "medium":
|
||||
return "Password sedang";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// ============ HANDLE RESET PASSWORD ============
|
||||
void resetPassword(BuildContext context) {
|
||||
String newPass = newPasswordController.text;
|
||||
String confirmPass = confirmPasswordController.text;
|
||||
String newPass = newPasswordController.text.trim();
|
||||
String confirmPass = confirmPasswordController.text.trim();
|
||||
|
||||
// ============ VALIDASI PASSWORD KOSONG ============
|
||||
if (newPass.isEmpty || confirmPass.isEmpty) {
|
||||
tts.speak("Semua field harus diisi");
|
||||
return;
|
||||
}
|
||||
|
||||
// ============ VALIDASI PASSWORD PANJANG ============
|
||||
if (newPass.length < 6) {
|
||||
tts.speak("Password minimal 6 karakter");
|
||||
return;
|
||||
}
|
||||
|
||||
// ============ VALIDASI PASSWORD SAMA ============
|
||||
if (newPass != confirmPass) {
|
||||
tts.speak("Password tidak sama");
|
||||
return;
|
||||
}
|
||||
|
||||
tts.speak("Password berhasil diubah");
|
||||
tts.speak("Password berhasil diubah, silakan login kembali");
|
||||
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
|
|
@ -38,38 +124,196 @@ class ResetPasswordScreen extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
tts.speak("Halaman reset password");
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
appBar: AppBar(title: Text("Reset Password")),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
appBar: AppBar(
|
||||
title: const Text("Reset Password"),
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppColors.background,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// ==================== HEADER SECTION ====================
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 40),
|
||||
|
||||
CustomTextField(
|
||||
label: "Password Baru",
|
||||
controller: newPasswordController,
|
||||
obscure: true,
|
||||
),
|
||||
// 🔑 ICON
|
||||
Container(
|
||||
width: 70,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.lock,
|
||||
size: 36,
|
||||
color: AppColors.background,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 20),
|
||||
SizedBox(height: 24),
|
||||
|
||||
CustomTextField(
|
||||
label: "Konfirmasi Password",
|
||||
controller: confirmPasswordController,
|
||||
obscure: true,
|
||||
),
|
||||
// 📝 TITLE
|
||||
Text(
|
||||
"Buat Password Baru",
|
||||
style: TextStyle(
|
||||
color: AppColors.text,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 30),
|
||||
SizedBox(height: 12),
|
||||
|
||||
CustomButton(
|
||||
text: "Simpan Password",
|
||||
onPressed: () => resetPassword(context),
|
||||
),
|
||||
],
|
||||
// 📌 SUBTITLE
|
||||
Text(
|
||||
"Masukkan password baru untuk keamanan akun Anda",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.grey[400],
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// ==================== PASSWORD SECTION ====================
|
||||
Text(
|
||||
"Password Baru",
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 8),
|
||||
|
||||
// 🔐 NEW PASSWORD FIELD
|
||||
CustomTextField(
|
||||
label: "Masukkan password baru",
|
||||
controller: newPasswordController,
|
||||
obscure: true,
|
||||
),
|
||||
|
||||
// ============ PASSWORD STRENGTH INDICATOR ============
|
||||
if (passwordStrength.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
_getPasswordStrengthText(),
|
||||
style: TextStyle(
|
||||
color: _getPasswordStrengthColor(),
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 20),
|
||||
|
||||
// 📌 PASSWORD REQUIREMENT INFO
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[900],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.grey[700]!, width: 0.5),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Requirement Password:",
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
"• Minimal 6 karakter",
|
||||
style: TextStyle(color: Colors.grey[400], fontSize: 12),
|
||||
),
|
||||
Text(
|
||||
"• Mengandung huruf besar (A-Z)",
|
||||
style: TextStyle(color: Colors.grey[400], fontSize: 12),
|
||||
),
|
||||
Text(
|
||||
"• Mengandung angka (0-9)",
|
||||
style: TextStyle(color: Colors.grey[400], fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 24),
|
||||
|
||||
// ==================== CONFIRM PASSWORD SECTION ====================
|
||||
Text(
|
||||
"Konfirmasi Password",
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 8),
|
||||
|
||||
// 🔐 CONFIRM PASSWORD FIELD
|
||||
CustomTextField(
|
||||
label: "Ulangi password baru",
|
||||
controller: confirmPasswordController,
|
||||
obscure: true,
|
||||
),
|
||||
|
||||
SizedBox(height: 32),
|
||||
|
||||
// ==================== SUBMIT BUTTON ====================
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: CustomButton(
|
||||
text: "Simpan Password Baru",
|
||||
onPressed: () => resetPassword(context),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 16),
|
||||
|
||||
// ==================== BACK TO LOGIN ====================
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => LoginScreen()),
|
||||
(route) => false,
|
||||
),
|
||||
child: Text(
|
||||
"Kembali ke Login",
|
||||
style: TextStyle(
|
||||
color: AppColors.primary,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class _SignUpScreenState extends State<SignUpScreen> {
|
|||
final TextEditingController phoneController = TextEditingController();
|
||||
final TTSService tts = TTSService();
|
||||
String? selectedGender;
|
||||
String passwordStrength = ""; // Track password strength
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -27,6 +28,13 @@ class _SignUpScreenState extends State<SignUpScreen> {
|
|||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
tts.speak("Halaman daftar akun");
|
||||
});
|
||||
|
||||
// ============ LISTENER UNTUK PASSWORD REAL-TIME ============
|
||||
passwordController.addListener(() {
|
||||
setState(() {
|
||||
passwordStrength = _getPasswordStrength(passwordController.text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -95,6 +103,56 @@ class _SignUpScreenState extends State<SignUpScreen> {
|
|||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
// ============ FUNGSI UNTUK MENGHITUNG PASSWORD STRENGTH ============
|
||||
String _getPasswordStrength(String password) {
|
||||
if (password.isEmpty) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// ✅ Cek ada huruf besar
|
||||
bool hasUpperCase = password.contains(RegExp(r'[A-Z]'));
|
||||
|
||||
// ✅ Cek ada angka
|
||||
bool hasNumber = password.contains(RegExp(r'[0-9]'));
|
||||
|
||||
// ✅ Penentuan strength
|
||||
if (password.length < 6) {
|
||||
return "weak"; // Password lemah
|
||||
} else if (hasUpperCase && hasNumber) {
|
||||
return "strong"; // Password kuat
|
||||
} else {
|
||||
return "medium"; // Password sedang
|
||||
}
|
||||
}
|
||||
|
||||
// ============ FUNGSI UNTUK MENDAPATKAN WARNA PASSWORD STRENGTH ============
|
||||
Color _getPasswordStrengthColor() {
|
||||
switch (passwordStrength) {
|
||||
case "weak":
|
||||
return Colors.red; // Danger
|
||||
case "strong":
|
||||
return Colors.green; // Success
|
||||
case "medium":
|
||||
return Colors.orange; // Warning
|
||||
default:
|
||||
return Colors.transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// ============ FUNGSI UNTUK MENDAPATKAN TEXT PASSWORD STRENGTH ============
|
||||
String _getPasswordStrengthText() {
|
||||
switch (passwordStrength) {
|
||||
case "weak":
|
||||
return "Password lemah";
|
||||
case "strong":
|
||||
return "Password kuat";
|
||||
case "medium":
|
||||
return "Password sedang";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildGenderOption(String gender) {
|
||||
return RadioListTile<String>(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
|
|
@ -271,6 +329,20 @@ class _SignUpScreenState extends State<SignUpScreen> {
|
|||
obscure: true,
|
||||
),
|
||||
|
||||
// ============ PASSWORD STRENGTH INDICATOR ============
|
||||
if (passwordStrength.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
_getPasswordStrengthText(),
|
||||
style: TextStyle(
|
||||
color: _getPasswordStrengthColor(),
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 16),
|
||||
|
||||
CustomTextField(
|
||||
|
|
|
|||
Loading…
Reference in New Issue