fix: register page

This commit is contained in:
akhdanre 2025-05-23 15:04:04 +07:00
parent bfd959a5df
commit bfa253ceec
11 changed files with 264 additions and 51 deletions

View File

@ -1,12 +1,14 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/data/controllers/user_controller.dart'; import 'package:quiz_app/data/controllers/user_controller.dart';
import 'package:quiz_app/data/providers/dio_client.dart'; import 'package:quiz_app/data/providers/dio_client.dart';
import 'package:quiz_app/data/services/connection_service.dart';
import 'package:quiz_app/data/services/user_storage_service.dart'; import 'package:quiz_app/data/services/user_storage_service.dart';
class InitialBindings extends Bindings { class InitialBindings extends Bindings {
@override @override
void dependencies() { void dependencies() {
Get.put<UserStorageService>(UserStorageService()); Get.put<UserStorageService>(UserStorageService());
Get.put(ConnectionService());
Get.putAsync(() => ApiClient().init()); Get.putAsync(() => ApiClient().init());
Get.put<UserController>(UserController(Get.find<UserStorageService>())); Get.put<UserController>(UserController(Get.find<UserStorageService>()));
} }

View File

@ -0,0 +1,17 @@
import 'package:quiz_app/core/utils/custom_notification.dart';
class ConnectionNotification {
static void internetConnected() {
CustomNotification.success(
title: "Terkoneksi kembali",
message: "Terhubugn dengan koneksi",
);
}
static void noInternedConnection() {
CustomNotification.error(
title: "Tidak ada internet",
message: "cek kembali koneksi internet kamu",
);
}
}

View File

@ -0,0 +1,56 @@
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get/get.dart';
/// [ConnectionService] is a GetX Service that monitors internet connectivity status.
///
/// It utilizes the [Connectivity] class from the `connectivity_plus` package.
class ConnectionService extends GetxService {
final Connectivity _connectivity = Connectivity();
/// Subscription to the connectivity change stream.
late StreamSubscription<List<ConnectivityResult>> _subscription;
/// Reactive boolean to indicate the current internet connection status.
/// `true` means the device is connected to the internet via Wi-Fi, mobile data, or other means.
final RxBool isConnected = true.obs;
bool get isCurrentlyConnected => isConnected.value;
/// Called when the service is first initialized.
@override
void onInit() {
super.onInit();
_initConnectivity();
_subscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
}
/// Checks the initial connectivity status when the service is initialized.
Future<void> _initConnectivity() async {
try {
final result = await _connectivity.checkConnectivity();
_updateConnectionStatus(result); // Wrap in a list for consistency
} catch (e) {
isConnected.value = false;
}
}
/// Callback function to handle changes in connectivity status.
/// @param results A list of [ConnectivityResult] representing all active network connections.
void _updateConnectionStatus(List<ConnectivityResult> results) {
// If all results are `none`, the device is considered offline.
isConnected.value = results.any((result) => result != ConnectivityResult.none);
}
Future<bool> checkConnection() async {
final result = await _connectivity.checkConnectivity();
return !result.contains(ConnectivityResult.none);
}
/// Cancels the connectivity subscription when the service is closed.
@override
void onClose() {
_subscription.cancel();
super.onClose();
}
}

View File

@ -2,6 +2,7 @@ import 'package:get/get_core/get_core.dart';
import 'package:get/get_instance/get_instance.dart'; import 'package:get/get_instance/get_instance.dart';
import 'package:quiz_app/data/controllers/user_controller.dart'; import 'package:quiz_app/data/controllers/user_controller.dart';
import 'package:quiz_app/data/services/auth_service.dart'; import 'package:quiz_app/data/services/auth_service.dart';
import 'package:quiz_app/data/services/connection_service.dart';
import 'package:quiz_app/data/services/google_auth_service.dart'; import 'package:quiz_app/data/services/google_auth_service.dart';
import 'package:quiz_app/data/services/user_storage_service.dart'; import 'package:quiz_app/data/services/user_storage_service.dart';
import 'package:quiz_app/feature/login/controllers/login_controller.dart'; import 'package:quiz_app/feature/login/controllers/login_controller.dart';
@ -11,6 +12,14 @@ class LoginBinding extends Bindings {
void dependencies() { void dependencies() {
Get.lazyPut<GoogleAuthService>(() => GoogleAuthService()); Get.lazyPut<GoogleAuthService>(() => GoogleAuthService());
Get.lazyPut(() => AuthService()); Get.lazyPut(() => AuthService());
Get.lazyPut(() => LoginController(Get.find<AuthService>(), Get.find<UserStorageService>(), Get.find<UserController>(), Get.find<GoogleAuthService>())); Get.lazyPut(
() => LoginController(
Get.find<AuthService>(),
Get.find<UserStorageService>(),
Get.find<UserController>(),
Get.find<GoogleAuthService>(),
Get.find<ConnectionService>(),
),
);
} }
} }

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/app/routes/app_pages.dart'; import 'package:quiz_app/app/routes/app_pages.dart';
import 'package:quiz_app/component/global_button.dart'; import 'package:quiz_app/component/global_button.dart';
import 'package:quiz_app/core/helper/connection_check.dart';
import 'package:quiz_app/core/utils/custom_notification.dart'; import 'package:quiz_app/core/utils/custom_notification.dart';
import 'package:quiz_app/core/utils/logger.dart'; import 'package:quiz_app/core/utils/logger.dart';
import 'package:quiz_app/data/controllers/user_controller.dart'; import 'package:quiz_app/data/controllers/user_controller.dart';
@ -9,6 +10,7 @@ import 'package:quiz_app/data/entity/user/user_entity.dart';
import 'package:quiz_app/data/models/login/login_request_model.dart'; import 'package:quiz_app/data/models/login/login_request_model.dart';
import 'package:quiz_app/data/models/login/login_response_model.dart'; import 'package:quiz_app/data/models/login/login_response_model.dart';
import 'package:quiz_app/data/services/auth_service.dart'; import 'package:quiz_app/data/services/auth_service.dart';
import 'package:quiz_app/data/services/connection_service.dart';
import 'package:quiz_app/data/services/google_auth_service.dart'; import 'package:quiz_app/data/services/google_auth_service.dart';
import 'package:quiz_app/data/services/user_storage_service.dart'; import 'package:quiz_app/data/services/user_storage_service.dart';
@ -17,12 +19,14 @@ class LoginController extends GetxController {
final UserStorageService _userStorageService; final UserStorageService _userStorageService;
final UserController _userController; final UserController _userController;
final GoogleAuthService _googleAuthService; final GoogleAuthService _googleAuthService;
final ConnectionService _connectionService;
LoginController( LoginController(
this._authService, this._authService,
this._userStorageService, this._userStorageService,
this._userController, this._userController,
this._googleAuthService, this._googleAuthService,
this._connectionService,
); );
final TextEditingController emailController = TextEditingController(); final TextEditingController emailController = TextEditingController();
@ -37,7 +41,25 @@ class LoginController extends GetxController {
super.onInit(); super.onInit();
emailController.addListener(validateFields); emailController.addListener(validateFields);
passwordController.addListener(validateFields); passwordController.addListener(validateFields);
ever(_connectionService.isConnected, (value) {
if (!value) {
ConnectionNotification.noInternedConnection();
} else {
ConnectionNotification.internetConnected();
} }
});
}
@override
void onReady() {
if (!_connectionService.isCurrentlyConnected) {
ConnectionNotification.noInternedConnection();
}
super.onReady();
}
void checkConnection() async {}
void validateFields() { void validateFields() {
final isEmailNotEmpty = emailController.text.trim().isNotEmpty; final isEmailNotEmpty = emailController.text.trim().isNotEmpty;
@ -59,6 +81,10 @@ class LoginController extends GetxController {
return; return;
} }
if (!_connectionService.isCurrentlyConnected) {
ConnectionNotification.noInternedConnection();
return;
}
try { try {
isLoading.value = true; isLoading.value = true;
@ -81,8 +107,11 @@ class LoginController extends GetxController {
} }
} }
/// **🔹 Login via Google**
Future<void> loginWithGoogle() async { Future<void> loginWithGoogle() async {
if (!_connectionService.isCurrentlyConnected) {
ConnectionNotification.noInternedConnection();
return;
}
try { try {
final user = await _googleAuthService.signIn(); final user = await _googleAuthService.signIn();
if (user == null) { if (user == null) {
@ -112,7 +141,6 @@ class LoginController extends GetxController {
void goToRegsPage() => Get.toNamed(AppRoutes.registerPage); void goToRegsPage() => Get.toNamed(AppRoutes.registerPage);
/// Helper untuk convert LoginResponseModel ke UserEntity
UserEntity _convertLoginResponseToUserEntity(LoginResponseModel response) { UserEntity _convertLoginResponseToUserEntity(LoginResponseModel response) {
logC.i("user id : ${response.id}"); logC.i("user id : ${response.id}");
return UserEntity( return UserEntity(

View File

@ -16,7 +16,7 @@ class LoginView extends GetView<LoginController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColors.background, // background soft clean backgroundColor: AppColors.background,
body: SafeArea( body: SafeArea(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),

View File

@ -1,11 +1,17 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/data/services/auth_service.dart'; import 'package:quiz_app/data/services/auth_service.dart';
import 'package:quiz_app/data/services/connection_service.dart';
import 'package:quiz_app/feature/register/controller/register_controller.dart'; import 'package:quiz_app/feature/register/controller/register_controller.dart';
class RegisterBinding extends Bindings { class RegisterBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
Get.lazyPut(() => AuthService()); Get.lazyPut(() => AuthService());
Get.lazyPut(() => RegisterController(Get.find<AuthService>())); Get.lazyPut(
() => RegisterController(
Get.find<AuthService>(),
Get.find<ConnectionService>(),
),
);
} }
} }

View File

@ -1,14 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/core/helper/connection_check.dart';
import 'package:quiz_app/core/utils/custom_floating_loading.dart'; import 'package:quiz_app/core/utils/custom_floating_loading.dart';
import 'package:quiz_app/core/utils/custom_notification.dart'; import 'package:quiz_app/core/utils/custom_notification.dart';
import 'package:quiz_app/data/models/register/register_request.dart'; import 'package:quiz_app/data/models/register/register_request.dart';
import 'package:quiz_app/data/services/auth_service.dart'; import 'package:quiz_app/data/services/auth_service.dart';
import 'package:quiz_app/data/services/connection_service.dart';
class RegisterController extends GetxController { class RegisterController extends GetxController {
final AuthService _authService; final AuthService _authService;
final ConnectionService _connectionService;
RegisterController(this._authService); RegisterController(
this._authService,
this._connectionService,
);
final TextEditingController nameController = TextEditingController(); final TextEditingController nameController = TextEditingController();
final TextEditingController bDateController = TextEditingController(); final TextEditingController bDateController = TextEditingController();
@ -20,6 +26,14 @@ class RegisterController extends GetxController {
var isPasswordHidden = true.obs; var isPasswordHidden = true.obs;
var isConfirmPasswordHidden = true.obs; var isConfirmPasswordHidden = true.obs;
@override
void onReady() {
if (!_connectionService.isCurrentlyConnected) {
ConnectionNotification.noInternedConnection();
}
super.onReady();
}
void togglePasswordVisibility() { void togglePasswordVisibility() {
isPasswordHidden.value = !isPasswordHidden.value; isPasswordHidden.value = !isPasswordHidden.value;
} }
@ -29,6 +43,11 @@ class RegisterController extends GetxController {
} }
Future<void> onRegister() async { Future<void> onRegister() async {
if (!_connectionService.isCurrentlyConnected) {
ConnectionNotification.noInternedConnection();
return;
}
String email = emailController.text.trim(); String email = emailController.text.trim();
String name = nameController.text.trim(); String name = nameController.text.trim();
String birthDate = bDateController.text.trim(); String birthDate = bDateController.text.trim();

View File

@ -18,12 +18,16 @@ class RegisterView extends GetView<RegisterController> {
body: SafeArea( body: SafeArea(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: ListView( child: Column(
children: [ children: [
Padding( Expanded(
padding: const EdgeInsets.symmetric(vertical: 40), child: SingleChildScrollView(
child: AppName(), child: Column(
), children: [
buildBackHeader(),
const SizedBox(height: 30),
AppName(),
const SizedBox(height: 40),
LabelTextField( LabelTextField(
label: context.tr('register_title'), label: context.tr('register_title'),
fontSize: 24, fontSize: 24,
@ -65,7 +69,11 @@ class RegisterView extends GetView<RegisterController> {
onToggleVisibility: controller.toggleConfirmPasswordVisibility, onToggleVisibility: controller.toggleConfirmPasswordVisibility,
), ),
), ),
const SizedBox(height: 40), ],
),
),
),
const SizedBox(height: 20),
GlobalButton( GlobalButton(
onPressed: controller.onRegister, onPressed: controller.onRegister,
text: context.tr('register_button'), text: context.tr('register_button'),
@ -76,4 +84,23 @@ class RegisterView extends GetView<RegisterController> {
), ),
); );
} }
Widget buildBackHeader({String title = ""}) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Get.back(),
),
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
],
);
}
} }

View File

@ -49,6 +49,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.0" version: "1.19.0"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -65,6 +81,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.0.8"
dbus:
dependency: transitive
description:
name: dbus
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
dio: dio:
dependency: "direct main" dependency: "direct main"
description: description:
@ -333,6 +357,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -397,6 +429,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.5" version: "4.2.5"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "6.0.2"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -578,6 +618,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.5.0"
sdks: sdks:
dart: ">=3.6.0 <4.0.0" dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0" flutter: ">=3.27.0"

View File

@ -45,6 +45,7 @@ dependencies:
socket_io_client: ^3.1.2 socket_io_client: ^3.1.2
easy_localization: ^3.0.7+1 easy_localization: ^3.0.7+1
percent_indicator: ^4.2.5 percent_indicator: ^4.2.5
connectivity_plus: ^6.1.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: