step 3 : copy login screen app dokter

This commit is contained in:
sindhu
2024-01-12 14:22:49 +07:00
parent b4500de798
commit 3ae6622951
16 changed files with 2143 additions and 0 deletions

View 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),
// ),
// ),
// ),
// ),
],
),
),
],
);
}
}

View 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),
),
),
],
);
}
}

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

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

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

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

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