feat: login page done
This commit is contained in:
parent
2763575e1b
commit
32404aceae
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class GlobalButton extends StatelessWidget {
|
||||
final VoidCallback onPressed;
|
||||
final String text;
|
||||
|
||||
const GlobalButton({super.key, required this.onPressed, required this.text});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: Text(text),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,15 +3,49 @@ import 'package:flutter/material.dart';
|
|||
class GlobalTextField extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final String? hintText;
|
||||
final String? labelText;
|
||||
final bool isPassword;
|
||||
final bool obscureText;
|
||||
final VoidCallback? onToggleVisibility;
|
||||
|
||||
const GlobalTextField({super.key, required this.controller, this.hintText});
|
||||
const GlobalTextField({
|
||||
super.key,
|
||||
required this.controller,
|
||||
this.hintText,
|
||||
this.labelText,
|
||||
this.isPassword = false,
|
||||
this.obscureText = false,
|
||||
this.onToggleVisibility,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
obscureText: isPassword ? obscureText : false,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: hintText,
|
||||
labelText: labelText,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
hintText: hintText,
|
||||
filled: true,
|
||||
fillColor: const Color.fromARGB(255, 238, 238, 238),
|
||||
suffixIcon: isPassword
|
||||
? IconButton(
|
||||
icon: Icon(obscureText ? Icons.visibility_off : Icons.visibility),
|
||||
onPressed: onToggleVisibility,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class LabelTextField extends StatelessWidget {
|
||||
final String label;
|
||||
final double fontSize;
|
||||
final FontWeight fontWeight;
|
||||
final Alignment alignment;
|
||||
const LabelTextField(
|
||||
{super.key, required, required this.label, this.fontSize = 16, this.alignment = Alignment.centerLeft, this.fontWeight = FontWeight.bold});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
alignment: alignment,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(10, 5, 0, 5),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: fontWeight,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -41,14 +41,9 @@ class LoginController extends GetxController {
|
|||
|
||||
if (response.statusCode == 200) {
|
||||
var data = jsonDecode(response.body);
|
||||
String token = data['token'];
|
||||
|
||||
// await _secureStorage.write(key: "auth_token", value: token);
|
||||
|
||||
Get.snackbar("Success", "Login successful!");
|
||||
logC.i("Login Token: $token");
|
||||
} else {
|
||||
var errorMsg = jsonDecode(response.body)['message'] ?? "Invalid credentials";
|
||||
logC.i(errorMsg);
|
||||
Get.snackbar("Error", errorMsg);
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
|
@ -84,19 +79,20 @@ class LoginController extends GetxController {
|
|||
// Send ID Token to backend
|
||||
var response = await http.post(
|
||||
Uri.parse("${APIEndpoint.baseUrl}${APIEndpoint.loginGoogle}"),
|
||||
body: jsonEncode({"id_token": idToken}), // Ensure correct key
|
||||
body: jsonEncode({"token_id": idToken}), // Ensure correct key
|
||||
headers: {"Content-Type": "application/json"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = jsonDecode(response.body);
|
||||
String backendToken = data['token']; // Token received from your backend
|
||||
|
||||
|
||||
Get.snackbar("Success", "Google login successful!");
|
||||
logC.i("Backend Auth Token: $backendToken");
|
||||
// logC.i("Backend Auth Token: $backendToken");
|
||||
} else {
|
||||
var errorMsg = jsonDecode(response.body)['message'] ?? "Google login failed";
|
||||
Get.snackbar("Error", errorMsg);
|
||||
logC.i(errorMsg);
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
logC.e("Google Sign-In Error: $e", stackTrace: stackTrace);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class GoogleButton extends StatelessWidget {
|
||||
final VoidCallback onPress;
|
||||
const GoogleButton({super.key, required this.onPress});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: onPress,
|
||||
icon: Image.asset(
|
||||
'assets/logo/google_logo.png',
|
||||
height: 24,
|
||||
),
|
||||
label: const Text("Masuk dengan Google"),
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class RegisterTextButton extends StatelessWidget {
|
||||
final VoidCallback? onTap;
|
||||
const RegisterTextButton({super.key, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
"Belum punya akun? ",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: onTap,
|
||||
child: const Text(
|
||||
"Daftar",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color.fromARGB(255, 0, 122, 255),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:quiz_app/component/global_button.dart';
|
||||
import 'package:quiz_app/component/global_text_field.dart';
|
||||
import 'package:quiz_app/component/label_text_field.dart';
|
||||
import 'package:quiz_app/feature/login/controllers/login_controller.dart';
|
||||
import 'package:quiz_app/feature/login/presentation/component/google_button.dart';
|
||||
import 'package:quiz_app/feature/login/presentation/component/register_text_button.dart';
|
||||
|
||||
class LoginView extends GetView<LoginController> {
|
||||
const LoginView({super.key});
|
||||
|
@ -11,64 +16,47 @@ class LoginView extends GetView<LoginController> {
|
|||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
child: ListView(
|
||||
children: [
|
||||
const Text("Login Page", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Email Input
|
||||
TextField(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 40),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
Text("GEN", style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold)),
|
||||
Text("SO", style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold, color: Colors.red)),
|
||||
],
|
||||
),
|
||||
),
|
||||
LabelTextField(
|
||||
label: "Log In",
|
||||
fontSize: 24,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
LabelTextField(label: "Email"),
|
||||
GlobalTextField(
|
||||
controller: controller.emailController,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Email",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// Password Input dengan fitur show/hide
|
||||
Obx(() => TextField(
|
||||
LabelTextField(label: "Password"),
|
||||
Obx(
|
||||
() => GlobalTextField(
|
||||
controller: controller.passwordController,
|
||||
isPassword: true,
|
||||
obscureText: controller.isPasswordHidden.value,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Password",
|
||||
border: OutlineInputBorder(),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(controller.isPasswordHidden.value ? Icons.visibility_off : Icons.visibility),
|
||||
onPressed: controller.togglePasswordVisibility,
|
||||
onToggleVisibility: controller.togglePasswordVisibility,
|
||||
),
|
||||
),
|
||||
)),
|
||||
const SizedBox(height: 40),
|
||||
GlobalButton(onPressed: controller.loginWithEmail, text: "Masuk"),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Login Button
|
||||
ElevatedButton(
|
||||
onPressed: () => controller.loginWithEmail(),
|
||||
child: const Text("Login with Email"),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// Google Sign-In Button
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => controller.loginWithGoogle(),
|
||||
icon: Image.asset(
|
||||
'assets/logo/google_logo.png', // Pastikan logo Google ada di folder assets
|
||||
height: 24,
|
||||
),
|
||||
label: const Text("Login with Google"),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
elevation: 2,
|
||||
),
|
||||
),
|
||||
|
||||
ElevatedButton(
|
||||
onPressed: () => controller.logout(),
|
||||
child: const Text("log out"),
|
||||
LabelTextField(label: "OR", alignment: Alignment.center),
|
||||
const SizedBox(height: 20),
|
||||
GoogleButton(
|
||||
onPress: controller.loginWithGoogle,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
RegisterTextButton()
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue