added kode otp page, input informasi diri page, and user index tracking page, repair the back button on UserIndexTrackingView

This commit is contained in:
kicap
2023-07-17 04:39:00 +08:00
parent 4c5ec4364d
commit b20b414a21
22 changed files with 1051 additions and 33 deletions

View File

@ -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<String>(
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<String>(
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),
],
),
),
),
),
),
);

View File

@ -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<void> init() async {}
List<String> jenisKelaminList = ['Laki-laki', 'Perempuan'];
String jenisKelamin = 'Laki-laki';
TextEditingController namaLengkapController = TextEditingController();
TextEditingController tanggalLahirController = TextEditingController();
TextEditingController alamatController = TextEditingController();
GlobalKey<FormState> formKey = GlobalKey<FormState>();
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();
}
}

View File

@ -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();
},

View File

@ -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'),
),
),
],
),
),
),
),
);

View File

@ -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<void> 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",
);
}
}

View File

@ -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(

View File

@ -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<MyEasyLoading>();
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 {

View File

@ -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<AkunUserViewModel>.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',
),
),
);
},
);
}
}

View File

@ -0,0 +1,5 @@
import 'package:reza_app/app/core/custom_base_view_model.dart';
class AkunUserViewModel extends CustomBaseViewModel {
Future<void> init() async {}
}

View File

@ -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<MakananListViewModel>.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',
),
),
);
},
);
}
}

View File

@ -0,0 +1,5 @@
import 'package:reza_app/app/core/custom_base_view_model.dart';
class MakananListViewModel extends CustomBaseViewModel {
Future<void> init() async {}
}

View File

@ -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<PesananListViewModel>.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',
),
),
);
},
);
}
}

View File

@ -0,0 +1,5 @@
import 'package:reza_app/app/core/custom_base_view_model.dart';
class PesananListViewModel extends CustomBaseViewModel {
Future<void> init() async {}
}

View File

@ -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<ReservasiMejaViewModel>.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',
),
),
);
},
);
}
}

View File

@ -0,0 +1,5 @@
import 'package:reza_app/app/core/custom_base_view_model.dart';
class ReservasiMejaViewModel extends CustomBaseViewModel {
Future<void> init() async {}
}

View File

@ -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<UserIndexTrackingViewModel>.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),
),
);
},
);
}
}

View File

@ -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<NavigationService>();
bool backPressed = true;
Future<void> 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<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
final List<String> _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();
}
}