721 lines
17 KiB
Dart
721 lines
17 KiB
Dart
import 'package:coffedryer/page/signup_page.dart';
|
|
import 'package:coffedryer/page/dashboard_page.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
|
|
class SigninPage extends StatefulWidget {
|
|
const SigninPage({super.key});
|
|
|
|
@override
|
|
State<SigninPage> createState() =>
|
|
_LoginPageState();
|
|
}
|
|
|
|
class _LoginPageState
|
|
extends State<SigninPage> {
|
|
|
|
bool isHidden = true;
|
|
bool isLoading = false;
|
|
|
|
final emailController =
|
|
TextEditingController();
|
|
|
|
final passwordController =
|
|
TextEditingController();
|
|
|
|
final FirebaseAuth _auth =
|
|
FirebaseAuth.instance;
|
|
|
|
/// LOGIN
|
|
Future<void> loginUser() async {
|
|
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
|
|
await _auth
|
|
.signInWithEmailAndPassword(
|
|
email:
|
|
emailController.text.trim(),
|
|
|
|
password:
|
|
passwordController.text.trim(),
|
|
);
|
|
|
|
if (mounted) {
|
|
|
|
Navigator.pushReplacement(
|
|
context,
|
|
|
|
MaterialPageRoute(
|
|
builder: (context) =>
|
|
const DashboardPage(),
|
|
),
|
|
);
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
ScaffoldMessenger.of(context)
|
|
.showSnackBar(
|
|
|
|
SnackBar(
|
|
backgroundColor: Colors.red,
|
|
|
|
content: Text(
|
|
"Login gagal: $e",
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
|
|
/// RESET PASSWORD
|
|
Future<void>
|
|
showForgotPasswordDialog() async {
|
|
|
|
final resetController =
|
|
TextEditingController();
|
|
|
|
showDialog(
|
|
context: context,
|
|
|
|
builder: (context) {
|
|
|
|
return AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius:
|
|
BorderRadius.circular(20),
|
|
),
|
|
|
|
title: const Text(
|
|
"Reset Password",
|
|
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold,
|
|
),
|
|
),
|
|
|
|
content: Column(
|
|
mainAxisSize:
|
|
MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
const Icon(
|
|
Icons.lock_reset,
|
|
size: 60,
|
|
color:
|
|
Color(0xFF6D4C41),
|
|
),
|
|
|
|
const SizedBox(height: 15),
|
|
|
|
const Text(
|
|
"Masukkan email akun anda",
|
|
textAlign:
|
|
TextAlign.center,
|
|
),
|
|
|
|
const SizedBox(height: 15),
|
|
|
|
TextField(
|
|
controller:
|
|
resetController,
|
|
|
|
keyboardType:
|
|
TextInputType
|
|
.emailAddress,
|
|
|
|
decoration:
|
|
InputDecoration(
|
|
hintText: "Email",
|
|
|
|
prefixIcon:
|
|
const Icon(
|
|
Icons.email,
|
|
),
|
|
|
|
filled: true,
|
|
|
|
fillColor:
|
|
Colors.grey
|
|
.shade100,
|
|
|
|
border:
|
|
OutlineInputBorder(
|
|
borderRadius:
|
|
BorderRadius
|
|
.circular(
|
|
15),
|
|
|
|
borderSide:
|
|
BorderSide
|
|
.none,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(
|
|
context);
|
|
},
|
|
|
|
child:
|
|
const Text("Batal"),
|
|
),
|
|
|
|
ElevatedButton(
|
|
style:
|
|
ElevatedButton
|
|
.styleFrom(
|
|
backgroundColor:
|
|
const Color(
|
|
0xFF6D4C41),
|
|
),
|
|
|
|
onPressed: () async {
|
|
|
|
try {
|
|
|
|
await FirebaseAuth
|
|
.instance
|
|
.sendPasswordResetEmail(
|
|
email:
|
|
resetController
|
|
.text
|
|
.trim(),
|
|
);
|
|
|
|
Navigator.pop(
|
|
context);
|
|
|
|
ScaffoldMessenger.of(
|
|
context)
|
|
.showSnackBar(
|
|
|
|
const SnackBar(
|
|
content: Text(
|
|
"Link reset password berhasil dikirim",
|
|
),
|
|
),
|
|
);
|
|
|
|
} on FirebaseAuthException catch (e) {
|
|
|
|
String message =
|
|
"Terjadi kesalahan";
|
|
|
|
if (e.code ==
|
|
'user-not-found') {
|
|
|
|
message =
|
|
"Email tidak ditemukan";
|
|
}
|
|
|
|
if (e.code ==
|
|
'invalid-email') {
|
|
|
|
message =
|
|
"Format email tidak valid";
|
|
}
|
|
|
|
ScaffoldMessenger.of(
|
|
context)
|
|
.showSnackBar(
|
|
|
|
SnackBar(
|
|
content:
|
|
Text(message),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
|
|
child: const Text(
|
|
"Kirim",
|
|
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
|
|
return Scaffold(
|
|
backgroundColor:
|
|
const Color(0xFFF5F1EE),
|
|
|
|
body: SafeArea(
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
children: [
|
|
|
|
/// TOP HEADER
|
|
Container(
|
|
width: double.infinity,
|
|
|
|
padding:
|
|
const EdgeInsets.symmetric(
|
|
horizontal: 25,
|
|
vertical: 45,
|
|
),
|
|
|
|
decoration:
|
|
const BoxDecoration(
|
|
gradient:
|
|
LinearGradient(
|
|
colors: [
|
|
|
|
Color(0xFF6F4E37),
|
|
Color(0xFF8D6E63),
|
|
],
|
|
|
|
begin:
|
|
Alignment.topLeft,
|
|
|
|
end:
|
|
Alignment
|
|
.bottomRight,
|
|
),
|
|
|
|
borderRadius:
|
|
BorderRadius.only(
|
|
bottomLeft:
|
|
Radius.circular(
|
|
35),
|
|
|
|
bottomRight:
|
|
Radius.circular(
|
|
35),
|
|
),
|
|
),
|
|
|
|
child: Column(
|
|
children: [
|
|
|
|
/// LOGO
|
|
Container(
|
|
padding:
|
|
const EdgeInsets
|
|
.all(18),
|
|
|
|
decoration:
|
|
BoxDecoration(
|
|
color: Colors
|
|
.white
|
|
.withOpacity(
|
|
0.15),
|
|
|
|
shape:
|
|
BoxShape.circle,
|
|
),
|
|
|
|
child: Image.asset(
|
|
"assets/logo.png",
|
|
height: 90,
|
|
),
|
|
),
|
|
|
|
const SizedBox(
|
|
height: 20),
|
|
|
|
const Text(
|
|
"Coffee Dryer",
|
|
|
|
style: TextStyle(
|
|
color:
|
|
Colors.white,
|
|
|
|
fontSize: 28,
|
|
|
|
fontWeight:
|
|
FontWeight.bold,
|
|
),
|
|
),
|
|
|
|
const SizedBox(
|
|
height: 8),
|
|
|
|
const Text(
|
|
"Monitoring & Control System",
|
|
|
|
style: TextStyle(
|
|
color:
|
|
Colors.white70,
|
|
|
|
fontSize: 15,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 35),
|
|
|
|
/// LOGIN CARD
|
|
Container(
|
|
margin:
|
|
const EdgeInsets.symmetric(
|
|
horizontal: 22),
|
|
|
|
padding:
|
|
const EdgeInsets.all(
|
|
25),
|
|
|
|
decoration:
|
|
BoxDecoration(
|
|
color: Colors.white,
|
|
|
|
borderRadius:
|
|
BorderRadius
|
|
.circular(30),
|
|
|
|
boxShadow: [
|
|
|
|
BoxShadow(
|
|
color: Colors.black
|
|
.withOpacity(
|
|
0.05),
|
|
|
|
blurRadius: 20,
|
|
|
|
offset:
|
|
const Offset(
|
|
0, 10),
|
|
),
|
|
],
|
|
),
|
|
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment
|
|
.start,
|
|
|
|
children: [
|
|
|
|
const Text(
|
|
"Sign In",
|
|
|
|
style: TextStyle(
|
|
fontSize: 28,
|
|
|
|
fontWeight:
|
|
FontWeight.bold,
|
|
|
|
color: Color(
|
|
0xFF6D4C41),
|
|
),
|
|
),
|
|
|
|
const SizedBox(
|
|
height: 8),
|
|
|
|
Text(
|
|
"Masuk ke akun anda",
|
|
|
|
style: TextStyle(
|
|
color:
|
|
Colors.grey
|
|
.shade600,
|
|
|
|
fontSize: 15,
|
|
),
|
|
),
|
|
|
|
const SizedBox(
|
|
height: 30),
|
|
|
|
/// EMAIL
|
|
buildTextField(
|
|
controller:
|
|
emailController,
|
|
|
|
hint: "Email",
|
|
|
|
icon: Icons
|
|
.email_outlined,
|
|
),
|
|
|
|
const SizedBox(
|
|
height: 20),
|
|
|
|
/// PASSWORD
|
|
TextField(
|
|
controller:
|
|
passwordController,
|
|
|
|
obscureText:
|
|
isHidden,
|
|
|
|
decoration:
|
|
InputDecoration(
|
|
hintText:
|
|
"Password",
|
|
|
|
prefixIcon:
|
|
const Icon(
|
|
Icons
|
|
.lock_outline,
|
|
),
|
|
|
|
suffixIcon:
|
|
IconButton(
|
|
icon: Icon(
|
|
isHidden
|
|
? Icons
|
|
.visibility_off
|
|
: Icons
|
|
.visibility,
|
|
),
|
|
|
|
onPressed:
|
|
() {
|
|
|
|
setState(() {
|
|
isHidden =
|
|
!isHidden;
|
|
});
|
|
},
|
|
),
|
|
|
|
filled: true,
|
|
|
|
fillColor:
|
|
Colors.grey
|
|
.shade100,
|
|
|
|
contentPadding:
|
|
const EdgeInsets
|
|
.symmetric(
|
|
vertical: 18,
|
|
),
|
|
|
|
border:
|
|
OutlineInputBorder(
|
|
borderRadius:
|
|
BorderRadius
|
|
.circular(
|
|
18),
|
|
|
|
borderSide:
|
|
BorderSide
|
|
.none,
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(
|
|
height: 12),
|
|
|
|
/// LUPA PASSWORD
|
|
Align(
|
|
alignment:
|
|
Alignment
|
|
.centerRight,
|
|
|
|
child:
|
|
GestureDetector(
|
|
onTap: () {
|
|
showForgotPasswordDialog();
|
|
},
|
|
|
|
child: const Text(
|
|
"Lupa Password?",
|
|
|
|
style:
|
|
TextStyle(
|
|
color: Color(
|
|
0xFF6D4C41),
|
|
|
|
fontWeight:
|
|
FontWeight
|
|
.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(
|
|
height: 25),
|
|
|
|
/// BUTTON LOGIN
|
|
SizedBox(
|
|
width:
|
|
double.infinity,
|
|
|
|
height: 55,
|
|
|
|
child:
|
|
ElevatedButton(
|
|
onPressed:
|
|
isLoading
|
|
? null
|
|
: loginUser,
|
|
|
|
style:
|
|
ElevatedButton
|
|
.styleFrom(
|
|
elevation: 0,
|
|
|
|
backgroundColor:
|
|
const Color(
|
|
0xFF6D4C41),
|
|
|
|
shape:
|
|
RoundedRectangleBorder(
|
|
borderRadius:
|
|
BorderRadius
|
|
.circular(
|
|
18),
|
|
),
|
|
),
|
|
|
|
child: isLoading
|
|
? const CircularProgressIndicator(
|
|
color: Colors
|
|
.white,
|
|
)
|
|
: const Text(
|
|
"Sign In",
|
|
|
|
style:
|
|
TextStyle(
|
|
color: Colors
|
|
.white,
|
|
|
|
fontSize:
|
|
16,
|
|
|
|
fontWeight:
|
|
FontWeight
|
|
.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(
|
|
height: 25),
|
|
|
|
/// REGISTER
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.center,
|
|
|
|
children: [
|
|
|
|
Text(
|
|
"Belum punya akun?",
|
|
|
|
style:
|
|
TextStyle(
|
|
color: Colors
|
|
.grey
|
|
.shade700,
|
|
),
|
|
),
|
|
|
|
const SizedBox(
|
|
width: 5),
|
|
|
|
GestureDetector(
|
|
onTap: () {
|
|
|
|
Navigator.push(
|
|
context,
|
|
|
|
MaterialPageRoute(
|
|
builder:
|
|
(context) =>
|
|
const SignUpPage(),
|
|
),
|
|
);
|
|
},
|
|
|
|
child:
|
|
const Text(
|
|
"Daftar",
|
|
|
|
style:
|
|
TextStyle(
|
|
color: Color(
|
|
0xFF6D4C41),
|
|
|
|
fontWeight:
|
|
FontWeight
|
|
.bold,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 30),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildTextField({
|
|
required TextEditingController
|
|
controller,
|
|
|
|
required String hint,
|
|
|
|
required IconData icon,
|
|
}) {
|
|
|
|
return TextField(
|
|
controller: controller,
|
|
|
|
decoration: InputDecoration(
|
|
hintText: hint,
|
|
|
|
prefixIcon: Icon(icon),
|
|
|
|
filled: true,
|
|
|
|
fillColor:
|
|
Colors.grey.shade100,
|
|
|
|
contentPadding:
|
|
const EdgeInsets.symmetric(
|
|
vertical: 18,
|
|
),
|
|
|
|
border: OutlineInputBorder(
|
|
borderRadius:
|
|
BorderRadius.circular(18),
|
|
|
|
borderSide: BorderSide.none,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |