feat: login page

This commit is contained in:
akhdanre 2025-03-01 13:03:55 +07:00
parent f1bb4abd6d
commit 5787cea803
9 changed files with 172 additions and 7 deletions

BIN
assets/logo/google_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,6 +1,7 @@
import 'package:get/get_navigation/src/routes/get_route.dart';
import 'package:quiz_app/app/middleware/auth_middleware.dart';
import 'package:quiz_app/feature/home/presentation/home_page.dart';
import 'package:quiz_app/feature/login/bindings/login_binding.dart';
import 'package:quiz_app/feature/login/presentation/login_page.dart';
import 'package:quiz_app/feature/splash_screen/presentation/splash_screen_page.dart';
@ -15,6 +16,7 @@ class AppPages {
GetPage(
name: AppRoutes.loginPage,
page: () => LoginView(),
binding: LoginBinding(),
),
GetPage(
name: AppRoutes.homePage,

View File

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
class GlobalTextField extends StatelessWidget {
final TextEditingController controller;
final String? hintText;
const GlobalTextField({super.key, required this.controller, this.hintText});
@override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: hintText,
),
);
}
}

View File

@ -0,0 +1,6 @@
class APIEndpoint {
static const String baseUrl = "http://127.0.0.1:8000/api";
static const String login = "/login";
static const String loginGoogle = "/login/google";
}

View File

@ -0,0 +1,10 @@
import 'package:get/get_core/get_core.dart';
import 'package:get/get_instance/get_instance.dart';
import 'package:quiz_app/feature/login/controllers/login_controller.dart';
class LoginBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => LoginController());
}
}

View File

@ -0,0 +1,69 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:google_sign_in/google_sign_in.dart';
import 'package:flutter/material.dart';
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
class LoginController extends GetxController {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
var isPasswordHidden = true.obs;
void togglePasswordVisibility() {
isPasswordHidden.value = !isPasswordHidden.value;
}
final GoogleSignIn _googleSignIn = GoogleSignIn();
// Login menggunakan Flask Backend (Email & Password)
Future<void> loginWithEmail() async {
String email = emailController.text.trim();
String password = passwordController.text.trim();
try {
var response = await http.post(
Uri.parse(APIEndpoint.baseUrl + APIEndpoint.login),
body: jsonEncode({"email": email, "password": password}),
headers: {"Content-Type": "application/json"},
);
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
String token = data['token'];
Get.snackbar("Success", "Login successful!");
} else {
Get.snackbar("Error", "Invalid email or password");
}
} catch (e) {
Get.snackbar("Error", "Failed to connect to server");
}
}
// Login menggunakan Google (Tanpa Firebase)
Future<void> loginWithGoogle() async {
try {
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
if (googleUser == null) return;
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
String idToken = googleAuth.idToken ?? "";
var response = await http.post(
Uri.parse(APIEndpoint.baseUrl + APIEndpoint.loginGoogle),
body: jsonEncode({"token": idToken}),
headers: {"Content-Type": "application/json"},
);
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
String token = data['token']; // Simpan token untuk sesi login
Get.snackbar("Success", "Google login successful!");
} else {
Get.snackbar("Error", "Google login failed");
}
} catch (e) {
Get.snackbar("Error", "Google sign-in error");
}
}
}

View File

@ -1,15 +1,72 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:quiz_app/feature/login/controllers/login_controller.dart';
class LoginView extends StatelessWidget {
class LoginView extends GetView<LoginController> {
const LoginView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTap: () {},
child: Text("test work in background"),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text("Login Page", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 20),
// Email Input
TextField(
controller: controller.emailController,
decoration: InputDecoration(
labelText: "Email",
border: OutlineInputBorder(),
),
),
const SizedBox(height: 10),
// Password Input dengan fitur show/hide
Obx(() => TextField(
controller: controller.passwordController,
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,
),
),
)),
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,
),
),
],
),
),
),
);

View File

@ -137,7 +137,7 @@ packages:
source: hosted
version: "0.12.4+3"
http:
dependency: transitive
dependency: "direct main"
description:
name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f

View File

@ -37,6 +37,7 @@ dependencies:
logger: ^2.5.0
google_sign_in: ^6.2.2
http: ^1.3.0
dev_dependencies:
flutter_test:
@ -60,7 +61,9 @@ flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
assets:
- assets/
- assets/logo/
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg