|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
|
|
|
|
import 'package:siopas/theme.dart';
|
|
|
|
|
|
|
|
import '../models/user_model.dart';
|
|
|
|
import '../providers/auth_provider.dart';
|
|
|
|
import '../widget/loading_button.dart';
|
|
|
|
|
|
|
|
class SignInPage extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
State<SignInPage> createState() => _SignInPageState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SignInPageState extends State<SignInPage> {
|
|
|
|
TextEditingController emailController = TextEditingController(text: '');
|
|
|
|
TextEditingController passwordController = TextEditingController(text: '');
|
|
|
|
bool isLoading = false;
|
|
|
|
bool _isPasswordVisible = false;
|
|
|
|
|
|
|
|
// Create controllers for IP Address and Port
|
|
|
|
TextEditingController ipAddressController = TextEditingController();
|
|
|
|
TextEditingController portController = TextEditingController();
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final bool isSmallScreen = MediaQuery.of(context).size.width < 600;
|
|
|
|
AuthProvider authProvider = Provider.of<AuthProvider>(context);
|
|
|
|
UserModel user = authProvider.user;
|
|
|
|
|
|
|
|
// Function to show the SettingsModal
|
|
|
|
void _showSettingsModal(BuildContext context) {
|
|
|
|
showDialog(
|
|
|
|
context: context,
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
return SettingsModal();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Scaffold(
|
|
|
|
body: Center(
|
|
|
|
child: isSmallScreen
|
|
|
|
? SingleChildScrollView(
|
|
|
|
reverse: true,
|
|
|
|
scrollDirection: Axis.vertical, // Tambahkan ini
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: const [
|
|
|
|
_Logo(),
|
|
|
|
_FormContent(),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
)
|
|
|
|
: Container(
|
|
|
|
padding: const EdgeInsets.all(30.0),
|
|
|
|
constraints: const BoxConstraints(maxWidth: 800),
|
|
|
|
child: Row(
|
|
|
|
children: [
|
|
|
|
Expanded(child: _Logo()),
|
|
|
|
Expanded(
|
|
|
|
child: Center(child: _FormContent()),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
bottomNavigationBar: BottomAppBar(
|
|
|
|
color: Colors.grey[50],
|
|
|
|
elevation: 0,
|
|
|
|
child: Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
children: [
|
|
|
|
TextButton(
|
|
|
|
onPressed: () {
|
|
|
|
_showSettingsModal(context); // Show the modal
|
|
|
|
},
|
|
|
|
child: Text('Pengaturan'),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _Logo extends StatelessWidget {
|
|
|
|
const _Logo({Key? key}) : super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final bool isSmallScreen = MediaQuery.of(context).size.width < 600;
|
|
|
|
|
|
|
|
return Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
Container(
|
|
|
|
// margin: EdgeInsets.only(top: 30),
|
|
|
|
child: Image.asset(
|
|
|
|
'assets/img/siopas_apps.png',
|
|
|
|
height: isSmallScreen ? 135 : 200,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.all(16.0),
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
"Siopas ISTW",
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: isSmallScreen ? 18 : 22,
|
|
|
|
fontWeight: semiBold,
|
|
|
|
color: Colors.black,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
SizedBox(
|
|
|
|
height:
|
|
|
|
8.0), // Berikan sedikit jarak antara teks "Siopas ISTW" dan versi
|
|
|
|
Text(
|
|
|
|
"Version 1.0.0", // Gantilah dengan versi aktual
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: isSmallScreen ? 14 : 16,
|
|
|
|
color: Colors.grey,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _FormContent extends StatefulWidget {
|
|
|
|
const _FormContent({Key? key}) : super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<_FormContent> createState() => __FormContentState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class __FormContentState extends State<_FormContent> {
|
|
|
|
TextEditingController emailController = TextEditingController(text: '');
|
|
|
|
TextEditingController passwordController = TextEditingController(text: '');
|
|
|
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
|
|
|
|
|
|
bool isLoading = false;
|
|
|
|
|
|
|
|
bool _isPasswordVisible = false;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
AuthProvider authProvider = Provider.of<AuthProvider>(context);
|
|
|
|
UserModel user = authProvider.user;
|
|
|
|
|
|
|
|
handleSignIn() async {
|
|
|
|
setState(() {
|
|
|
|
isLoading = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (await authProvider.login(
|
|
|
|
email: emailController.text,
|
|
|
|
password: passwordController.text,
|
|
|
|
)) {
|
|
|
|
UserModel user = authProvider.user;
|
|
|
|
// Simpan token pengguna ke SharedPreferences
|
|
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
|
|
prefs.setString('token', user.token!); // Pastikan user.token tidak null
|
|
|
|
// Role ID Operator
|
|
|
|
// final String roleId = 'A5C7B207-1A1C-43B8-89BF-222222222222';
|
|
|
|
|
|
|
|
print('token dapat login: ${user.token}');
|
|
|
|
if (user.role_id == 2) {
|
|
|
|
print('Berhasil login HALAMAN USER');
|
|
|
|
Navigator.pushNamed(context, '/home');
|
|
|
|
} else {
|
|
|
|
print('Tidak ada informasi peran (roles) yang tersedia');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
SnackBar(
|
|
|
|
backgroundColor: alertColor,
|
|
|
|
content: Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
children: [
|
|
|
|
Icon(
|
|
|
|
Icons.warning,
|
|
|
|
color: Colors.white,
|
|
|
|
),
|
|
|
|
SizedBox(width: 8), // Jarak antara ikon dan teks
|
|
|
|
Text(
|
|
|
|
'Gagal Login, Email dan Password anda salah',
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
Future.delayed(Duration(seconds: 3), () {
|
|
|
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
isLoading = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return Container(
|
|
|
|
constraints: const BoxConstraints(maxWidth: 300),
|
|
|
|
child: Form(
|
|
|
|
key: _formKey,
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
children: [
|
|
|
|
TextFormField(
|
|
|
|
validator: (value) {
|
|
|
|
// add email validation
|
|
|
|
if (value == null || value.isEmpty) {
|
|
|
|
return 'Silakan masukkan email anda';
|
|
|
|
}
|
|
|
|
|
|
|
|
bool emailValid = RegExp(
|
|
|
|
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
|
|
|
|
.hasMatch(value);
|
|
|
|
if (!emailValid) {
|
|
|
|
return 'Tolong masukkan email yang benar';
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
controller: emailController,
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
labelText: 'Email',
|
|
|
|
hintText: 'Masukkan email Anda',
|
|
|
|
prefixIcon: Icon(Icons.email_outlined),
|
|
|
|
border: OutlineInputBorder(),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
_gap(),
|
|
|
|
TextFormField(
|
|
|
|
validator: (value) {
|
|
|
|
if (value == null || value.isEmpty) {
|
|
|
|
return 'Silakan masukkan password anda';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value.length < 6) {
|
|
|
|
return 'Kata sandi minimal harus 6 karakter';
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
obscureText: !_isPasswordVisible,
|
|
|
|
controller: passwordController,
|
|
|
|
decoration: InputDecoration(
|
|
|
|
labelText: 'Password',
|
|
|
|
hintText: 'Masukkan kata sandi Anda',
|
|
|
|
prefixIcon: const Icon(Icons.lock_outline_rounded),
|
|
|
|
border: const OutlineInputBorder(),
|
|
|
|
suffixIcon: IconButton(
|
|
|
|
icon: Icon(_isPasswordVisible
|
|
|
|
? Icons.visibility_off
|
|
|
|
: Icons.visibility),
|
|
|
|
onPressed: () {
|
|
|
|
setState(() {
|
|
|
|
_isPasswordVisible = !_isPasswordVisible;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
)),
|
|
|
|
),
|
|
|
|
_gap(),
|
|
|
|
SizedBox(
|
|
|
|
width: double.infinity,
|
|
|
|
child: ElevatedButton(
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
),
|
|
|
|
primary: Colors.indigo, // Ganti warna tombol menjadi indigo
|
|
|
|
alignment: Alignment.center, // Menyusun isi tombol ke tengah
|
|
|
|
),
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.all(10.0),
|
|
|
|
child: Row(
|
|
|
|
mainAxisAlignment:
|
|
|
|
MainAxisAlignment.center, // Tengahkan teks 'Login'
|
|
|
|
children: [
|
|
|
|
Icon(Icons.login),
|
|
|
|
SizedBox(
|
|
|
|
width:
|
|
|
|
8.0), // Berikan sedikit jarak antara ikon dan teks
|
|
|
|
Text(
|
|
|
|
'Masuk',
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 16, fontWeight: FontWeight.bold),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
onPressed: () {
|
|
|
|
if (_formKey.currentState!.validate()) {
|
|
|
|
if (emailController.text.isEmpty ||
|
|
|
|
passwordController.text.isEmpty) {
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
|
|
SnackBar(
|
|
|
|
backgroundColor: alertColor,
|
|
|
|
content: Row(
|
|
|
|
children: [
|
|
|
|
Icon(
|
|
|
|
Icons.warning,
|
|
|
|
color: Colors.white,
|
|
|
|
),
|
|
|
|
SizedBox(width: 8), // Jarak antara ikon dan teks
|
|
|
|
Text(
|
|
|
|
'Email dan Password tidak boleh kosong',
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
Future.delayed(Duration(seconds: 2), () {
|
|
|
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
handleSignIn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _gap() => const SizedBox(height: 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
class SettingsModal extends StatefulWidget {
|
|
|
|
const SettingsModal({Key? key}) : super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<SettingsModal> createState() => _SettingsModalState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SettingsModalState extends State<SettingsModal> {
|
|
|
|
TextEditingController ipAddressController = TextEditingController();
|
|
|
|
TextEditingController portController = TextEditingController();
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
// Load saved IP Address and Port when the modal is initialized
|
|
|
|
loadSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void loadSettings() async {
|
|
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
|
|
String ipAddress = prefs.getString('ipAddress') ?? '';
|
|
|
|
String port = prefs.getString('port') ?? '';
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
ipAddressController.text = ipAddress;
|
|
|
|
portController.text = port;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return AlertDialog(
|
|
|
|
title: Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
|
|
|
Text('Pengaturan'),
|
|
|
|
GestureDetector(
|
|
|
|
onTap: () {
|
|
|
|
Navigator.of(context).pop(); // Close the modal
|
|
|
|
},
|
|
|
|
child: Icon(Icons.close),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
content: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
TextFormField(
|
|
|
|
controller: ipAddressController,
|
|
|
|
keyboardType: TextInputType.number, // Set numeric keyboard
|
|
|
|
decoration: InputDecoration(labelText: 'IP Address'),
|
|
|
|
),
|
|
|
|
SizedBox(height: 16),
|
|
|
|
TextFormField(
|
|
|
|
controller: portController,
|
|
|
|
keyboardType: TextInputType.number, // Set numeric keyboard
|
|
|
|
decoration: InputDecoration(labelText: 'Port'),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
actions: [
|
|
|
|
ElevatedButton(
|
|
|
|
onPressed: () {
|
|
|
|
// Save IP Address and Port to SharedPreferences
|
|
|
|
saveSettings(ipAddressController.text, portController.text);
|
|
|
|
Navigator.of(context).pop(); // Close the modal
|
|
|
|
},
|
|
|
|
child: Text('Simpan'),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void saveSettings(String ipAddress, String port) async {
|
|
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
|
|
prefs.setString('ipAddress', ipAddress);
|
|
|
|
prefs.setString('port', port);
|
|
|
|
|
|
|
|
// Print the saved data
|
|
|
|
print('IP Address saved: $ipAddress');
|
|
|
|
print('Port saved: $port');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to clear the IP Address and Port in SharedPreferences
|
|
|
|
void clearSettings() async {
|
|
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
|
|
prefs.remove('ipAddress');
|
|
|
|
prefs.remove('port');
|
|
|
|
print('IP Address and Port cleared');
|
|
|
|
}
|
|
|
|
}
|