From b20b414a217dff8d3c3088f18883ec0bd3788d23 Mon Sep 17 00:00:00 2001 From: kicap Date: Mon, 17 Jul 2023 04:39:00 +0800 Subject: [PATCH] added kode otp page, input informasi diri page, and user index tracking page, repair the back button on UserIndexTrackingView --- lib/app/app.dart | 15 + lib/app/app.router.dart | 299 +++++++++++++++++- lib/app/core/custom_base_view_model.dart | 37 +++ .../input_informasi_diri_view.dart | 164 +++++++++- .../input_informasi_diri_view_model.dart | 43 ++- .../masukan_no_hp/masukan_no_hp_view.dart | 7 +- .../verifikasi_no_hp_view.dart | 129 +++++++- .../verifikasi_no_hp_view_model.dart | 19 +- lib/ui/views/login_user/login_user_view.dart | 8 +- .../login_user/login_user_view_model.dart | 11 +- .../user_ui/akun_user/akun_user_view.dart | 31 ++ .../akun_user/akun_user_view_model.dart | 5 + .../makanan_list/makanan_list_view.dart | 31 ++ .../makanan_list/makanan_list_view_model.dart | 5 + .../pesanan_list/pesanan_list_view.dart | 31 ++ .../pesanan_list/pesanan_list_view_model.dart | 5 + .../reservasi_meja/reservasi_meja_view.dart | 31 ++ .../reservasi_meja_view_model.dart | 5 + .../user_index_tracking_view.dart | 89 ++++++ .../user_index_tracking_view_model.dart | 110 +++++++ pubspec.lock | 8 + pubspec.yaml | 1 + 22 files changed, 1051 insertions(+), 33 deletions(-) create mode 100644 lib/ui/views/user_ui/akun_user/akun_user_view.dart create mode 100644 lib/ui/views/user_ui/akun_user/akun_user_view_model.dart create mode 100644 lib/ui/views/user_ui/makanan_list/makanan_list_view.dart create mode 100644 lib/ui/views/user_ui/makanan_list/makanan_list_view_model.dart create mode 100644 lib/ui/views/user_ui/pesanan_list/pesanan_list_view.dart create mode 100644 lib/ui/views/user_ui/pesanan_list/pesanan_list_view_model.dart create mode 100644 lib/ui/views/user_ui/reservasi_meja/reservasi_meja_view.dart create mode 100644 lib/ui/views/user_ui/reservasi_meja/reservasi_meja_view_model.dart create mode 100644 lib/ui/views/user_ui/user_index_tracking/user_index_tracking_view.dart create mode 100644 lib/ui/views/user_ui/user_index_tracking/user_index_tracking_view_model.dart diff --git a/lib/app/app.dart b/lib/app/app.dart index 58ebbbd..cceb45d 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,3 +1,8 @@ +import 'package:reza_app/ui/views/user_ui/akun_user/akun_user_view.dart'; +import 'package:reza_app/ui/views/user_ui/makanan_list/makanan_list_view.dart'; +import 'package:reza_app/ui/views/user_ui/pesanan_list/pesanan_list_view.dart'; +import 'package:reza_app/ui/views/user_ui/reservasi_meja/reservasi_meja_view.dart'; +import 'package:reza_app/ui/views/user_ui/user_index_tracking/user_index_tracking_view.dart'; import 'package:stacked_services/stacked_services.dart'; import 'package:stacked/stacked_annotations.dart'; @@ -16,6 +21,16 @@ import '../ui/views/splash_screen/splash_screen_view.dart'; MaterialRoute(page: MasukanNoHpView), MaterialRoute(page: VerifikasiNoHpView), MaterialRoute(page: InputInformasiDiriView), + MaterialRoute( + page: UserIndexTrackingView, + fullscreenDialog: true, + children: [ + MaterialRoute(page: ReservasiMejaView, initial: true), + MaterialRoute(page: MakananListView), + MaterialRoute(page: PesananListView), + MaterialRoute(page: AkunUserView), + ], + ), ], // dialogs: [ // StackedDialog(classType: AddSiswaDialogView), diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart index cb37ba6..f496e39 100644 --- a/lib/app/app.router.dart +++ b/lib/app/app.router.dart @@ -5,7 +5,7 @@ // ************************************************************************** // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:flutter/material.dart' as _i7; +import 'package:flutter/material.dart' as _i8; import 'package:flutter/material.dart'; import 'package:reza_app/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view.dart' as _i6; @@ -15,8 +15,18 @@ import 'package:reza_app/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_ as _i5; import 'package:reza_app/ui/views/login_user/login_user_view.dart' as _i3; import 'package:reza_app/ui/views/splash_screen/splash_screen_view.dart' as _i2; +import 'package:reza_app/ui/views/user_ui/akun_user/akun_user_view.dart' + as _i12; +import 'package:reza_app/ui/views/user_ui/makanan_list/makanan_list_view.dart' + as _i10; +import 'package:reza_app/ui/views/user_ui/pesanan_list/pesanan_list_view.dart' + as _i11; +import 'package:reza_app/ui/views/user_ui/reservasi_meja/reservasi_meja_view.dart' + as _i9; +import 'package:reza_app/ui/views/user_ui/user_index_tracking/user_index_tracking_view.dart' + as _i7; import 'package:stacked/stacked.dart' as _i1; -import 'package:stacked_services/stacked_services.dart' as _i8; +import 'package:stacked_services/stacked_services.dart' as _i13; class Routes { static const splashScreenView = '/'; @@ -29,12 +39,15 @@ class Routes { static const inputInformasiDiriView = '/input-informasi-diri-view'; + static const userIndexTrackingView = '/user-index-tracking-view'; + static const all = { splashScreenView, loginUserView, masukanNoHpView, verifikasiNoHpView, inputInformasiDiriView, + userIndexTrackingView, }; } @@ -60,36 +73,146 @@ class StackedRouter extends _i1.RouterBase { Routes.inputInformasiDiriView, page: _i6.InputInformasiDiriView, ), + _i1.RouteDef( + Routes.userIndexTrackingView, + page: _i7.UserIndexTrackingView, + ), ]; final _pagesMap = { _i2.SplashScreenView: (data) { - return _i7.MaterialPageRoute( + return _i8.MaterialPageRoute( builder: (context) => const _i2.SplashScreenView(), settings: data, ); }, _i3.LoginUserView: (data) { - return _i7.MaterialPageRoute( + return _i8.MaterialPageRoute( builder: (context) => const _i3.LoginUserView(), settings: data, ); }, _i4.MasukanNoHpView: (data) { - return _i7.MaterialPageRoute( + return _i8.MaterialPageRoute( builder: (context) => const _i4.MasukanNoHpView(), settings: data, ); }, _i5.VerifikasiNoHpView: (data) { - return _i7.MaterialPageRoute( + return _i8.MaterialPageRoute( builder: (context) => const _i5.VerifikasiNoHpView(), settings: data, ); }, _i6.InputInformasiDiriView: (data) { - return _i7.MaterialPageRoute( - builder: (context) => const _i6.InputInformasiDiriView(), + final args = data.getArgs(nullOk: false); + return _i8.MaterialPageRoute( + builder: (context) => + _i6.InputInformasiDiriView(key: args.key, noHp: args.noHp), + settings: data, + ); + }, + _i7.UserIndexTrackingView: (data) { + return _i8.MaterialPageRoute( + builder: (context) => const _i7.UserIndexTrackingView(), + settings: data, + fullscreenDialog: true, + ); + }, + }; + + @override + List<_i1.RouteDef> get routes => _routes; + @override + Map get pagesMap => _pagesMap; +} + +class InputInformasiDiriViewArguments { + const InputInformasiDiriViewArguments({ + this.key, + required this.noHp, + }); + + final _i8.Key? key; + + final String noHp; + + @override + String toString() { + return '{"key": "$key", "noHp": "$noHp"}'; + } + + @override + bool operator ==(covariant InputInformasiDiriViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && other.noHp == noHp; + } + + @override + int get hashCode { + return key.hashCode ^ noHp.hashCode; + } +} + +class UserIndexTrackingViewRoutes { + static const reservasiMejaView = ''; + + static const makananListView = 'makanan-list-view'; + + static const pesananListView = 'pesanan-list-view'; + + static const akunUserView = 'akun-user-view'; + + static const all = { + reservasiMejaView, + makananListView, + pesananListView, + akunUserView, + }; +} + +class UserIndexTrackingViewRouter extends _i1.RouterBase { + final _routes = <_i1.RouteDef>[ + _i1.RouteDef( + UserIndexTrackingViewRoutes.reservasiMejaView, + page: _i9.ReservasiMejaView, + ), + _i1.RouteDef( + UserIndexTrackingViewRoutes.makananListView, + page: _i10.MakananListView, + ), + _i1.RouteDef( + UserIndexTrackingViewRoutes.pesananListView, + page: _i11.PesananListView, + ), + _i1.RouteDef( + UserIndexTrackingViewRoutes.akunUserView, + page: _i12.AkunUserView, + ), + ]; + + final _pagesMap = { + _i9.ReservasiMejaView: (data) { + return _i8.MaterialPageRoute( + builder: (context) => const _i9.ReservasiMejaView(), + settings: data, + ); + }, + _i10.MakananListView: (data) { + return _i8.MaterialPageRoute( + builder: (context) => const _i10.MakananListView(), + settings: data, + ); + }, + _i11.PesananListView: (data) { + return _i8.MaterialPageRoute( + builder: (context) => const _i11.PesananListView(), + settings: data, + ); + }, + _i12.AkunUserView: (data) { + return _i8.MaterialPageRoute( + builder: (context) => const _i12.AkunUserView(), settings: data, ); }, @@ -101,7 +224,7 @@ class StackedRouter extends _i1.RouterBase { Map get pagesMap => _pagesMap; } -extension NavigatorStateExtension on _i8.NavigationService { +extension NavigatorStateExtension on _i13.NavigationService { Future navigateToSplashScreenView([ int? routerId, bool preventDuplicates = true, @@ -158,14 +281,88 @@ extension NavigatorStateExtension on _i8.NavigationService { transition: transition); } - Future navigateToInputInformasiDiriView([ + Future navigateToInputInformasiDiriView({ + _i8.Key? key, + required String noHp, + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + }) async { + return navigateTo(Routes.inputInformasiDiriView, + arguments: InputInformasiDiriViewArguments(key: key, noHp: noHp), + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future navigateToUserIndexTrackingView([ int? routerId, bool preventDuplicates = true, Map? parameters, Widget Function(BuildContext, Animation, Animation, Widget)? transition, ]) async { - return navigateTo(Routes.inputInformasiDiriView, + return navigateTo(Routes.userIndexTrackingView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future + navigateToNestedReservasiMejaViewInUserIndexTrackingViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(UserIndexTrackingViewRoutes.reservasiMejaView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future navigateToNestedMakananListViewInUserIndexTrackingViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(UserIndexTrackingViewRoutes.makananListView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future navigateToNestedPesananListViewInUserIndexTrackingViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(UserIndexTrackingViewRoutes.pesananListView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future navigateToNestedAkunUserViewInUserIndexTrackingViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(UserIndexTrackingViewRoutes.akunUserView, id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, @@ -228,14 +425,90 @@ extension NavigatorStateExtension on _i8.NavigationService { transition: transition); } - Future replaceWithInputInformasiDiriView([ + Future replaceWithInputInformasiDiriView({ + _i8.Key? key, + required String noHp, + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + }) async { + return replaceWith(Routes.inputInformasiDiriView, + arguments: InputInformasiDiriViewArguments(key: key, noHp: noHp), + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future replaceWithUserIndexTrackingView([ int? routerId, bool preventDuplicates = true, Map? parameters, Widget Function(BuildContext, Animation, Animation, Widget)? transition, ]) async { - return replaceWith(Routes.inputInformasiDiriView, + return replaceWith(Routes.userIndexTrackingView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future + replaceWithNestedReservasiMejaViewInUserIndexTrackingViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(UserIndexTrackingViewRoutes.reservasiMejaView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future + replaceWithNestedMakananListViewInUserIndexTrackingViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(UserIndexTrackingViewRoutes.makananListView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future + replaceWithNestedPesananListViewInUserIndexTrackingViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(UserIndexTrackingViewRoutes.pesananListView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future replaceWithNestedAkunUserViewInUserIndexTrackingViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(UserIndexTrackingViewRoutes.akunUserView, id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, diff --git a/lib/app/core/custom_base_view_model.dart b/lib/app/core/custom_base_view_model.dart index e4a5e69..8b99696 100755 --- a/lib/app/core/custom_base_view_model.dart +++ b/lib/app/core/custom_base_view_model.dart @@ -1,16 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; +import '../../services/http_services.dart'; +import '../../services/my_easyloading.dart'; import '../app.locator.dart'; +import '../themes/app_colors.dart'; class CustomBaseViewModel extends BaseViewModel { final dialogService = locator(); final navigationService = locator(); final bottomSheetService = locator(); final snackbarService = locator(); + final easyLoading = locator(); + final httpService = locator(); + bool backPressed = true; void back() { navigationService.back(); } + + quitApp(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Keluar'), + content: const Text('Apakah Anda yakin ingin keluar?'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Batal'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text( + 'Keluar', + style: TextStyle(color: dangerColor), + ), + ), + ], + ); + }, + ).then((value) { + if (value == true) { + SystemNavigator.pop(); + } + }); + } } diff --git a/lib/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view.dart b/lib/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view.dart index 1a7050d..590ac4c 100644 --- a/lib/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view.dart +++ b/lib/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view.dart @@ -1,10 +1,19 @@ import 'package:flutter/material.dart'; +import 'package:reza_app/app/app.router.dart'; +import 'package:reza_app/ui/widgets/my_button.dart'; +import 'package:reza_app/ui/widgets/my_textformfield.dart'; import 'package:stacked/stacked.dart'; +import 'package:validatorless/validatorless.dart'; +import '../../../../app/themes/app_colors.dart'; +import '../../../../app/themes/app_text.dart'; import './input_informasi_diri_view_model.dart'; class InputInformasiDiriView extends StatelessWidget { - const InputInformasiDiriView({super.key}); + final String noHp; + + const InputInformasiDiriView({Key? key, required this.noHp}) + : super(key: key); @override Widget build(BuildContext context) { @@ -18,10 +27,155 @@ class InputInformasiDiriView extends StatelessWidget { InputInformasiDiriViewModel model, Widget? child, ) { - return const Scaffold( - body: Center( - child: Text( - 'InputInformasiDiriView', + return Scaffold( + appBar: AppBar( + title: const Text('INPUT INFORMASI DIRI', + style: TextStyle( + color: lightColor, + )), + backgroundColor: mainColor, + iconTheme: const IconThemeData( + color: + Colors.white), // Set the color of the back button to white + ), + body: WillPopScope( + onWillPop: () async { + if (model.backPressed) { + model.navigationService.navigateToMasukanNoHpView(); + } + return false; + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: SingleChildScrollView( + child: Form( + key: model.formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox(height: 25), + Center( + child: Image.asset( + 'assets/logo.png', + width: 100, + height: 100, + alignment: Alignment.center, + ), + ), + const SizedBox(height: 15), + const Align( + alignment: Alignment.centerLeft, + child: Text( + ' No . HP', + ), + ), + const SizedBox(height: 5), + MyTextFormField( + initialValue: noHp, + readOnly: true, + ), + const SizedBox(height: 15), + const Align( + alignment: Alignment.centerLeft, + child: Text( + ' Nama Lengkap', + ), + ), + const SizedBox(height: 5), + MyTextFormField( + hintText: 'Masukan Nama Lengkap', + controller: model.namaLengkapController, + validator: Validatorless.required( + 'Nama Lengkap tidak boleh kosong', + ), + ), + const SizedBox(height: 15), + const Align( + alignment: Alignment.centerLeft, + child: Text( + ' Tanggal Lahir', + ), + ), + const SizedBox(height: 5), + MyTextFormField( + hintText: 'Pilih Tanggal Lahir', + readOnly: true, + controller: model.tanggalLahirController, + validator: Validatorless.required( + 'Tanggal Lahir tidak boleh kosong', + ), + onTap: () => model.selectDate(context), + ), + const SizedBox(height: 15), + const Align( + alignment: Alignment.centerLeft, + child: Text( + ' Jenis Kelamin', + ), + ), + Container( + width: double.infinity, + height: 60, + padding: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25), + border: Border.all( + color: mainColor, + ), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + value: model.jenisKelamin, + onChanged: (String? newValue) { + // model.setSelectedJenisKelamin(newValue!); + model.log.i(newValue); + model.jenisKelamin = newValue!; + model.notifyListeners(); + }, + items: model.jenisKelaminList.map((String value) { + return DropdownMenuItem( + value: value, + child: Text( + value, + style: regularTextStyle.copyWith( + fontSize: 16, + ), + ), + ); + }).toList(), + ), + ), + ), + const SizedBox(height: 15), + const Align( + alignment: Alignment.centerLeft, + child: Text( + ' Alamat', + ), + ), + const SizedBox(height: 5), + MyTextFormField( + hintText: 'Masukan Alamat', + maxLines: 3, + controller: model.alamatController, + validator: Validatorless.required( + 'Alamat tidak boleh kosong', + ), + ), + const SizedBox(height: 15), + MyButton( + text: 'Daftar', + onPressed: () { + if (model.formKey.currentState!.validate()) { + model.goToLogin(); + } + }, + ), + const SizedBox(height: 25), + ], + ), + ), + ), ), ), ); diff --git a/lib/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view_model.dart b/lib/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view_model.dart index e8e97b6..a1316ba 100644 --- a/lib/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view_model.dart +++ b/lib/ui/views/daftar_user_ui/input_informasi_diri/input_informasi_diri_view_model.dart @@ -1,5 +1,46 @@ -import 'package:reza_app/app/core/custom_base_view_model.dart'; +import 'package:flutter/material.dart'; + +import '../../../../app/app.router.dart'; +import '../../../../app/app.logger.dart'; +import '../../../../app/core/custom_base_view_model.dart'; class InputInformasiDiriViewModel extends CustomBaseViewModel { + final log = getLogger('InputInformasiDiriViewModel'); Future init() async {} + + List jenisKelaminList = ['Laki-laki', 'Perempuan']; + String jenisKelamin = 'Laki-laki'; + + TextEditingController namaLengkapController = TextEditingController(); + TextEditingController tanggalLahirController = TextEditingController(); + TextEditingController alamatController = TextEditingController(); + GlobalKey formKey = GlobalKey(); + + selectDate(BuildContext context) async { + // get today's date + var datePicked = await showDatePicker( + context: context, + initialDate: DateTime(2013), + firstDate: DateTime(1950), + // last date is today's date + lastDate: DateTime(2013), + ); + + if (datePicked != null) { + String date = datePicked.toString().split(' ')[0]; + tanggalLahirController.text = date; + } + } + + goToLogin() async { + backPressed = false; + easyLoading.customLoading("Mendaftarkan Akun Anda"); + await Future.delayed(const Duration(seconds: 2)); + easyLoading.customLoading("Ke Halaman Login"); + await Future.delayed(const Duration(seconds: 2)); + easyLoading.dismissLoading(); + backPressed = true; + notifyListeners(); + await navigationService.navigateToLoginUserView(); + } } diff --git a/lib/ui/views/daftar_user_ui/masukan_no_hp/masukan_no_hp_view.dart b/lib/ui/views/daftar_user_ui/masukan_no_hp/masukan_no_hp_view.dart index a9db29c..b3c5a51 100644 --- a/lib/ui/views/daftar_user_ui/masukan_no_hp/masukan_no_hp_view.dart +++ b/lib/ui/views/daftar_user_ui/masukan_no_hp/masukan_no_hp_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../../app/app.router.dart'; import 'package:stacked/stacked.dart'; import 'package:validatorless/validatorless.dart'; @@ -35,7 +36,10 @@ class MasukanNoHpView extends StatelessWidget { ), body: WillPopScope( onWillPop: () async { - return model.backPressed; + if (model.backPressed) { + model.navigationService.navigateToLoginUserView(); + } + return false; }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 25), @@ -78,6 +82,7 @@ class MasukanNoHpView extends StatelessWidget { if (!model.formKey.currentState!.validate()) { return; } + FocusScope.of(context).unfocus(); model.log.i('Selanjutnya button pressed'); model.selanjutnya(); }, diff --git a/lib/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_hp_view.dart b/lib/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_hp_view.dart index 3883915..d3f618d 100644 --- a/lib/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_hp_view.dart +++ b/lib/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_hp_view.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:otp_text_field/otp_text_field.dart'; +import 'package:otp_text_field/style.dart'; import 'package:stacked/stacked.dart'; +import '../../../../app/themes/app_colors.dart'; import './verifikasi_no_hp_view_model.dart'; class VerifikasiNoHpView extends StatelessWidget { @@ -18,10 +21,128 @@ class VerifikasiNoHpView extends StatelessWidget { VerifikasiNoHpViewModel model, Widget? child, ) { - return const Scaffold( - body: Center( - child: Text( - 'VerifikasiNoHpView', + return Scaffold( + appBar: AppBar( + title: const Text( + 'Verifikasi No. HP', + style: TextStyle( + color: lightColor, + ), + ), + backgroundColor: mainColor, + iconTheme: const IconThemeData( + color: + Colors.white), // Set the color of the back button to white + ), + body: WillPopScope( + onWillPop: () async { + if (model.backPressed) { + model.back(); + } + return false; + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/logo.png', + width: 75, + height: 75, + ), + const SizedBox(height: 15), + const Text( + 'Masukkan Kode OTP yang telah \ndikirimkan ke nomor HP anda', + textAlign: TextAlign.center, + ), + const SizedBox(height: 5), + OTPTextField( + length: 6, + width: MediaQuery.of(context).size.width, + fieldWidth: 50, + style: const TextStyle(fontSize: 17), + textFieldAlignment: MainAxisAlignment.spaceAround, + fieldStyle: FieldStyle.box, + onCompleted: (pin) { + model.log.i('Completed: $pin'); + model.noOTP = int.parse(pin); + model.notifyListeners(); + }, + onChanged: (value) { + model.log.i('onChanged: $value'); + model.noOTP = int.parse(value); + model.notifyListeners(); + }, + ), + const SizedBox(height: 5), + RichText( + textAlign: TextAlign.center, + text: TextSpan( + text: 'Kode OTP kadaluarsa dalam ', + style: const TextStyle(color: Colors.black), + children: [ + const TextSpan( + text: '00:30\n', + style: TextStyle(color: mainColor), + ), + WidgetSpan( + child: GestureDetector( + onTap: () { + // Handle tap + }, + child: const Center( + child: Text( + 'Kirim ulang kode OTP', + style: TextStyle( + color: mainColor, + fontSize: 14, + height: 1.5, + decoration: TextDecoration.underline, + ), + ), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 5), + SizedBox( + width: MediaQuery.of(context).size.width * 0.5, + child: ElevatedButton( + onPressed: () { + if (model.noOTP == null) { + model.snackbarService.showSnackbar( + message: 'Kode OTP tidak boleh kosong'); + return; + } + + // check the length of noOTP + if (model.noOTP.toString().length < 6) { + model.snackbarService.showSnackbar( + message: 'Kode OTP tidak boleh kurang dari 6'); + return; + } + // hide the keyboard + FocusScope.of(context).unfocus(); + + model.log.i('noOTP: ${model.noOTP}'); + model.goToInputInformasiDiri(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: mainColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: const Text('Verifikasi'), + ), + ), + ], + ), + ), ), ), ); diff --git a/lib/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_hp_view_model.dart b/lib/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_hp_view_model.dart index 2a8bed8..80014ca 100644 --- a/lib/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_hp_view_model.dart +++ b/lib/ui/views/daftar_user_ui/verifikasi_no_hp/verifikasi_no_hp_view_model.dart @@ -1,5 +1,22 @@ -import 'package:reza_app/app/core/custom_base_view_model.dart'; +import '../../../../app/app.logger.dart'; +import '../../../../app/app.router.dart'; +import '../../../../app/core/custom_base_view_model.dart'; class VerifikasiNoHpViewModel extends CustomBaseViewModel { + final log = getLogger('VerifikasiNoHpViewModel'); + int? noOTP; + Future init() async {} + + goToInputInformasiDiri() async { + backPressed = false; + easyLoading.customLoading("Ke Halaman Input Informasi Diri"); + await Future.delayed(const Duration(seconds: 3)); + easyLoading.dismissLoading(); + backPressed = true; + notifyListeners(); + await navigationService.navigateToInputInformasiDiriView( + noHp: "082293246583", + ); + } } diff --git a/lib/ui/views/login_user/login_user_view.dart b/lib/ui/views/login_user/login_user_view.dart index 3e6ba2e..31c8324 100644 --- a/lib/ui/views/login_user/login_user_view.dart +++ b/lib/ui/views/login_user/login_user_view.dart @@ -23,7 +23,11 @@ class LoginUserView extends StatelessWidget { return Scaffold( body: WillPopScope( onWillPop: () async { - return model.backPressed; + if (model.backPressed) { + // model.back(); + model.quitApp(context); + } + return false; }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 25), @@ -75,12 +79,14 @@ class LoginUserView extends StatelessWidget { MyButton( text: 'Login', onPressed: () { + FocusScope.of(context).unfocus(); model.log.i('Login button pressed'); model.login(); }, ), TextButton( onPressed: () { + FocusScope.of(context).unfocus(); model.daftar(); }, child: const Text( diff --git a/lib/ui/views/login_user/login_user_view_model.dart b/lib/ui/views/login_user/login_user_view_model.dart index f356cd3..7488f0b 100644 --- a/lib/ui/views/login_user/login_user_view_model.dart +++ b/lib/ui/views/login_user/login_user_view_model.dart @@ -1,14 +1,11 @@ import 'package:flutter/material.dart'; import '../../../app/app.router.dart'; -import '../../../app/app.locator.dart'; import '../../../app/app.logger.dart'; import '../../../app/core/custom_base_view_model.dart'; -import '../../../services/my_easyloading.dart'; class LoginUserViewModel extends CustomBaseViewModel { final log = getLogger('LoginUserViewModel'); - final easyloading = locator(); TextEditingController noHpController = TextEditingController(); TextEditingController passwordController = TextEditingController(); @@ -21,13 +18,13 @@ class LoginUserViewModel extends CustomBaseViewModel { login() async { setBusy(true); backPressed = false; - easyloading.showLoading(); - await Future.delayed(const Duration(seconds: 5)); - easyloading.dismissLoading(); + easyLoading.showLoading(); + await Future.delayed(const Duration(seconds: 2)); + easyLoading.dismissLoading(); setBusy(false); backPressed = true; notifyListeners(); - // await navigationService.navigateToHomeView(); + await navigationService.navigateToUserIndexTrackingView(); } daftar() async { diff --git a/lib/ui/views/user_ui/akun_user/akun_user_view.dart b/lib/ui/views/user_ui/akun_user/akun_user_view.dart new file mode 100644 index 0000000..3eaa2a0 --- /dev/null +++ b/lib/ui/views/user_ui/akun_user/akun_user_view.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import './akun_user_view_model.dart'; + +class AkunUserView extends StatelessWidget { + const AkunUserView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => AkunUserViewModel(), + onViewModelReady: (AkunUserViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + AkunUserViewModel model, + Widget? child, + ) { + return const Scaffold( + body: Center( + child: Text( + 'AkunUserView', + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/user_ui/akun_user/akun_user_view_model.dart b/lib/ui/views/user_ui/akun_user/akun_user_view_model.dart new file mode 100644 index 0000000..c84d20c --- /dev/null +++ b/lib/ui/views/user_ui/akun_user/akun_user_view_model.dart @@ -0,0 +1,5 @@ +import 'package:reza_app/app/core/custom_base_view_model.dart'; + +class AkunUserViewModel extends CustomBaseViewModel { + Future init() async {} +} diff --git a/lib/ui/views/user_ui/makanan_list/makanan_list_view.dart b/lib/ui/views/user_ui/makanan_list/makanan_list_view.dart new file mode 100644 index 0000000..ee6da55 --- /dev/null +++ b/lib/ui/views/user_ui/makanan_list/makanan_list_view.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import './makanan_list_view_model.dart'; + +class MakananListView extends StatelessWidget { + const MakananListView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => MakananListViewModel(), + onViewModelReady: (MakananListViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + MakananListViewModel model, + Widget? child, + ) { + return const Scaffold( + body: Center( + child: Text( + 'MakananListView', + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/user_ui/makanan_list/makanan_list_view_model.dart b/lib/ui/views/user_ui/makanan_list/makanan_list_view_model.dart new file mode 100644 index 0000000..cc0bdd4 --- /dev/null +++ b/lib/ui/views/user_ui/makanan_list/makanan_list_view_model.dart @@ -0,0 +1,5 @@ +import 'package:reza_app/app/core/custom_base_view_model.dart'; + +class MakananListViewModel extends CustomBaseViewModel { + Future init() async {} +} diff --git a/lib/ui/views/user_ui/pesanan_list/pesanan_list_view.dart b/lib/ui/views/user_ui/pesanan_list/pesanan_list_view.dart new file mode 100644 index 0000000..0dd3a13 --- /dev/null +++ b/lib/ui/views/user_ui/pesanan_list/pesanan_list_view.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import './pesanan_list_view_model.dart'; + +class PesananListView extends StatelessWidget { + const PesananListView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => PesananListViewModel(), + onViewModelReady: (PesananListViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + PesananListViewModel model, + Widget? child, + ) { + return const Scaffold( + body: Center( + child: Text( + 'PesananListView', + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/user_ui/pesanan_list/pesanan_list_view_model.dart b/lib/ui/views/user_ui/pesanan_list/pesanan_list_view_model.dart new file mode 100644 index 0000000..e60a9d3 --- /dev/null +++ b/lib/ui/views/user_ui/pesanan_list/pesanan_list_view_model.dart @@ -0,0 +1,5 @@ +import 'package:reza_app/app/core/custom_base_view_model.dart'; + +class PesananListViewModel extends CustomBaseViewModel { + Future init() async {} +} diff --git a/lib/ui/views/user_ui/reservasi_meja/reservasi_meja_view.dart b/lib/ui/views/user_ui/reservasi_meja/reservasi_meja_view.dart new file mode 100644 index 0000000..581aad5 --- /dev/null +++ b/lib/ui/views/user_ui/reservasi_meja/reservasi_meja_view.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import './reservasi_meja_view_model.dart'; + +class ReservasiMejaView extends StatelessWidget { + const ReservasiMejaView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ReservasiMejaViewModel(), + onViewModelReady: (ReservasiMejaViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + ReservasiMejaViewModel model, + Widget? child, + ) { + return const Scaffold( + body: Center( + child: Text( + 'ReservasiMejaView', + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/user_ui/reservasi_meja/reservasi_meja_view_model.dart b/lib/ui/views/user_ui/reservasi_meja/reservasi_meja_view_model.dart new file mode 100644 index 0000000..b950afd --- /dev/null +++ b/lib/ui/views/user_ui/reservasi_meja/reservasi_meja_view_model.dart @@ -0,0 +1,5 @@ +import 'package:reza_app/app/core/custom_base_view_model.dart'; + +class ReservasiMejaViewModel extends CustomBaseViewModel { + Future init() async {} +} diff --git a/lib/ui/views/user_ui/user_index_tracking/user_index_tracking_view.dart b/lib/ui/views/user_ui/user_index_tracking/user_index_tracking_view.dart new file mode 100644 index 0000000..09c5c75 --- /dev/null +++ b/lib/ui/views/user_ui/user_index_tracking/user_index_tracking_view.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:stacked_services/stacked_services.dart'; +import 'package:stylish_bottom_bar/model/bar_items.dart'; +import 'package:stylish_bottom_bar/stylish_bottom_bar.dart'; + +import '../../../../app/app.router.dart'; +import '../../../../app/themes/app_colors.dart'; +import '../../../../app/themes/app_text.dart'; +import 'user_index_tracking_view_model.dart'; + +class UserIndexTrackingView extends StatelessWidget { + const UserIndexTrackingView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => UserIndexTrackingViewModel(), + onViewModelReady: (UserIndexTrackingViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + UserIndexTrackingViewModel model, + Widget? child, + ) { + return Scaffold( + appBar: AppBar( + title: Text( + model.header, + style: const TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + backgroundColor: mainColor, + elevation: 0, + automaticallyImplyLeading: false, + ), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + child: ExtendedNavigator( + navigatorKey: StackedService.nestedNavigationKey(3), + router: UserIndexTrackingViewRouter(), + observers: [ + StackedService.routeObserver, + ], + ), + ), + bottomNavigationBar: StylishBottomBar( + items: [ + for (var item in model.bottomNavBarList) + BottomBarItem( + icon: Icon(item['icon'], + color: model.currentIndex == + model.bottomNavBarList.indexOf(item) + ? sixthGrey + : backgroundColor), + title: Text( + item['name'], + style: regularTextStyle.copyWith( + color: model.currentIndex == + model.bottomNavBarList.indexOf(item) + ? sixthGrey + : Colors.grey, + ), + ), + backgroundColor: + model.currentIndex == model.bottomNavBarList.indexOf(item) + ? Colors.white + : Colors.grey, + ), + ], + currentIndex: model.currentIndex, + hasNotch: true, + backgroundColor: mainColor, + onTap: (value) { + model.handleNavigation(value); + }, + option: BubbleBarOptions( + barStyle: BubbleBarStyle.horizotnal, + bubbleFillStyle: BubbleFillStyle.fill, + opacity: 0.3), + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/user_ui/user_index_tracking/user_index_tracking_view_model.dart b/lib/ui/views/user_ui/user_index_tracking/user_index_tracking_view_model.dart new file mode 100644 index 0000000..b004240 --- /dev/null +++ b/lib/ui/views/user_ui/user_index_tracking/user_index_tracking_view_model.dart @@ -0,0 +1,110 @@ +import 'package:back_button_interceptor/back_button_interceptor.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:stacked/stacked.dart'; +import 'package:stacked_services/stacked_services.dart'; + +import '../../../../app/app.locator.dart'; +import '../../../../app/app.logger.dart'; +import '../../../../app/app.router.dart'; +import '../../../../app/themes/app_colors.dart'; + +class UserIndexTrackingViewModel extends IndexTrackingViewModel { + final log = getLogger('UserIndexTrackingViewModel'); + final navigationService = locator(); + bool backPressed = true; + Future init() async { + BackButtonInterceptor.add(myInterceptor); + } + + bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) { + // print("BACK BUTTON!"); // Do some stuff. + + if (backPressed) { + quitApp(null); + } + + return true; + } + + final _bottomNavBarList = [ + { + 'name': 'Meja', + 'icon': Icons.table_restaurant_outlined, + 'header': 'RESERVASI MEJA', + }, + { + 'name': 'Makanan', + 'icon': Icons.food_bank_outlined, + 'header': 'LIST MAKANAN', + }, + { + 'name': 'Pesanan', + 'icon': Icons.shopping_cart_outlined, + 'header': 'LIST PESANAN', + }, + { + 'name': 'Akun', + 'icon': Icons.person_outline, + 'header': 'AKUN', + }, + ]; + String header = 'RESERVASI MEJA'; + + List> get bottomNavBarList => _bottomNavBarList; + + final List _views = [ + UserIndexTrackingViewRoutes.reservasiMejaView, + UserIndexTrackingViewRoutes.makananListView, + UserIndexTrackingViewRoutes.pesananListView, + UserIndexTrackingViewRoutes.akunUserView, + ]; + + void handleNavigation(int index) { + // log.d("handleNavigation: $index"); + // log.d("currentIndex: $currentIndex"); + + if (currentIndex == index) return; + + setIndex(index); + header = _bottomNavBarList[index]['header'] as String; + navigationService.navigateTo( + _views[index], + id: 3, + ); + } + + quitApp(BuildContext? context) { + backPressed = false; + showDialog( + context: context ?? StackedService.navigatorKey!.currentContext!, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Keluar'), + content: const Text('Apakah Anda yakin ingin keluar?'), + actions: [ + TextButton( + onPressed: () { + backPressed = true; + Navigator.of(context).pop(false); + }, + child: const Text('Batal'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text( + 'Keluar', + style: TextStyle(color: dangerColor), + ), + ), + ], + ); + }, + ).then((value) { + if (value == true) { + SystemNavigator.pop(); + } + }); + notifyListeners(); + } +} diff --git a/pubspec.lock b/pubspec.lock index b5bed82..1e254f7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + back_button_interceptor: + dependency: "direct main" + description: + name: back_button_interceptor + sha256: e47660f2178a4392eb72001f9594d3fdcb5efde93e59d2819d61fda499e781c8 + url: "https://pub.dev" + source: hosted + version: "6.0.2" boolean_selector: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5f8149e..9c7d9eb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: validatorless: ^1.2.3 intl: otp_text_field: ^1.1.3 + back_button_interceptor: ^6.0.2 dev_dependencies: flutter_test: