added splashscreen, login and add hp number pages
This commit is contained in:
@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
import './input_informasi_diri_view_model.dart';
|
||||
|
||||
class InputInformasiDiriView extends StatelessWidget {
|
||||
const InputInformasiDiriView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<InputInformasiDiriViewModel>.reactive(
|
||||
viewModelBuilder: () => InputInformasiDiriViewModel(),
|
||||
onViewModelReady: (InputInformasiDiriViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
InputInformasiDiriViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: Text(
|
||||
'InputInformasiDiriView',
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
import 'package:reza_app/app/core/custom_base_view_model.dart';
|
||||
|
||||
class InputInformasiDiriViewModel extends CustomBaseViewModel {
|
||||
Future<void> init() async {}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:validatorless/validatorless.dart';
|
||||
|
||||
import '../../../../app/themes/app_colors.dart';
|
||||
import '../../../widgets/my_button.dart';
|
||||
import '../../../widgets/my_textformfield.dart';
|
||||
import './masukan_no_hp_view_model.dart';
|
||||
|
||||
class MasukanNoHpView extends StatelessWidget {
|
||||
const MasukanNoHpView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<MasukanNoHpViewModel>.reactive(
|
||||
viewModelBuilder: () => MasukanNoHpViewModel(),
|
||||
onViewModelReady: (MasukanNoHpViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
MasukanNoHpViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('PENDAFTARAN USER BARU',
|
||||
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 {
|
||||
return model.backPressed;
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Center(
|
||||
child: Form(
|
||||
key: model.formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/logo.png',
|
||||
width: 100,
|
||||
height: 100,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const Text(
|
||||
'Masukkan Nomor HP',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
MyTextFormField(
|
||||
maxLength: 13,
|
||||
hintText: 'No. HP',
|
||||
keyboardType: TextInputType.phone,
|
||||
controller: model.noHpController,
|
||||
validator:
|
||||
Validatorless.required('No. HP tidak boleh kosong'),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.5,
|
||||
child: MyButton(
|
||||
text: 'Selanjutnya',
|
||||
onPressed: () {
|
||||
// if noHpController length is less than 9, return
|
||||
if (model.noHpController.text.length < 9) {
|
||||
model.snackbarService.showSnackbar(
|
||||
message: 'No. HP tidak boleh kurang dari 9');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!model.formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
model.log.i('Selanjutnya button pressed');
|
||||
model.selanjutnya();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../app/app.locator.dart';
|
||||
import '../../../../app/app.router.dart';
|
||||
|
||||
import '../../../../app/app.logger.dart';
|
||||
import '../../../../app/core/custom_base_view_model.dart';
|
||||
import '../../../../services/my_easyloading.dart';
|
||||
|
||||
class MasukanNoHpViewModel extends CustomBaseViewModel {
|
||||
final log = getLogger('MasukanNoHpViewModel');
|
||||
final _easyloading = locator<MyEasyLoading>();
|
||||
|
||||
TextEditingController noHpController = TextEditingController();
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
Future<void> init() async {}
|
||||
|
||||
selanjutnya() async {
|
||||
_easyloading.customLoading("Menghantar Kode OTP \nke WhatsApp Anda");
|
||||
backPressed = false;
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
backPressed = true;
|
||||
notifyListeners();
|
||||
_easyloading.dismissLoading();
|
||||
await navigationService.navigateToVerifikasiNoHpView();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
import './verifikasi_no_hp_view_model.dart';
|
||||
|
||||
class VerifikasiNoHpView extends StatelessWidget {
|
||||
const VerifikasiNoHpView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<VerifikasiNoHpViewModel>.reactive(
|
||||
viewModelBuilder: () => VerifikasiNoHpViewModel(),
|
||||
onViewModelReady: (VerifikasiNoHpViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
VerifikasiNoHpViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: Text(
|
||||
'VerifikasiNoHpView',
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
import 'package:reza_app/app/core/custom_base_view_model.dart';
|
||||
|
||||
class VerifikasiNoHpViewModel extends CustomBaseViewModel {
|
||||
Future<void> init() async {}
|
||||
}
|
||||
104
lib/ui/views/login_user/login_user_view.dart
Normal file
104
lib/ui/views/login_user/login_user_view.dart
Normal file
@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
import '../../widgets/my_button.dart';
|
||||
import '../../widgets/my_textformfield.dart';
|
||||
import './login_user_view_model.dart';
|
||||
|
||||
class LoginUserView extends StatelessWidget {
|
||||
const LoginUserView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<LoginUserViewModel>.reactive(
|
||||
viewModelBuilder: () => LoginUserViewModel(),
|
||||
onViewModelReady: (LoginUserViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
LoginUserViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return Scaffold(
|
||||
body: WillPopScope(
|
||||
onWillPop: () async {
|
||||
return model.backPressed;
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: model.formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/logo.png',
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'Halaman Login',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
MyTextFormField(
|
||||
maxLength: 13,
|
||||
hintText: 'No. HP',
|
||||
keyboardType: TextInputType.phone,
|
||||
controller: model.noHpController,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
MyTextFormField(
|
||||
hintText: 'Password',
|
||||
obscureText: model.showPassword,
|
||||
controller: model.passwordController,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
model.showPassword = !model.showPassword;
|
||||
model.notifyListeners();
|
||||
},
|
||||
icon: Icon(
|
||||
model.showPassword
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
MyButton(
|
||||
text: 'Login',
|
||||
onPressed: () {
|
||||
model.log.i('Login button pressed');
|
||||
model.login();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
model.daftar();
|
||||
},
|
||||
child: const Text(
|
||||
'Belum punya akun? Daftar disini',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
36
lib/ui/views/login_user/login_user_view_model.dart
Normal file
36
lib/ui/views/login_user/login_user_view_model.dart
Normal file
@ -0,0 +1,36 @@
|
||||
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();
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
bool showPassword = true;
|
||||
|
||||
Future<void> init() async {}
|
||||
|
||||
login() async {
|
||||
setBusy(true);
|
||||
backPressed = false;
|
||||
easyloading.showLoading();
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
easyloading.dismissLoading();
|
||||
setBusy(false);
|
||||
backPressed = true;
|
||||
notifyListeners();
|
||||
// await navigationService.navigateToHomeView();
|
||||
}
|
||||
|
||||
daftar() async {
|
||||
await navigationService.navigateToMasukanNoHpView();
|
||||
}
|
||||
}
|
||||
67
lib/ui/views/splash_screen/splash_screen_view.dart
Normal file
67
lib/ui/views/splash_screen/splash_screen_view.dart
Normal file
@ -0,0 +1,67 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
import './splash_screen_view_model.dart';
|
||||
|
||||
class SplashScreenView extends StatelessWidget {
|
||||
const SplashScreenView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<SplashScreenViewModel>.reactive(
|
||||
viewModelBuilder: () => SplashScreenViewModel(),
|
||||
onViewModelReady: (SplashScreenViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
SplashScreenViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Expanded(
|
||||
flex: 1,
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
children: [
|
||||
Center(
|
||||
child: Image.asset(
|
||||
'assets/logo.png',
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Reza Table Reservation \n& Food Order',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const Text(
|
||||
'Alamat jalan jendral sudirman',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
10
lib/ui/views/splash_screen/splash_screen_view_model.dart
Normal file
10
lib/ui/views/splash_screen/splash_screen_view_model.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:reza_app/app/app.router.dart';
|
||||
import 'package:reza_app/app/core/custom_base_view_model.dart';
|
||||
|
||||
class SplashScreenViewModel extends CustomBaseViewModel {
|
||||
Future<void> init() async {
|
||||
// after 2 second, navigate to login page
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
await navigationService.navigateToLoginUserView();
|
||||
}
|
||||
}
|
||||
34
lib/ui/widgets/my_button.dart
Normal file
34
lib/ui/widgets/my_button.dart
Normal file
@ -0,0 +1,34 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/themes/app_colors.dart';
|
||||
|
||||
class MyButton extends StatelessWidget {
|
||||
const MyButton({
|
||||
Key? key,
|
||||
required this.text,
|
||||
this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
final String text;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: mainColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
color: backgroundColor,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
91
lib/ui/widgets/my_textformfield.dart
Normal file
91
lib/ui/widgets/my_textformfield.dart
Normal file
@ -0,0 +1,91 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/themes/app_colors.dart';
|
||||
|
||||
class MyTextFormField extends StatelessWidget {
|
||||
const MyTextFormField({
|
||||
Key? key,
|
||||
this.labelText,
|
||||
this.hintText,
|
||||
this.obscureText,
|
||||
this.validator,
|
||||
this.suffixIcon,
|
||||
this.prefixIcon,
|
||||
this.focusNode,
|
||||
this.controller,
|
||||
this.maxLines = 1,
|
||||
this.onEditingComplete,
|
||||
this.readOnly = false,
|
||||
this.onTap,
|
||||
this.keyboardType = TextInputType.text,
|
||||
this.initialValue,
|
||||
this.enabled = true,
|
||||
this.maxLength,
|
||||
}) : super(key: key);
|
||||
|
||||
final String? labelText;
|
||||
final String? hintText;
|
||||
final bool? obscureText;
|
||||
final FormFieldValidator<String>? validator;
|
||||
final Widget? suffixIcon;
|
||||
final Widget? prefixIcon;
|
||||
final FocusNode? focusNode;
|
||||
final TextEditingController? controller;
|
||||
final int maxLines;
|
||||
final VoidCallback? onEditingComplete;
|
||||
final bool readOnly;
|
||||
final VoidCallback? onTap;
|
||||
final TextInputType keyboardType;
|
||||
final String? initialValue;
|
||||
final bool enabled;
|
||||
final int? maxLength;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
maxLength: maxLength,
|
||||
enabled: enabled,
|
||||
initialValue: initialValue,
|
||||
onEditingComplete: onEditingComplete,
|
||||
maxLines: maxLines,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
obscureText: obscureText ?? false,
|
||||
readOnly: readOnly,
|
||||
onTap: onTap,
|
||||
keyboardType: keyboardType,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: prefixIcon,
|
||||
suffixIcon: suffixIcon,
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(
|
||||
color: mainColor,
|
||||
),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(
|
||||
color: mainColor,
|
||||
),
|
||||
),
|
||||
focusedErrorBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(
|
||||
color: dangerColor,
|
||||
),
|
||||
),
|
||||
errorBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(
|
||||
color: dangerColor,
|
||||
),
|
||||
),
|
||||
labelText: labelText,
|
||||
hintText: hintText,
|
||||
labelStyle: const TextStyle(color: fontColor),
|
||||
),
|
||||
validator: validator,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user