Files
scan-ktp/lib/screen/no-login-scan/no_login_edit_scan_screen.dart
sindhu 29c38b011a step 25 : upload ktp, qrcode string, edit data,
check token expired 10 menit dari expired_date
2025-02-28 05:21:17 +07:00

716 lines
24 KiB
Dart

import 'package:dropdown_button2/dropdown_button2.dart';
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 'package:scanktpflutter/model/no-login/no_login_sex_model.dart';
import 'package:scanktpflutter/provider/no-login-scan/no_login_scan_provider.dart';
import 'package:scanktpflutter/screen/no-login-scan/no_login_edit_scan_provider.dart';
import 'package:scanktpflutter/screen/scan/edit_scan_provider.dart';
import '../../model/sex_model.dart';
import '../../provider/no-login/no_login_current_user_provider.dart';
import '../../widget/customsnackbarwidget.dart';
import '../../app/app_extension.dart';
import '../../app/constant.dart';
import '../../app/route.dart';
import '../../provider/current_user_provider.dart';
import '../home/list_riwayat_scan_provider.dart';
import '../no-login-home/no_login_riwayat_scan_provider.dart';
import '../no-login/no_login_refresh_token_provider.dart';
import 'no_login_sex_provider.dart';
class NoLoginEditScanScreen extends HookConsumerWidget {
const NoLoginEditScanScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [
SystemUiOverlay.bottom,
]);
final currentUser = ref.watch(noLoginCurrentUserProvider);
// final username = currentUser?.model.username ?? "-";
final host = currentUser?.host ?? "";
final isLoading = useState<bool>(false);
final client_id = currentUser?.client_id ?? "";
final token = currentUser?.token ?? "";
final expire_date = currentUser?.expire_date ?? "";
final readProsesAksi = ref.watch(noLoginprosesAksi);
final baseUrl = "https://$host/";
final selectedPersonId = ref.watch(selectedPersonIdx);
final listDataEdit = ref.watch(selectedEdit);
final listSex = useState<List<NoLoginSexModel>>(
List.empty(growable: true),
);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
if (client_id == "0" || token == "") {
// not logged in and doesn't have token
Navigator.of(context)
.pushNamedAndRemoveUntil(noLoginRoute, (route) => false);
return;
}
if (token != "") {
if (await isTokenExpired() == true) {
print("Token expired, refreshing...");
ref.read(noLoginRefreshTokenProvider.notifier).refreshToken(
client_id: client_id,
host: host,
expire_date: expire_date,
);
}
}
});
return () {};
}, [currentUser]);
// sex
ref.listen(noLoginSexProvider, (prev, next) {
if (next is NoLoginSexStateLoading) {
isLoading.value = true;
} else if (next is NoLoginSexStateError) {
isLoading.value = false;
// errorMessage.value = next.message;
snackbarWidget(
context,
next.message,
snackbarType.error,
Duration(seconds: 3),
);
} else if (next is NoLoginSexStateDone) {
isLoading.value = false;
listSex.value = next.model;
// ref.read(eSexCtr.notifier).state =
// TextEditingController(text: next.model[0].m_sexname);
final sexSel = ref.read(selectedEdit.notifier).state;
if (next.model.isNotEmpty) {
final matchedItems = next.model
.where((item) => item.M_SexID == sexSel.personSex)
.toList();
if (matchedItems.length == 1) {
ref.read(eSexCtr.notifier).state = TextEditingController(
text: matchedItems[0].m_sexname,
);
} else {
// Jika lebih dari satu atau tidak ada yang cocok
print('Error: Multiple or no matching items found.');
// Bisa juga mengatur default value jika tidak ditemukan
ref.read(eSexCtr.notifier).state =
TextEditingController(text: ''); // atau nilai default lain
}
}
}
});
// isLoading
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timestamp) async {
if (isLoading.value == true) {
snackbarWidget(
context,
"Sedang Memuat Data",
snackbarType.warning,
Duration(days: 1),
);
}
});
return () {};
}, [isLoading]);
// check person id
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timestamp) async {
final personIDx = selectedPersonId;
if (personIDx == "0") {
snackbarWidget(
context,
'Gagal mendapatkan data',
snackbarType.error,
Duration(seconds: 3),
);
Navigator.of(context)
.pushNamedAndRemoveUntil(homeRoute, (route) => false);
return;
} else {
// listDataEdit
// set ke inputan
ref.read(eQrCode.notifier).state =
TextEditingController(text: listDataEdit.personQrCode);
ref.read(eNikCtr.notifier).state =
TextEditingController(text: listDataEdit.personNIK);
ref.read(eNamaCtr.notifier).state =
TextEditingController(text: listDataEdit.personName);
ref.read(eDobCtr.notifier).state = TextEditingController(
text: formatDateJiffy(listDataEdit.personDob));
ref.read(eDobDt.notifier).state =
DateTime.parse(listDataEdit.personDob);
final parsedDate = DateTime.parse(listDataEdit.personDob);
// print("Parsed Date: $parsedDate");
ref.read(eDobDt.notifier).state = parsedDate;
// sex
ref.read(noLoginSexProvider.notifier).sex(
host: host,
client_id: client_id,
token: token,
);
}
});
return () {};
}, [selectedPersonId]);
// date picker
Future<void> _selectDate(BuildContext context, WidgetRef ref) async {
DateTime? newSelectedDate = await showDatePicker(
initialEntryMode: DatePickerEntryMode.calendarOnly,
context: context,
initialDate: (listDataEdit.personDob != "0000-00-00")
? DateTime.parse(listDataEdit.personDob)
: DateTime.now(),
firstDate: DateTime(1900),
lastDate: DateTime(2100),
);
if (newSelectedDate != null) {
ref.read(eDobDt.notifier).state = newSelectedDate;
ref.read(eDobCtr.notifier).state.text =
formatDateJiffy(newSelectedDate.toLocal().toString());
}
}
// refreshToken Provider
ref.listen(noLoginRefreshTokenProvider, (prev, next) async {
if (next is NoLoginRefreshTokenStateLoading) {
isLoading.value = true;
} else if (next is NoLoginRefreshTokenStateError) {
isLoading.value = false;
// errorMessage.value = next.message;
snackbarWidget(
context,
next.message,
snackbarType.error,
Duration(seconds: 3),
);
} else if (next is NoLoginRefreshTokenStateDone) {
isLoading.value = false;
// check proses aksi yg berlangsung
if (readProsesAksi == Constant.getRiwayat) {
ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan(
host: host,
client_id: client_id,
token: token,
expire_date: expire_date,
);
} else if (readProsesAksi == Constant.postEditScan) {
ref.read(noLoginEditScanProvider.notifier).editScan(
token: token,
host: host,
client_id: client_id,
Person_ID: selectedPersonId,
Person_NIK: ref.read(eNikCtr).text,
Person_Name: ref.read(eNamaCtr).text,
Person_Dob: ref.read(eDobCtr).text,
Person_Sex: ref.read(eSexSelected).M_SexID,
);
}
}
});
// void getRiwayat
void getRiwayat() async {
if (await isTokenExpired() == true) {
ref.read(noLoginprosesAksi.notifier).state = Constant.getRiwayat;
print("Token expired, refreshing...");
ref.read(noLoginRefreshTokenProvider.notifier).refreshToken(
client_id: client_id,
host: host,
expire_date: expire_date,
);
} else {
ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan(
host: host,
client_id: client_id,
token: token,
expire_date: expire_date,
);
}
}
// listRiwayProvider
ref.listen(noLoginRiwayatScanProvider, (prev, next) async {
if (next is NoLoginRiwayatScanStateLoading) {
isLoading.value = true;
} else if (next is NoLoginRiwayatScanStateError) {
isLoading.value = false;
// errorMessage.value = next.message;
snackbarWidget(
context,
next.message,
snackbarType.error,
Duration(seconds: 3),
);
} else if (next is NoLoginRiwayatScanStateDone) {
// isLoading.value = false;
Navigator.of(context).pop();
Navigator.of(context).pushNamedAndRemoveUntil(
noLoginHomeRoute,
(route) => false,
);
}
});
// proses edit
ref.listen(noLoginEditScanProvider, (prev, next) {
if (next is NoLoginEditScanStateLoading) {
isLoading.value = true;
} else if (next is NoLoginEditScanStateError) {
isLoading.value = false;
// errorMessage.value = next.message;
snackbarWidget(
context,
next.message,
snackbarType.error,
Duration(seconds: 3),
);
} else if (next is NoLoginEditScanStateDone) {
isLoading.value = false;
getRiwayat();
}
});
void postEdit() async {
if (await isTokenExpired() == true) {
ref.read(noLoginprosesAksi.notifier).state = Constant.postEditScan;
print("Token expired, refreshing...");
ref.read(noLoginRefreshTokenProvider.notifier).refreshToken(
client_id: client_id,
host: host,
expire_date: expire_date,
);
} else {
ref.read(noLoginEditScanProvider.notifier).editScan(
token: token,
host: host,
client_id: client_id,
Person_ID: selectedPersonId,
Person_NIK: ref.read(eNikCtr).text,
Person_Name: ref.read(eNamaCtr).text,
Person_Dob: ref.read(eDobCtr).text,
Person_Sex: ref.read(eSexSelected).M_SexID,
);
}
}
return GestureDetector(
onTap: () {
FocusManager.instance.primaryFocus!.unfocus();
},
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Constant.bgGrey,
body: ListView(
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
// atas
Image.asset(
'images/vektoratas.png',
width: double.infinity,
fit: BoxFit.cover,
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 34,
),
),
// image ktp
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: Image.network(
baseUrl + listDataEdit.personUrl,
fit: BoxFit.fitWidth,
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 32,
),
),
// inputan qrcode
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: Text(
'QR Code',
style: Constant.title_400(context: context).copyWith(
color: Constant.inputanGrey,
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: TextField(
readOnly: true,
maxLines: 2,
controller: ref.read(eQrCode),
decoration: InputDecoration(
// hintText: "NIK",
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey,
width: 1,
),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
width: 2,
),
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
// inputan nik
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: Text(
'NIK',
style: Constant.title_400(context: context).copyWith(
color: Constant.inputanGrey,
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: TextField(
controller: ref.read(eNikCtr),
keyboardType: TextInputType.number,
decoration: InputDecoration(
// hintText: "NIK",
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey,
width: 1,
),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
width: 2,
),
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
// inputan nama
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: Text(
'Nama',
style: Constant.title_400(context: context).copyWith(
color: Constant.inputanGrey,
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: TextField(
controller: ref.read(eNamaCtr),
decoration: InputDecoration(
// hintText: "NIK",
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey,
width: 1,
),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
width: 2,
),
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
// inputan dob
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: Text(
'DOB',
style: Constant.title_400(context: context).copyWith(
color: Constant.inputanGrey,
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: TextField(
readOnly: true,
controller: ref.read(eDobCtr),
onTap: () {
_selectDate(context, ref);
},
decoration: InputDecoration(
// hintText: "NIK",
suffixIcon: IconButton(
icon: Icon(Icons.calendar_today,
color: Constant.textCardGrey),
onPressed: () {
_selectDate(context, ref);
},
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey,
width: 1,
),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue,
width: 2,
),
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
// inputan jenis kelamin
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: Text(
'Jenis Kelamin',
style: Constant.title_400(context: context).copyWith(
color: Constant.inputanGrey,
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 16,
),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 12),
right: Constant.getActualYPhone(context: context, y: 12),
),
child: DropdownButtonHideUnderline(
child: DropdownButton2<NoLoginSexModel>(
isExpanded: true,
value: (listSex.value.isNotEmpty)
? listSex.value.firstWhere(
(sex) =>
sex.M_SexID ==
ref.read(eSexSelected.notifier).state.M_SexID,
orElse: () => listSex.value[0],
)
: null,
items: listSex.value.map((NoLoginSexModel g) {
return DropdownMenuItem<NoLoginSexModel>(
value: g,
child: Text(
g.m_sexname,
style: Constant.title_400(context: context).copyWith(
color: Colors.black,
fontWeight: FontWeight.normal,
),
),
);
}).toList(),
onChanged: (NoLoginSexModel? value) {
if (value != null) {
ref.read(eSexSelected.notifier).state = value;
ref.read(eSexCtr.notifier).state = TextEditingController(
text: value.m_sexname,
);
FocusScope.of(context).unfocus();
}
},
buttonStyleData: ButtonStyleData(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: Colors.grey),
),
padding: EdgeInsets.symmetric(horizontal: 12),
width: double.infinity,
),
dropdownStyleData: DropdownStyleData(
maxHeight: 100,
width: MediaQuery.of(context).size.width - 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.white,
),
offset: Offset(0, 2),
),
menuItemStyleData: MenuItemStyleData(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
onMenuStateChange: (isOpen) {
if (!isOpen) {
ref.read(eSexCtr).clear();
}
},
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 44,
),
),
// button save
Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(
context: context,
x: 16,
),
right: Constant.getActualXPhone(
context: context,
x: 16,
),
),
child: SizedBox(
width: double.infinity,
height: Constant.getActualYPhone(
context: context,
y: 48,
),
child: ElevatedButton(
onPressed: () {
postEdit();
},
style: ElevatedButton.styleFrom(
backgroundColor: Constant.bgButton,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 8,
shadowColor: Constant.bgButton.withOpacity(0.24),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'SAVE',
style:
Constant.titleButton500(context: context).copyWith(
color: Constant.textWhite,
),
),
],
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 70,
),
),
],
),
),
);
}
}