From 9c330514a76768d5520279feee47176e54cf1eae Mon Sep 17 00:00:00 2001 From: orangdeso Date: Sat, 17 May 2025 22:03:41 +0700 Subject: [PATCH] Feat: done for features details passenger --- .dart_tool/package_config.json | 2 +- .../repositories/profil_repository_impl.dart | 45 +- lib/domain/bindings/profil_binding.dart | 7 +- lib/domain/models/user_entity.dart | 3 + .../repositories/profil_repository.dart | 11 + lib/domain/usecases/profil_usecase.dart | 32 ++ .../controllers/profil_controller.dart | 52 +- .../profile/pages/passenger_list_screen.dart | 482 ++++++++++++++++-- .../screens/routes/app_rountes.dart | 1 + 9 files changed, 590 insertions(+), 45 deletions(-) diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json index aff65ea..9631aa9 100644 --- a/.dart_tool/package_config.json +++ b/.dart_tool/package_config.json @@ -680,7 +680,7 @@ "languageVersion": "3.4" } ], - "generated": "2025-05-15T05:07:55.383919Z", + "generated": "2025-05-16T07:28:56.076101Z", "generator": "pub", "generatorVersion": "3.5.0", "flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0", diff --git a/lib/data/repositories/profil_repository_impl.dart b/lib/data/repositories/profil_repository_impl.dart index dd08a96..31828bb 100644 --- a/lib/data/repositories/profil_repository_impl.dart +++ b/lib/data/repositories/profil_repository_impl.dart @@ -1,9 +1,8 @@ // ignore_for_file: deprecated_member_use - +import 'dart:developer'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:e_porter/domain/repositories/profil_repository.dart'; import 'package:firebase_auth/firebase_auth.dart'; -import '../../_core/service/logger_service.dart'; import '../../domain/models/user_entity.dart'; class ProfilRepositoryImpl implements ProfilRepository { @@ -17,7 +16,7 @@ class ProfilRepositoryImpl implements ProfilRepository { try { DocumentReference docRef = await _firestore.collection('users').doc(userId).collection('passenger').add(passenger.toMap()); - logger.d("Passenger doc id: ${docRef.id}"); + log("Passenger doc id: ${docRef.id}"); } catch (e) { rethrow; } @@ -27,12 +26,30 @@ class ProfilRepositoryImpl implements ProfilRepository { Future> getPassengerById(String userId) async { try { QuerySnapshot querySnapshot = await _firestore.collection('users').doc(userId).collection('passenger').get(); - return querySnapshot.docs.map((doc) => PassengerModel.fromMap(doc.data() as Map)).toList(); + return querySnapshot.docs + .map((doc) => PassengerModel.fromMap({ + 'id': doc.id, + ...doc.data() as Map + })) + .toList(); } catch (e) { rethrow; } } + @override + Future deletePassenger({ + required String userId, + required String passengerId, + }) async { + try { + await _firestore.collection('users').doc(userId).collection('passenger').doc(passengerId).delete(); + log("Passenger deleted with ID: $passengerId"); + } catch (e) { + log("Error deleting passenger: $e"); + rethrow; + } + } @override Future getUserById(String userId) async { try { @@ -45,6 +62,26 @@ class ProfilRepositoryImpl implements ProfilRepository { rethrow; } } + + @override + Future updatePassenger({ + required String userId, + required String passengerId, + required PassengerModel passenger, + }) async { + try { + await _firestore + .collection('users') + .doc(userId) + .collection('passenger') + .doc(passengerId) + .update(passenger.toMap()); + log("Passenger updated with ID: $passengerId"); + } catch (e) { + log("Error updating passenger: $e"); + rethrow; + } + } @override Future changePassword({ diff --git a/lib/domain/bindings/profil_binding.dart b/lib/domain/bindings/profil_binding.dart index e7333d5..5d896a2 100644 --- a/lib/domain/bindings/profil_binding.dart +++ b/lib/domain/bindings/profil_binding.dart @@ -15,8 +15,10 @@ class ProfilBinding extends Bindings { Get.lazyPut(() => GetUserByIdUseCase(Get.find())); Get.lazyPut(() => ChangePasswordUseCase(Get.find())); Get.lazyPut(() => ChangePhoneUseCase(Get.find())); + Get.lazyPut(() => DeletePassengerUseCase(Get.find())); + Get.lazyPut(() => UpdatePassengerUseCase(Get.find())); // Get.lazyPut(() => ChangeEmailUseCase(Get.find())); - + Get.lazyPut( () => ProfilController( createPassengerUseCase: Get.find(), @@ -24,7 +26,8 @@ class ProfilBinding extends Bindings { getUserByIdUseCase: Get.find(), changePasswordUseCase: Get.find(), changePhoneUseCase: Get.find(), - // changeEmailUseCase: Get.find(), + deletePassengerUseCase: Get.find(), + updatePassengerUseCase: Get.find(), ), ); } diff --git a/lib/domain/models/user_entity.dart b/lib/domain/models/user_entity.dart index 6481393..f8750c4 100644 --- a/lib/domain/models/user_entity.dart +++ b/lib/domain/models/user_entity.dart @@ -116,12 +116,14 @@ class UserData { } class PassengerModel { + final String? id; // Added document ID final String typeId; final String noId; final String name; final String gender; PassengerModel({ + this.id, required this.typeId, required this.noId, required this.name, @@ -139,6 +141,7 @@ class PassengerModel { factory PassengerModel.fromMap(Map map) { return PassengerModel( + id: map['id'], typeId: map['typeId'] ?? '', noId: map['noId'] ?? '', name: map['name'] ?? '', diff --git a/lib/domain/repositories/profil_repository.dart b/lib/domain/repositories/profil_repository.dart index e359a70..70ae166 100644 --- a/lib/domain/repositories/profil_repository.dart +++ b/lib/domain/repositories/profil_repository.dart @@ -10,6 +10,17 @@ abstract class ProfilRepository { Future getUserById(String userId); + Future deletePassenger({ + required String userId, + required String passengerId, + }); + + Future updatePassenger({ + required String userId, + required String passengerId, + required PassengerModel passenger, + }); + Future changePassword({ required String oldPassword, required String newPassword, diff --git a/lib/domain/usecases/profil_usecase.dart b/lib/domain/usecases/profil_usecase.dart index 4636296..43df7ef 100644 --- a/lib/domain/usecases/profil_usecase.dart +++ b/lib/domain/usecases/profil_usecase.dart @@ -64,6 +64,38 @@ class ChangePhoneUseCase { } } +class DeletePassengerUseCase { + final ProfilRepository profilRepository; + DeletePassengerUseCase(this.profilRepository); + + Future call({ + required String userId, + required String passengerId, + }) { + return profilRepository.deletePassenger( + userId: userId, + passengerId: passengerId, + ); + } +} + +class UpdatePassengerUseCase { + final ProfilRepository profilRepository; + UpdatePassengerUseCase(this.profilRepository); + + Future call({ + required String userId, + required String passengerId, + required PassengerModel passenger, + }) { + return profilRepository.updatePassenger( + userId: userId, + passengerId: passengerId, + passenger: passenger, + ); + } +} + // class ChangeEmailUseCase { // final ProfilRepository profilRepository; // ChangeEmailUseCase(this.profilRepository); diff --git a/lib/presentation/controllers/profil_controller.dart b/lib/presentation/controllers/profil_controller.dart index 1d0d408..d24cc73 100644 --- a/lib/presentation/controllers/profil_controller.dart +++ b/lib/presentation/controllers/profil_controller.dart @@ -1,5 +1,4 @@ import 'dart:developer'; -import 'package:e_porter/_core/service/logger_service.dart'; import 'package:e_porter/_core/service/preferences_service.dart'; import 'package:e_porter/_core/utils/snackbar/snackbar_helper.dart'; import 'package:firebase_auth/firebase_auth.dart'; @@ -14,6 +13,8 @@ class ProfilController extends GetxController { final GetUserByIdUseCase getUserByIdUseCase; final ChangePasswordUseCase changePasswordUseCase; final ChangePhoneUseCase changePhoneUseCase; + final DeletePassengerUseCase deletePassengerUseCase; + final UpdatePassengerUseCase updatePassengerUseCase; // final ChangeEmailUseCase changeEmailUseCase; var passengerList = [].obs; @@ -21,6 +22,8 @@ class ProfilController extends GetxController { var isLoading = false.obs; var isChangingPassword = false.obs; var isChangingPhone = false.obs; + var isDeletingPassenger = false.obs; + var isUpdatingPassenger = false.obs; // var isChangingEmail = false.obs; ProfilController({ @@ -29,6 +32,8 @@ class ProfilController extends GetxController { required this.getUserByIdUseCase, required this.changePasswordUseCase, required this.changePhoneUseCase, + required this.deletePassengerUseCase, + required this.updatePassengerUseCase, // required this.changeEmailUseCase, }); @@ -97,7 +102,50 @@ class ProfilController extends GetxController { List list = await getPassengerByIdUseCase(userId); passengerList.assignAll(list); } catch (e) { - logger.e("Error fetching passengers: $e"); + log("Error fetching passengers: $e"); + } + } + + Future deletePassenger({ + required String userId, + required String passengerId, + }) async { + isDeletingPassenger.value = true; + try { + await deletePassengerUseCase( + userId: userId, + passengerId: passengerId, + ); + SnackbarHelper.showSuccess("Berhasil", "Data penumpang berhasil dihapus."); + return true; + } catch (e) { + log("Error deleting passenger: $e"); + SnackbarHelper.showError("Gagal", "Terjadi kesalahan saat menghapus data penumpang."); + return false; + } finally { + isDeletingPassenger.value = false; + } + } + + Future updatePassenger({ + required String userId, + required String passengerId, + required PassengerModel passenger, + }) async { + isUpdatingPassenger.value = true; + try { + await updatePassengerUseCase( + userId: userId, + passengerId: passengerId, + passenger: passenger, + ); + return true; + } catch (e) { + log("Error updating passenger: $e"); + SnackbarHelper.showError("Gagal", "Terjadi kesalahan saat mengupdate data penumpang."); + return false; + } finally { + isUpdatingPassenger.value = false; } } diff --git a/lib/presentation/screens/profile/pages/passenger_list_screen.dart b/lib/presentation/screens/profile/pages/passenger_list_screen.dart index 68751b3..8ec001e 100644 --- a/lib/presentation/screens/profile/pages/passenger_list_screen.dart +++ b/lib/presentation/screens/profile/pages/passenger_list_screen.dart @@ -2,14 +2,21 @@ import 'package:e_porter/_core/component/appbar/appbar_component.dart'; import 'package:e_porter/_core/component/button/button_fill.dart'; import 'package:e_porter/_core/component/card/custome_shadow_cotainner.dart'; +import 'package:e_porter/_core/component/text_field/dropdown/dropdown_component.dart'; +import 'package:e_porter/_core/component/text_field/text_input/text_field_component.dart'; import 'package:e_porter/_core/constants/colors.dart'; import 'package:e_porter/_core/constants/typography.dart'; +import 'package:e_porter/_core/service/preferences_service.dart'; +import 'package:e_porter/_core/utils/formatter/uppercase_helper.dart'; +import 'package:e_porter/_core/utils/snackbar/snackbar_helper.dart'; +import 'package:e_porter/_core/validators/validators.dart'; +import 'package:e_porter/domain/models/user_entity.dart'; +import 'package:e_porter/presentation/controllers/profil_controller.dart'; import 'package:e_porter/presentation/screens/routes/app_rountes.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:zoom_tap_animation/zoom_tap_animation.dart'; class PassengerListScreen extends StatefulWidget { const PassengerListScreen({super.key}); @@ -19,6 +26,104 @@ class PassengerListScreen extends StatefulWidget { } class _PassengerListScreenState extends State { + final ValueNotifier selectedGender = ValueNotifier('Laki-laki'); + final _profileController = Get.find(); + + // Controllers for edit form + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _noIdController = TextEditingController(); + String selectedTypeId = 'KTP'; + + @override + void dispose() { + _nameController.dispose(); + _noIdController.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + _loadPassengers(); + } + + Future _loadPassengers() async { + final userData = await PreferencesService.getUserData(); + if (userData == null || userData.uid.isEmpty) { + SnackbarHelper.showError('Error', 'User ID tidak ditemukan, silakan login kembali'); + return; + } + final userId = userData.uid; + await _profileController.fetchPassangerById(userId); + } + + Future _deletePassenger(PassengerModel passenger) async { + if (passenger.id == null) { + SnackbarHelper.showError('Error', 'ID Penumpang tidak valid'); + return; + } + + final userData = await PreferencesService.getUserData(); + if (userData == null || userData.uid.isEmpty) { + SnackbarHelper.showError('Error', 'User ID tidak ditemukan, silakan login kembali'); + return; + } + + final userId = userData.uid; + final success = await _profileController.deletePassenger( + userId: userId, + passengerId: passenger.id!, + ); + + if (success) { + await _loadPassengers(); + } + } + + Future _updatePassenger(PassengerModel passenger) async { + if (passenger.id == null) { + SnackbarHelper.showError('Error', 'ID Penumpang tidak valid'); + return; + } + + if (_nameController.text.isEmpty) { + SnackbarHelper.showError('Error', 'Nama tidak boleh kosong'); + return; + } + + if (_noIdController.text.isEmpty) { + SnackbarHelper.showError('Error', 'Nomor ID tidak boleh kosong'); + return; + } + + final userData = await PreferencesService.getUserData(); + if (userData == null || userData.uid.isEmpty) { + SnackbarHelper.showError('Error', 'User ID tidak ditemukan, silakan login kembali'); + return; + } + + final userId = userData.uid; + + final updatedPassenger = PassengerModel( + id: passenger.id, + name: _nameController.text, + typeId: selectedTypeId, + noId: _noIdController.text, + gender: selectedGender.value, + ); + + final success = await _profileController.updatePassenger( + userId: userId, + passengerId: passenger.id!, + passenger: updatedPassenger, + ); + + if (success) { + SnackbarHelper.showSuccess('Berhasil', 'Data penumpang berhasil diperbarui'); + await _loadPassengers(); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -33,65 +138,370 @@ class _PassengerListScreenState extends State { ), body: SafeArea( child: Padding( - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h), - child: ListView.builder( - itemCount: 2, - itemBuilder: (context, index) { - return Padding( - padding: EdgeInsets.only(bottom: 16.h), - child: _buildCardItem( - value: 'Name', - onTap: () { - // Get.toNamed(Routes.passengerDetail); - }, + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h), + child: Obx( + () { + if (_profileController.passengerList.isEmpty) { + return Center( + child: TypographyStyles.body( + 'Bulum ada penumpang', + color: GrayColors.gray400, + fontWeight: FontWeight.w500, ), ); - }, - )), + } + return ListView.builder( + itemCount: _profileController.passengerList.length, + shrinkWrap: true, + itemBuilder: (context, index) { + final passenger = _profileController.passengerList[index]; + return Padding( + padding: EdgeInsets.only(bottom: 16.h), + child: _buildPassengerCard(passenger), + ); + }, + ); + }, + ), + ), ), bottomNavigationBar: CustomeShadowCotainner( child: ButtonFill( text: 'Tambah Penumpang', textColor: Colors.white, - onTap: () { - Get.toNamed(Routes.ADDPASSENGER); + onTap: () async { + final result = await Get.toNamed(Routes.ADDPASSENGER); + if (result == true) { + await _loadPassengers(); + setState(() {}); + } }, ), ), ); } - Widget _buildCardItem({required String value, required VoidCallback onTap}) { - return ZoomTapAnimation( - child: GestureDetector( - onTap: onTap, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10.r), - border: Border.all(color: GrayColors.gray200), + Widget _buildPassengerCard(PassengerModel passenger) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10.r), + border: Border.all(color: GrayColors.gray200), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TypographyStyles.caption( + 'Dewasa (${passenger.gender})', + fontWeight: FontWeight.w400, + color: GrayColors.gray500, + ), + TypographyStyles.body('${passenger.name}', color: GrayColors.gray800), + ], + ), ), + PopupMenuButton( + icon: Icon(Icons.more_vert_outlined, color: GrayColors.gray500, size: 24.w), + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.r), + ), + onSelected: (value) { + if (value == 'delete') { + _showDeleteConfirmation(passenger); + } else if (value == 'details') { + Get.bottomSheet( + Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.r), + topRight: Radius.circular(10.r), + ), + ), + child: _buildDetailContent(passenger), + ), + isScrollControlled: true, + ); + } else if (value == 'edit') { + Get.bottomSheet( + Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.r), + topRight: Radius.circular(10.r), + ), + ), + child: _buildEditContent(passenger), + ), + isScrollControlled: true, + ); + } + }, + itemBuilder: (context) => [ + PopupMenuItem( + value: 'details', + child: Row( + children: [ + Icon(Icons.person, size: 18, color: GrayColors.gray600), + SizedBox(width: 8.w), + TypographyStyles.caption('Lihat Detail', color: GrayColors.gray600), + ], + ), + ), + PopupMenuItem( + value: 'edit', + child: Row( + children: [ + Icon(Icons.edit, size: 18, color: GrayColors.gray600), + SizedBox(width: 8.w), + TypographyStyles.caption('Edit', color: GrayColors.gray600), + ], + ), + ), + PopupMenuItem( + value: 'delete', + child: Row( + children: [ + Icon(Icons.delete, size: 18, color: Colors.red), + SizedBox(width: 8.w), + TypographyStyles.caption('Hapus', color: Colors.red), + ], + ), + ), + ], + ), + ], + ), + ); + } + + Widget _buildDetailContent(PassengerModel passenger) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Align( + alignment: Alignment.center, + child: Container( + width: MediaQuery.of(context).size.height * 0.070, + height: 6.h, + decoration: BoxDecoration( + color: GrayColors.gray200, + borderRadius: BorderRadius.circular(20.r), + ), + ), + ), + Padding( + padding: EdgeInsets.only(bottom: 20.h, top: 16.h), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TypographyStyles.h6('Detail Penumpang', color: GrayColors.gray800), + ], + ), + ), + TypographyStyles.caption('Nama Penumpang', color: GrayColors.gray400, fontWeight: FontWeight.w400), + Padding( + padding: EdgeInsets.only(top: 6.h), + child: TypographyStyles.body('${passenger.name}', color: GrayColors.gray800), + ), + SizedBox(height: 16.h), + TypographyStyles.caption('Identitas', color: GrayColors.gray400, fontWeight: FontWeight.w400), + Padding( + padding: EdgeInsets.only(top: 6.h), + child: TypographyStyles.body('${passenger.typeId} - ${passenger.noId}', color: GrayColors.gray800), + ), + SizedBox(height: 16.h), + TypographyStyles.caption('Jenis Kelamin', color: GrayColors.gray400, fontWeight: FontWeight.w400), + Padding( + padding: EdgeInsets.only(top: 6.h), + child: TypographyStyles.body('${passenger.gender}', color: GrayColors.gray800), + ), + ], + ); + } + + void _showDeleteConfirmation(PassengerModel passenger) { + Get.dialog( + AlertDialog( + backgroundColor: Colors.white, + title: TypographyStyles.h6('Konfirmasi', color: GrayColors.gray800), + content: TypographyStyles.body( + 'Apakah Anda yakin ingin menghapus data penumpang ${passenger.name}?', + fontWeight: FontWeight.w500, + color: GrayColors.gray500, + maxlines: 3, + ), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: TypographyStyles.body('Batal', color: GrayColors.gray600), + ), + TextButton( + onPressed: () async { + Get.back(); + await _deletePassenger(passenger); + }, + child: TypographyStyles.body('Hapus', color: RedColors.red500), + ), + ], + ), + ); + } + + Widget _buildEditContent(PassengerModel passenger) { + _nameController.text = passenger.name; + _noIdController.text = passenger.noId; + selectedTypeId = passenger.typeId; + selectedGender.value = passenger.gender; + + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Align( + alignment: Alignment.center, + child: Container( + width: MediaQuery.of(context).size.height * 0.070, + height: 6.h, + decoration: BoxDecoration( + color: GrayColors.gray200, + borderRadius: BorderRadius.circular(20.r), + ), + ), + ), + Padding( + padding: EdgeInsets.only(bottom: 20.h, top: 16.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TypographyStyles.h6('Edit Penumpang', color: GrayColors.gray800), + ], + ), + ), + TypographyStyles.body('Nama Lengkap', color: GrayColors.gray600, fontWeight: FontWeight.w400), + SizedBox(height: 16.w), + TextFieldComponent( + controller: _nameController, + hintText: 'Masukkan nama lengkap', + validators: Validators.validatorName, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z\s]')), + UpperCaseTextFormatter(), + ], + textInputType: TextInputType.text, + ), + SizedBox(height: 20.h), + Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - TypographyStyles.caption('Dewasa', fontWeight: FontWeight.w400, color: GrayColors.gray500), - TypographyStyles.body(value, color: GrayColors.gray800) + TypographyStyles.body('Tipe ID', color: GrayColors.gray600, fontWeight: FontWeight.w400), + SizedBox(height: 16.h), + DropdownComponent( + hintText: "Pilih jenis dokument", + items: ['KTP', 'Pasport'], + value: selectedTypeId, + onChanged: (value) { + setState(() { + selectedTypeId = value!; + }); + }, + ), ], ), - SvgPicture.asset( - 'assets/icons/ic_more_than.svg', - color: GrayColors.gray500, - width: 20.w, - height: 20.h, - ), + SizedBox(width: 16.w), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TypographyStyles.body('No ID', color: GrayColors.gray600, fontWeight: FontWeight.w400), + SizedBox(height: 16.h), + TextFieldComponent( + controller: _noIdController, + hintText: 'Masukkan ID', + validators: Validators.validatorNoID, + textInputType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(16), + ], + ) + ], + ), + ) ], ), - ), + SizedBox(height: 20.w), + TypographyStyles.body('Jenis Kelamin', color: GrayColors.gray600, fontWeight: FontWeight.w400), + Row( + children: [ + _buildRadioButton(context, label: 'Laki-laki', value: 'Laki-laki'), + SizedBox(width: 40.h), + _buildRadioButton(context, label: 'Perempuan', value: 'Perempuan') + ], + ), + SizedBox(height: 20.h), + ButtonFill( + text: 'Simpan', + textColor: Colors.white, + onTap: () async { + bool isDataChanged = passenger.name != _nameController.text || + passenger.typeId != selectedTypeId || + passenger.noId != _noIdController.text || + passenger.gender != selectedGender.value; + + if (!isDataChanged) { + SnackbarHelper.showInfo('Info', 'Tidak ada perubahan data yang dilakukan'); + return; + } + + if (Get.isBottomSheetOpen ?? false) { + Get.back(); + } + await _updatePassenger(passenger); + }, + ) + ], ), ); } + + Widget _buildRadioButton( + BuildContext context, { + required String label, + required String value, + }) { + return ValueListenableBuilder( + valueListenable: selectedGender, + builder: (context, selected, child) { + return Row( + children: [ + Radio( + value: value, + groupValue: selected, + activeColor: PrimaryColors.primary800, + onChanged: (val) { + selectedGender.value = val!; + }, + ), + SizedBox(width: 10.w), + TypographyStyles.body(label, color: GrayColors.gray800, fontWeight: FontWeight.w500) + ], + ); + }, + ); + } } diff --git a/lib/presentation/screens/routes/app_rountes.dart b/lib/presentation/screens/routes/app_rountes.dart index 7fb53de..0e167d4 100644 --- a/lib/presentation/screens/routes/app_rountes.dart +++ b/lib/presentation/screens/routes/app_rountes.dart @@ -223,6 +223,7 @@ class AppRoutes { GetPage( name: Routes.PASSENGERLIST, page: () => PassengerListScreen(), + binding: ProfilBinding(), ), ]; }