step 3 : copy login screen app dokter
This commit is contained in:
278
app_petty_cash/lib/screen/login/block_body.dart
Normal file
278
app_petty_cash/lib/screen/login/block_body.dart
Normal file
@@ -0,0 +1,278 @@
|
||||
// ignore_for_file: prefer_const_constructors, use_full_hex_values_for_flutter_colors
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../../app/route.dart';
|
||||
import '../../provider/current_menu_provider.dart';
|
||||
import '../../provider/current_user_provider.dart';
|
||||
import 'custom_text_field.dart';
|
||||
import 'login_provider.dart';
|
||||
|
||||
class BlockBody extends HookConsumerWidget {
|
||||
const BlockBody({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isPasswordObscured = useState<bool>(true);
|
||||
final ctrlUsername = useTextEditingController(text: "");
|
||||
final ctrlDokterId = useTextEditingController(text: "");
|
||||
final ctrlPassword = useTextEditingController(text: "");
|
||||
final isLoading = useState(false);
|
||||
final errorMessage = useState("");
|
||||
final isSuccess = useState(false);
|
||||
|
||||
ref.listen(loginProvider, (prev, next) {
|
||||
if (next is LoginStateLoading) {
|
||||
isLoading.value = true;
|
||||
} else if (next is LoginStateError) {
|
||||
isLoading.value = false;
|
||||
errorMessage.value = next.message;
|
||||
Timer(const Duration(seconds: 3), () {
|
||||
errorMessage.value = "";
|
||||
});
|
||||
} else if (next is LoginStateDone) {
|
||||
isLoading.value = false;
|
||||
isSuccess.value = true;
|
||||
ref.read(currentPageProvider.state).update((state) => 0);
|
||||
ref.read(currentUserProvider.notifier).state = next.model;
|
||||
|
||||
// print(next.model.model.doctorName);
|
||||
|
||||
// Navigator.of(context).pop();
|
||||
// Navigator.of(context).pushNamedAndRemoveUntil(menuRoute,(route) => true,);
|
||||
// Navigator.popAndPushNamed(context,menuRoute);
|
||||
|
||||
Navigator.of(context)
|
||||
.pushNamedAndRemoveUntil(menuRoute, (route) => false);
|
||||
}
|
||||
});
|
||||
|
||||
return ListView(
|
||||
children: [
|
||||
Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 390),
|
||||
height: Constant.getActualYPhone(context: context, y: 343),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('images/header_tanpa_logo.png'),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 136),
|
||||
),
|
||||
Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 148),
|
||||
height: Constant.getActualYPhone(context: context, y: 78),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.fitHeight,
|
||||
image: AssetImage('images/logo_pramita.png'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 43),
|
||||
),
|
||||
Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 391),
|
||||
height: Constant.getActualYPhone(context: context, y: 454),
|
||||
padding: EdgeInsets.only(
|
||||
left: Constant.getActualXPhone(context: context, x: 32),
|
||||
right: Constant.getActualXPhone(context: context, x: 32),
|
||||
top: Constant.getActualYPhone(context: context, y: 20),
|
||||
bottom: Constant.getActualYPhone(context: context, y: 20),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// form
|
||||
SizedBox(
|
||||
width: Constant.getActualXPhone(context: context, x: 180),
|
||||
height: Constant.getActualYPhone(context: context, y: 29),
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text("LOGIN DOKTER",
|
||||
style: Constant.titleH3_700(context: context)
|
||||
.copyWith(color: Constant.subTitleBlack)),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 4),
|
||||
),
|
||||
|
||||
// notify error dari backend start
|
||||
|
||||
if (isLoading.value)
|
||||
SizedBox(
|
||||
width: Constant.getActualXPhone(context: context, x: 20),
|
||||
height: Constant.getActualYPhone(context: context, y: 20),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
|
||||
if (errorMessage.value != "")
|
||||
Text(
|
||||
"Peringatan : ${errorMessage.value}",
|
||||
style: Constant.titleH7_700(context: context)
|
||||
.copyWith(color: Constant.textRedProblemMaketing),
|
||||
),
|
||||
|
||||
// notify error dari backend end
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 24),
|
||||
),
|
||||
// textfield marketing ID
|
||||
SizedBox(
|
||||
width: Constant.getActualXPhone(context: context, x: 320),
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
child: CustomTextField(
|
||||
ctrl: ctrlDokterId,
|
||||
isPassword: false,
|
||||
hintText: "Doctor ID",
|
||||
labelText: "Doctor ID",
|
||||
isError: (errorMessage.value != "") ? true : false,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 20),
|
||||
),
|
||||
// textfield username
|
||||
SizedBox(
|
||||
width: Constant.getActualXPhone(context: context, x: 320),
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
child: CustomTextField(
|
||||
ctrl: ctrlUsername,
|
||||
isPassword: false,
|
||||
hintText: "Username",
|
||||
labelText: "Username",
|
||||
isError: (errorMessage.value != "") ? true : false,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 20),
|
||||
),
|
||||
// textfield password
|
||||
SizedBox(
|
||||
width: Constant.getActualXPhone(context: context, x: 320),
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
child: CustomTextField(
|
||||
ctrl: ctrlPassword,
|
||||
isPassword: false,
|
||||
obscureText: isPasswordObscured.value,
|
||||
hintText: "Password",
|
||||
labelText: "Password",
|
||||
onToggle: () {
|
||||
isPasswordObscured.value = !isPasswordObscured.value;
|
||||
},
|
||||
isError: (errorMessage.value != "") ? true : false,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 40),
|
||||
),
|
||||
// button login
|
||||
SizedBox(
|
||||
width: Constant.getActualXPhone(context: context, x: 320),
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
child: ElevatedButton(
|
||||
onPressed: (isSuccess.value == true)
|
||||
? () {}
|
||||
: () {
|
||||
if (ctrlUsername.text.isEmpty ||
|
||||
ctrlDokterId.text.isEmpty ||
|
||||
ctrlPassword.text.isEmpty) {
|
||||
isLoading.value = true;
|
||||
errorMessage.value = 'Inputan harus diisi';
|
||||
Timer(const Duration(seconds: 3), () {
|
||||
isLoading.value = false;
|
||||
errorMessage.value = "";
|
||||
});
|
||||
} else {
|
||||
ref.read(loginProvider.notifier).login(
|
||||
username: ctrlUsername.text,
|
||||
dokterId: ctrlDokterId.text,
|
||||
password: ctrlPassword.text);
|
||||
}
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateColor.resolveWith((st) =>
|
||||
(isSuccess.value == true)
|
||||
? Constant.textGrey
|
||||
: Constant.buttonLoginBackgroundColor),
|
||||
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
side: BorderSide(color: Colors.red),
|
||||
),
|
||||
),
|
||||
shadowColor: MaterialStateProperty.all(Color(0xffff48423d)),
|
||||
elevation: MaterialStateProperty.all(4.0),
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'LOGIN',
|
||||
style: Constant.titleH3_700(context: context)
|
||||
.copyWith(color: Constant.textLoginColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 22),
|
||||
),
|
||||
|
||||
// versi
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Versi ${Constant.version}',
|
||||
style: Constant.titleH4Login(context: context)
|
||||
.copyWith(color: Constant.textGrey),
|
||||
),
|
||||
),
|
||||
|
||||
// text problem login
|
||||
|
||||
// SizedBox(
|
||||
// width: Constant.getActualXPhone(context: context, x: 121),
|
||||
// height: Constant.getActualYPhone(context: context, y: 20),
|
||||
// // color: Colors.brown,
|
||||
// child: Align(
|
||||
// alignment: Alignment.center,
|
||||
// child: InkWell(
|
||||
// onTap: () {
|
||||
// Navigator.of(context).pushNamed(problemLoginRoute);
|
||||
// },
|
||||
// child: Text(
|
||||
// 'Problem Login ? ',
|
||||
// style: Constant.body1_400(context: context)
|
||||
// .copyWith(color: Constant.textRedProblemMaketing),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
375
app_petty_cash/lib/screen/login/block_bodyv2.dart
Normal file
375
app_petty_cash/lib/screen/login/block_bodyv2.dart
Normal file
@@ -0,0 +1,375 @@
|
||||
// ignore_for_file: prefer_const_constructors, use_full_hex_values_for_flutter_colors
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../../app/route.dart';
|
||||
import '../../provider/current_menu_provider.dart';
|
||||
import '../../provider/current_user_provider.dart';
|
||||
import 'custom_text_field.dart';
|
||||
import 'login_provider.dart';
|
||||
|
||||
class BlockBodyV2 extends HookConsumerWidget {
|
||||
const BlockBodyV2({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isPasswordObscured = useState<bool>(true);
|
||||
final ctrlUsername = useTextEditingController(text: "");
|
||||
final ctrlDokterId = useTextEditingController(text: "");
|
||||
final ctrlPassword = useTextEditingController(text: "");
|
||||
final isLoading = useState(false);
|
||||
final errorMessage = useState("");
|
||||
final isSuccess = useState(false);
|
||||
final focusNodeDokterID = useFocusNode();
|
||||
final focusNodeDokterUsername = useFocusNode();
|
||||
final focusNodeDokterPassword = useFocusNode();
|
||||
|
||||
final dokterIDhasFocus = useState(false);
|
||||
final usernamehasFocus = useState(false);
|
||||
final passwordhasFocus = useState(false);
|
||||
|
||||
focusNodeDokterID.addListener(() {
|
||||
if (focusNodeDokterID.hasPrimaryFocus) {
|
||||
dokterIDhasFocus.value = true;
|
||||
usernamehasFocus.value = false;
|
||||
passwordhasFocus.value = false;
|
||||
} else {
|
||||
dokterIDhasFocus.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
focusNodeDokterUsername.addListener(() {
|
||||
if (focusNodeDokterUsername.hasFocus) {
|
||||
dokterIDhasFocus.value = false;
|
||||
usernamehasFocus.value = true;
|
||||
passwordhasFocus.value = false;
|
||||
} else {
|
||||
usernamehasFocus.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
focusNodeDokterPassword.addListener(() {
|
||||
if (focusNodeDokterPassword.hasFocus) {
|
||||
dokterIDhasFocus.value = false;
|
||||
usernamehasFocus.value = false;
|
||||
passwordhasFocus.value = true;
|
||||
} else {
|
||||
passwordhasFocus.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
ref.listen(loginProvider, (prev, next) {
|
||||
if (next is LoginStateLoading) {
|
||||
isLoading.value = true;
|
||||
} else if (next is LoginStateError) {
|
||||
isLoading.value = false;
|
||||
errorMessage.value = next.message;
|
||||
Timer(const Duration(seconds: 3), () {
|
||||
errorMessage.value = "";
|
||||
});
|
||||
} else if (next is LoginStateDone) {
|
||||
isLoading.value = false;
|
||||
isSuccess.value = true;
|
||||
ref.read(currentPageProvider.state).update((state) => 0);
|
||||
ref.read(currentUserProvider.notifier).state = next.model;
|
||||
|
||||
// print(next.model.model.doctorName);
|
||||
|
||||
// Navigator.of(context).pop();
|
||||
// Navigator.of(context).pushNamedAndRemoveUntil(menuRoute,(route) => true,);
|
||||
// Navigator.popAndPushNamed(context,menuRoute);
|
||||
|
||||
Navigator.of(context)
|
||||
.pushNamedAndRemoveUntil(menuRoute, (route) => false);
|
||||
}
|
||||
});
|
||||
|
||||
return ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 30),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: Constant.getActualXPhone(context: context, x: 40),
|
||||
right: Constant.getActualXPhone(context: context, x: 40),
|
||||
),
|
||||
child: Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 283),
|
||||
height: Constant.getActualYPhone(context: context, y: 100),
|
||||
decoration: BoxDecoration(
|
||||
// color: Colors.green,
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.fitWidth,
|
||||
// image: AssetImage('images/logo_pramita.png'),
|
||||
image: AssetImage('images/logo_gabung_bg_png.png'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 60),
|
||||
),
|
||||
// form
|
||||
Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 180),
|
||||
height: Constant.getActualYPhone(context: context, y: 40),
|
||||
// color: Colors.amber,
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text(
|
||||
"LOGIN",
|
||||
style: Constant.titleH3_700V2(context: context)
|
||||
.copyWith(color: Constant.subTitleBlack),
|
||||
),
|
||||
Text(
|
||||
" DOKTER",
|
||||
style: Constant.titleH3_700V2(context: context).copyWith(
|
||||
color: Color(0xffd70302),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 20),
|
||||
),
|
||||
|
||||
// notify error dari backend start
|
||||
|
||||
if (isLoading.value)
|
||||
SizedBox(
|
||||
width: Constant.getActualXPhone(context: context, x: 20),
|
||||
height: Constant.getActualYPhone(context: context, y: 20),
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
|
||||
if (errorMessage.value != "")
|
||||
Center(
|
||||
child: Text(
|
||||
"Peringatan : ${errorMessage.value}",
|
||||
style: Constant.titleH7_700(context: context)
|
||||
.copyWith(color: Constant.textRedProblemMaketing),
|
||||
),
|
||||
),
|
||||
|
||||
// notify error dari backend end
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
),
|
||||
// textfield marketing ID
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: Constant.getActualXPhone(context: context, x: 100),
|
||||
right: Constant.getActualXPhone(context: context, x: 100),
|
||||
),
|
||||
child: Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 320),
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
// color: Colors.blue,
|
||||
// decoration: BoxDecoration(
|
||||
// // border: Border.all(color: Colors.black),
|
||||
// color: Color(0xffee0606),
|
||||
// borderRadius: BorderRadius.circular(30),
|
||||
// ),
|
||||
child: CustomTextFieldLogin(
|
||||
// onTap: () {
|
||||
// dokterIDhasFocus.value = true;
|
||||
// },
|
||||
// onEditingComplete: () {
|
||||
// dokterIDhasFocus.value = false;
|
||||
// },
|
||||
onChange: (String x) {
|
||||
// print(x);
|
||||
if (x.isNotEmpty || ctrlDokterId.text.isNotEmpty) {
|
||||
print('onchange');
|
||||
focusNodeDokterID.requestFocus();
|
||||
}
|
||||
},
|
||||
ctrl: ctrlDokterId,
|
||||
focusNode: focusNodeDokterID,
|
||||
hasFocus: dokterIDhasFocus.value,
|
||||
isPassword: false,
|
||||
hintText: "DOKTER ID",
|
||||
labelText: "DOKTER ID",
|
||||
isError: (errorMessage.value != "") ? true : false,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 40),
|
||||
),
|
||||
// textfield username
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: Constant.getActualXPhone(context: context, x: 100),
|
||||
right: Constant.getActualXPhone(context: context, x: 100),
|
||||
),
|
||||
child: Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 320),
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
// color: Colors.blue,
|
||||
// decoration: BoxDecoration(
|
||||
// // border: Border.all(color: Colors.black),
|
||||
// color: Color(0xffee0606),
|
||||
// borderRadius: BorderRadius.circular(30),
|
||||
// ),
|
||||
child: CustomTextFieldLogin(
|
||||
// onTap: () {
|
||||
// if (ctrlUsername.text.isNotEmpty) {
|
||||
// focusNodeDokterUsername.requestFocus();
|
||||
// }
|
||||
// },
|
||||
onChange: (String x) {
|
||||
// print(x);
|
||||
if (x.isNotEmpty || ctrlUsername.text.isNotEmpty) {
|
||||
focusNodeDokterUsername.requestFocus();
|
||||
}
|
||||
},
|
||||
ctrl: ctrlUsername,
|
||||
focusNode: focusNodeDokterUsername,
|
||||
hasFocus: usernamehasFocus.value,
|
||||
isPassword: false,
|
||||
hintText: "USERNAME",
|
||||
labelText: "USERNAME",
|
||||
isError: (errorMessage.value != "") ? true : false,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 40),
|
||||
),
|
||||
// textfield password
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: Constant.getActualXPhone(context: context, x: 100),
|
||||
right: Constant.getActualXPhone(context: context, x: 100),
|
||||
),
|
||||
child: Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 320),
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
// color: Colors.blue,
|
||||
// decoration: BoxDecoration(
|
||||
// // border: Border.all(color: Colors.black),
|
||||
// color: Color(0xffee0606),
|
||||
// borderRadius: BorderRadius.circular(30),
|
||||
// ),
|
||||
child: CustomTextFieldLogin(
|
||||
// onTap: () {
|
||||
// if (ctrlPassword.text.isNotEmpty) {
|
||||
// focusNodeDokterPassword.requestFocus();
|
||||
// }
|
||||
// },
|
||||
onChange: (String x) {
|
||||
// print(x);
|
||||
if (x.isNotEmpty || ctrlPassword.text.isNotEmpty) {
|
||||
focusNodeDokterPassword.requestFocus();
|
||||
}
|
||||
},
|
||||
ctrl: ctrlPassword,
|
||||
focusNode: focusNodeDokterPassword,
|
||||
hasFocus: passwordhasFocus.value,
|
||||
isPassword: false,
|
||||
obscureText: isPasswordObscured.value,
|
||||
hintText: "PASSWORD",
|
||||
labelText: "PASSWORD",
|
||||
onToggle: () {
|
||||
isPasswordObscured.value = !isPasswordObscured.value;
|
||||
},
|
||||
isError: (errorMessage.value != "") ? true : false,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 40),
|
||||
),
|
||||
// button login
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: Constant.getActualXPhone(context: context, x: 100),
|
||||
right: Constant.getActualXPhone(context: context, x: 100),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: Constant.getActualXPhone(context: context, x: 320),
|
||||
height: Constant.getActualYPhone(context: context, y: 50),
|
||||
child: ElevatedButton(
|
||||
onPressed: (isSuccess.value == true)
|
||||
? () {}
|
||||
: () {
|
||||
if (ctrlUsername.text.isEmpty ||
|
||||
ctrlDokterId.text.isEmpty ||
|
||||
ctrlPassword.text.isEmpty) {
|
||||
isLoading.value = true;
|
||||
errorMessage.value = 'Inputan harus diisi';
|
||||
Timer(const Duration(seconds: 3), () {
|
||||
isLoading.value = false;
|
||||
errorMessage.value = "";
|
||||
});
|
||||
} else {
|
||||
ref.read(loginProvider.notifier).login(
|
||||
username: ctrlUsername.text,
|
||||
dokterId: ctrlDokterId.text,
|
||||
password: ctrlPassword.text);
|
||||
}
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateColor.resolveWith((st) =>
|
||||
(isSuccess.value == true)
|
||||
? Constant.textGrey
|
||||
: Constant.buttonRedBackgroundColor),
|
||||
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
side: BorderSide(color: Colors.red),
|
||||
),
|
||||
),
|
||||
shadowColor: MaterialStateProperty.all(Color(0xffff48423d)),
|
||||
elevation: MaterialStateProperty.all(4.0),
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'SIGN IN',
|
||||
style: Constant.titleH3_700(context: context)
|
||||
.copyWith(color: Constant.textLoginColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Constant.getActualYPhone(context: context, y: 22),
|
||||
),
|
||||
|
||||
// versi
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Versi ${Constant.version}',
|
||||
style: Constant.titleH4Login(context: context)
|
||||
.copyWith(color: Constant.textGrey),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
19
app_petty_cash/lib/screen/login/block_header.dart
Normal file
19
app_petty_cash/lib/screen/login/block_header.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
|
||||
class BlokHeader extends StatelessWidget {
|
||||
const BlokHeader({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Image.asset(
|
||||
'images/Background.png',
|
||||
fit: BoxFit.fill,
|
||||
width: Constant.getActualXPhone(context: context, x: 390),
|
||||
height: double.infinity,
|
||||
);
|
||||
}
|
||||
}
|
||||
290
app_petty_cash/lib/screen/login/custom_text_field.dart
Normal file
290
app_petty_cash/lib/screen/login/custom_text_field.dart
Normal file
@@ -0,0 +1,290 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
|
||||
// login
|
||||
class CustomTextFieldLogin extends StatelessWidget {
|
||||
final String hintText;
|
||||
final String labelText;
|
||||
final bool isPassword;
|
||||
final bool obscureText;
|
||||
final bool isMaxLine;
|
||||
final void Function()? onToggle;
|
||||
final TextEditingController? ctrl;
|
||||
final void Function(String)? onChange;
|
||||
final void Function(String)? onSubmitted;
|
||||
final void Function()? onTap;
|
||||
final void Function()? onEditingComplete;
|
||||
final FocusNode? focusNode;
|
||||
final bool isPrefix;
|
||||
|
||||
final bool isError;
|
||||
final bool isTextArea;
|
||||
final bool isReadOnly;
|
||||
|
||||
final bool hasFocus;
|
||||
|
||||
const CustomTextFieldLogin({
|
||||
Key? key,
|
||||
required this.hintText,
|
||||
required this.labelText,
|
||||
this.isPassword = false,
|
||||
this.isMaxLine = false,
|
||||
this.onToggle,
|
||||
this.obscureText = false,
|
||||
this.ctrl,
|
||||
this.onChange,
|
||||
this.onSubmitted,
|
||||
this.focusNode,
|
||||
this.isPrefix = false,
|
||||
this.isTextArea = false,
|
||||
this.isError = false,
|
||||
this.isReadOnly = false,
|
||||
this.hasFocus = false,
|
||||
this.onTap,
|
||||
this.onEditingComplete
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
autofocus: true,
|
||||
showCursor: (hasFocus) ? true : false,
|
||||
readOnly: isReadOnly,
|
||||
controller: ctrl,
|
||||
enableInteractiveSelection: false,
|
||||
style:
|
||||
Constant.titleH4Login(context: context).copyWith(color: Colors.white),
|
||||
obscureText: obscureText,
|
||||
onChanged: onChange,
|
||||
onSubmitted: onSubmitted,
|
||||
onTap: onTap,
|
||||
onEditingComplete: onEditingComplete,
|
||||
focusNode: focusNode,
|
||||
maxLines: (isTextArea) ? 4 : 1,
|
||||
cursorColor: Colors.white,
|
||||
decoration: InputDecoration(
|
||||
fillColor: (hasFocus) ? Color(0xffff6868) : Color(0xffd90504),
|
||||
filled: true,
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Constant.buttonLoginBackgroundColor, width: 2),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
enabledBorder: (isError == true)
|
||||
? OutlineInputBorder(
|
||||
// ignore: prefer_const_constructors
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
borderSide: BorderSide(
|
||||
color: Constant.textRedProblemMaketing,
|
||||
width: 1,
|
||||
),
|
||||
)
|
||||
: OutlineInputBorder(
|
||||
// ignore: prefer_const_constructors
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
borderSide: BorderSide(
|
||||
color: Constant.buttonLoginBackgroundColor,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
// hintStyle: Constant.body1_400(context: context)
|
||||
// .copyWith(color: Constant.textBlack),
|
||||
// labelStyle: Constant.body3_400(context: context),
|
||||
hintStyle:
|
||||
Constant.body3_400(context: context).copyWith(color: Colors.white),
|
||||
labelStyle: Constant.body3_400(context: context),
|
||||
// labelText: labelText,
|
||||
hintText: hintText,
|
||||
alignLabelWithHint: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
prefixIcon: isPrefix ? const Icon(Icons.search) : null,
|
||||
prefixStyle: isPrefix ? Constant.titleH4Login(context: context) : null,
|
||||
// suffixIcon: isPassword
|
||||
// ? IconButton(
|
||||
// alignment: Alignment.centerRight,
|
||||
// onPressed: onToggle,
|
||||
// icon: const Icon(Icons.remove_red_eye),
|
||||
// iconSize: Constant.getActualYPhone(context: context, y: 24),
|
||||
// )
|
||||
// : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// selain login
|
||||
class CustomTextField extends StatelessWidget {
|
||||
final String hintText;
|
||||
final String labelText;
|
||||
final bool isPassword;
|
||||
final bool obscureText;
|
||||
final bool isMaxLine;
|
||||
final void Function()? onToggle;
|
||||
final TextEditingController? ctrl;
|
||||
final void Function(String)? onChange;
|
||||
final void Function(String)? onSubmitted;
|
||||
final FocusNode? focusNode;
|
||||
final bool isPrefix;
|
||||
|
||||
final bool isError;
|
||||
final bool isTextArea;
|
||||
final bool isReadOnly;
|
||||
|
||||
const CustomTextField(
|
||||
{Key? key,
|
||||
required this.hintText,
|
||||
required this.labelText,
|
||||
this.isPassword = false,
|
||||
this.isMaxLine = false,
|
||||
this.onToggle,
|
||||
this.obscureText = false,
|
||||
this.ctrl,
|
||||
this.onChange,
|
||||
this.onSubmitted,
|
||||
this.focusNode,
|
||||
this.isPrefix = false,
|
||||
this.isTextArea = false,
|
||||
this.isError = false,
|
||||
this.isReadOnly = false})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
readOnly: isReadOnly,
|
||||
controller: ctrl,
|
||||
style: Constant.titleH4Login(context: context),
|
||||
obscureText: obscureText,
|
||||
onChanged: onChange,
|
||||
onSubmitted: onSubmitted,
|
||||
focusNode: focusNode,
|
||||
maxLines: (isTextArea) ? 4 : 1,
|
||||
decoration: InputDecoration(
|
||||
// focusedBorder: OutlineInputBorder(
|
||||
// borderSide: BorderSide(color: Constant.textGrey, width: 1),
|
||||
// borderRadius: BorderRadius.circular(8.0),
|
||||
// ),
|
||||
enabledBorder: (isError == true)
|
||||
? OutlineInputBorder(
|
||||
// ignore: prefer_const_constructors
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(
|
||||
color: Constant.textRedProblemMaketing,
|
||||
width: 1,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
// hintStyle: Constant.body1_400(context: context)
|
||||
// .copyWith(color: Constant.textBlack),
|
||||
// labelStyle: Constant.body3_400(context: context),
|
||||
hintStyle: Constant.body3_400(context: context),
|
||||
labelStyle: Constant.body3_400(context: context),
|
||||
labelText: labelText,
|
||||
hintText: hintText,
|
||||
alignLabelWithHint: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
prefixIcon: isPrefix ? const Icon(Icons.search) : null,
|
||||
prefixStyle: isPrefix ? Constant.titleH4Login(context: context) : null,
|
||||
// suffixIcon: isPassword
|
||||
// ? IconButton(
|
||||
// alignment: Alignment.centerRight,
|
||||
// onPressed: onToggle,
|
||||
// icon: const Icon(Icons.remove_red_eye),
|
||||
// iconSize: Constant.getActualYPhone(context: context, y: 24),
|
||||
// )
|
||||
// : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// untuk inputan selain login
|
||||
class CustomTextFieldV2 extends StatelessWidget {
|
||||
final String hintText;
|
||||
final String labelText;
|
||||
final bool isPassword;
|
||||
final bool obscureText;
|
||||
final bool isMaxLine;
|
||||
final void Function()? onToggle;
|
||||
final TextEditingController? ctrl;
|
||||
final void Function(String)? onChange;
|
||||
final void Function(String)? onSubmitted;
|
||||
final FocusNode? focusNode;
|
||||
final bool isPrefix;
|
||||
final InkWell? suffixIcon;
|
||||
|
||||
final bool isError;
|
||||
final bool isTextArea;
|
||||
|
||||
const CustomTextFieldV2(
|
||||
{Key? key,
|
||||
required this.hintText,
|
||||
required this.labelText,
|
||||
this.isPassword = false,
|
||||
this.isMaxLine = false,
|
||||
this.onToggle,
|
||||
this.obscureText = false,
|
||||
this.ctrl,
|
||||
this.onChange,
|
||||
this.onSubmitted,
|
||||
this.focusNode,
|
||||
this.isPrefix = false,
|
||||
this.isTextArea = false,
|
||||
this.isError = false,
|
||||
this.suffixIcon})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: ctrl,
|
||||
style: Constant.titleH4Login(context: context),
|
||||
obscureText: obscureText,
|
||||
onChanged: onChange,
|
||||
onSubmitted: onSubmitted,
|
||||
focusNode: focusNode,
|
||||
maxLines: (isTextArea) ? 4 : 1,
|
||||
decoration: InputDecoration(
|
||||
// focusedBorder: OutlineInputBorder(
|
||||
// borderSide: BorderSide(color: Constant.textGrey, width: 1),
|
||||
// borderRadius: BorderRadius.circular(8.0),
|
||||
// ),
|
||||
enabledBorder: (isError == true)
|
||||
? OutlineInputBorder(
|
||||
// ignore: prefer_const_constructors
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(
|
||||
color: Constant.textRedProblemMaketing,
|
||||
width: 1,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
hintStyle: Constant.body3_400(context: context),
|
||||
labelStyle: Constant.body3_400(context: context),
|
||||
labelText: labelText,
|
||||
hintText: hintText,
|
||||
alignLabelWithHint: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
prefixIcon: isPrefix ? const Icon(Icons.search) : null,
|
||||
prefixStyle:
|
||||
isPrefix ? Constant.titleH4Login(context: context) : null,
|
||||
suffixIcon: suffixIcon
|
||||
// suffixIcon: isPassword
|
||||
// ? IconButton(
|
||||
// alignment: Alignment.centerRight,
|
||||
// onPressed: onToggle,
|
||||
// icon: const Icon(Icons.remove_red_eye),
|
||||
// iconSize: Constant.getActualYPhone(context: context, y: 24),
|
||||
// )
|
||||
// : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
83
app_petty_cash/lib/screen/login/login_provider.dart
Normal file
83
app_petty_cash/lib/screen/login/login_provider.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../../model/auth_model.dart';
|
||||
import '../../provider/current_user_provider.dart';
|
||||
import '../../provider/dio_provider.dart';
|
||||
import '../../repository/auth_repository.dart';
|
||||
import '../../repository/base_repository.dart';
|
||||
|
||||
// 3. state provider
|
||||
final loginProvider = StateNotifierProvider<LoginNotifier, LoginState>(
|
||||
(ref) => LoginNotifier(ref: ref));
|
||||
|
||||
// 2. notifier
|
||||
class LoginNotifier extends StateNotifier<LoginState> {
|
||||
final Ref ref;
|
||||
LoginNotifier({required this.ref}) : super(LoginStateInit());
|
||||
void login({
|
||||
required String username,
|
||||
required String dokterId,
|
||||
required String password,
|
||||
}) async {
|
||||
try {
|
||||
state = LoginStateLoading();
|
||||
final resp = await AuthRepository(dio: ref.read(dioProvider))
|
||||
.login(username: username, dokterId: dokterId, password: password);
|
||||
|
||||
// print(resp);
|
||||
state = LoginStateDone(model: resp);
|
||||
//Simpan ke token
|
||||
final shared = await SharedPreferences.getInstance();
|
||||
final token = jsonEncode({
|
||||
"date": DateTime.now().toString(),
|
||||
"model": resp.model,
|
||||
"token": resp.token
|
||||
});
|
||||
await shared.setString(Constant.bearerName, token);
|
||||
ref.read(currentUserProvider.notifier).state = resp;
|
||||
|
||||
// print(shared.getString(Constant.bearerName));
|
||||
} catch (e) {
|
||||
if (e is BaseRepositoryException) {
|
||||
state = LoginStateError(message: e.message);
|
||||
} else {
|
||||
state = LoginStateError(message: e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. state
|
||||
abstract class LoginState extends Equatable {
|
||||
final DateTime date;
|
||||
const LoginState(this.date);
|
||||
@override
|
||||
List<Object?> get props => [date];
|
||||
}
|
||||
|
||||
class LoginStateInit extends LoginState {
|
||||
LoginStateInit() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class LoginStateLoading extends LoginState {
|
||||
LoginStateLoading() : super(DateTime.now());
|
||||
}
|
||||
|
||||
class LoginStateError extends LoginState {
|
||||
final String message;
|
||||
LoginStateError({
|
||||
required this.message,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
|
||||
class LoginStateDone extends LoginState {
|
||||
final AuthModel model;
|
||||
LoginStateDone({
|
||||
required this.model,
|
||||
}) : super(DateTime.now());
|
||||
}
|
||||
79
app_petty_cash/lib/screen/login/login_screen.dart
Normal file
79
app_petty_cash/lib/screen/login/login_screen.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
// ignore_for_file: use_build_context_synchronously, prefer_const_constructors
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../../app/route.dart';
|
||||
import '../../model/auth_model.dart';
|
||||
import '../../provider/current_menu_provider.dart';
|
||||
import '../../provider/current_user_provider.dart';
|
||||
import 'block_body.dart';
|
||||
import 'block_bodyv2.dart';
|
||||
|
||||
class LoginScreen extends HookConsumerWidget {
|
||||
const LoginScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final shared = await SharedPreferences.getInstance();
|
||||
final bearerString = shared.get(Constant.bearerName).toString();
|
||||
final xmodel = jsonDecode(bearerString);
|
||||
if (xmodel == null) return;
|
||||
final authModel = AuthModel(
|
||||
token: xmodel["token"],
|
||||
model: AuthDoctorModel(
|
||||
M_UserM_DoctorID: xmodel["model"]["M_UserM_DoctorID"],
|
||||
M_UserUsername: xmodel["model"]["M_UserUsername"],
|
||||
M_UserM_DoctorCode: xmodel["model"]["M_UserM_DoctorCode"],
|
||||
M_UserM_MouID: xmodel["model"]["M_UserM_MouID"] ?? "",
|
||||
M_UserID: xmodel["model"]["M_UserID"]),
|
||||
);
|
||||
ref.read(currentUserProvider.notifier).state = authModel;
|
||||
|
||||
ref.read(currentPageProvider.state).update((state) => 0);
|
||||
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
menuRoute,
|
||||
(route) => false,
|
||||
);
|
||||
});
|
||||
return () {};
|
||||
}, []);
|
||||
|
||||
return GestureDetector(
|
||||
// buat dismiss keyboard
|
||||
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
backgroundColor: Constant.backgroundColor,
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 390),
|
||||
height: Constant.getActualYPhone(context: context, y: 844),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Color(0xffdadae6),
|
||||
Color(0xfff6f7fb),
|
||||
],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
),
|
||||
),
|
||||
// child: BlockBody(),
|
||||
child: BlockBodyV2(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
64
app_petty_cash/lib/screen/splash/splash_screen.dart
Normal file
64
app_petty_cash/lib/screen/splash/splash_screen.dart
Normal file
@@ -0,0 +1,64 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../../app/route.dart';
|
||||
import '../../provider/current_user_provider.dart';
|
||||
|
||||
class SplashScreen extends HookConsumerWidget {
|
||||
const SplashScreen({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// useEffect(() {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
// final doctorID =
|
||||
// ref.read(currentUserProvider)?.model.M_UserM_DoctorCode ?? "0";
|
||||
// // print(doctorID);
|
||||
|
||||
// if (doctorID == "0") {
|
||||
// //not login
|
||||
|
||||
// Navigator.of(context)
|
||||
// .pushNamedAndRemoveUntil(loginRoute, (route) => false);
|
||||
// return;
|
||||
|
||||
// } else {
|
||||
// Navigator.of(context)
|
||||
// .pushNamedAndRemoveUntil(menuRoute, (route) => false);
|
||||
// }
|
||||
// });
|
||||
// return () {};
|
||||
// }, []);
|
||||
|
||||
// setelah 3 detik redirect ke login route
|
||||
Timer(const Duration(seconds: 3), () {
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
loginRoute,
|
||||
(route) => false,
|
||||
);
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
// appBar: AppBar(
|
||||
// backgroundColor: Constant.backgroundColor,
|
||||
// elevation: 0,
|
||||
// ),
|
||||
backgroundColor: Constant.backgroundColor,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
width: Constant.getActualXPhone(context: context, x: 390),
|
||||
height: Constant.getActualYPhone(context: context, y: 844),
|
||||
child: Image.asset(
|
||||
"images/splashscreen.png",
|
||||
fit: BoxFit.cover,
|
||||
// scale: 1,
|
||||
),
|
||||
// color: Colors.green,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user