Compare commits
5 Commits
main
...
scanktpv2_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80eabdd65f | ||
|
|
29c38b011a | ||
|
|
464f747892 | ||
|
|
b723dc4596 | ||
|
|
a28830deb0 |
@@ -1,5 +1,46 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import 'constant.dart';
|
||||||
|
|
||||||
String formatDateJiffy(String serverDate) {
|
String formatDateJiffy(String serverDate) {
|
||||||
return Jiffy.parse(serverDate).format(pattern: 'dd-MM-yyyy');
|
return Jiffy.parse(serverDate).format(pattern: 'dd-MM-yyyy');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isTokenExpired() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final bearerString = prefs.getString(Constant.bearerName);
|
||||||
|
|
||||||
|
if (bearerString == null || bearerString == "null") {
|
||||||
|
print("BEARER STRING NULL");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final xmodel = jsonDecode(bearerString);
|
||||||
|
if (xmodel == null) return true;
|
||||||
|
|
||||||
|
final String? expireDateStr = xmodel['expire_date'];
|
||||||
|
|
||||||
|
print("EXPIRE DATE $expireDateStr");
|
||||||
|
|
||||||
|
if (expireDateStr == null || expireDateStr.isEmpty) {
|
||||||
|
print("Token EXPIRED (Tidak Ada Tanggal Expire)");
|
||||||
|
return true; // Anggap expired jika tidak ada data
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DateTime expiredDate = DateTime.parse(expireDateStr);
|
||||||
|
// DateTime batasExpired = expiredDate.subtract(Duration(minutes: 3));
|
||||||
|
DateTime batasExpired = expiredDate.subtract(Duration(minutes: 10));
|
||||||
|
DateTime now = DateTime.now();
|
||||||
|
|
||||||
|
final bool expired = now.isAfter(batasExpired);
|
||||||
|
print(expired ? "Token EXPIRED (Perlu Refresh)" : "Token MASIH AKTIF");
|
||||||
|
return expired;
|
||||||
|
} catch (e) {
|
||||||
|
print("Token EXPIRED (Format Salah)");
|
||||||
|
return true; // Anggap expired jika terjadi error parsing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ class Constant {
|
|||||||
// static double designHeight = 1024;
|
// static double designHeight = 1024;
|
||||||
// static double designWidth = 1440;
|
// static double designWidth = 1440;
|
||||||
|
|
||||||
|
// prosesAksi
|
||||||
|
static String getRiwayat = "getRiwayat";
|
||||||
|
static String postUploadFoto = "postUploadFoto";
|
||||||
|
static String postEditScan = "postEditScan";
|
||||||
|
|
||||||
// base url
|
// base url
|
||||||
static String baseURL = "http://devone.aplikasi.web.id/";
|
static String baseURL = "http://devone.aplikasi.web.id/";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:scanktpflutter/screen/no-login-scan/no_login_scan_screen.dart';
|
||||||
|
import '../screen/no-login-home/no_login_home_screen.dart';
|
||||||
|
import '../screen/no-login-scan/no_login_edit_scan_screen.dart';
|
||||||
|
import '../screen/no-login-splash/no_login_splash_screen.dart';
|
||||||
|
import '../screen/no-login/no_login_screen.dart';
|
||||||
|
|
||||||
import '../screen/home/home_screen.dart';
|
import '../screen/home/home_screen.dart';
|
||||||
import '../screen/login/login_screen.dart';
|
import '../screen/login/login_screen.dart';
|
||||||
@@ -12,6 +17,12 @@ const homeRoute = "/homeRoute";
|
|||||||
const scanRoute = "/scanRoute";
|
const scanRoute = "/scanRoute";
|
||||||
const editScanRoute = "/editScanRoute";
|
const editScanRoute = "/editScanRoute";
|
||||||
|
|
||||||
|
const noLoginSplashRoute = "/noLoginSplashRoute";
|
||||||
|
const noLoginRoute = "/noLoginRoute";
|
||||||
|
const noLoginHomeRoute = "/noLoginHomeRoute";
|
||||||
|
const noLoginScanRoute = "/noLoginScanRoute";
|
||||||
|
const noLoginEditScanRoute = "/noLoginEditScanRoute";
|
||||||
|
|
||||||
class AppRoute {
|
class AppRoute {
|
||||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||||
// splash screen
|
// splash screen
|
||||||
@@ -25,6 +36,16 @@ class AppRoute {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.name == noLoginSplashRoute) {
|
||||||
|
return MaterialPageRoute(builder: (context) {
|
||||||
|
return MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
textScaler: TextScaler.linear(1.0), padding: EdgeInsets.all(0)),
|
||||||
|
child: NoLoginSplashScreen(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// login screen
|
// login screen
|
||||||
if (settings.name == loginRoute) {
|
if (settings.name == loginRoute) {
|
||||||
return MaterialPageRoute(builder: (context) {
|
return MaterialPageRoute(builder: (context) {
|
||||||
@@ -36,6 +57,16 @@ class AppRoute {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.name == noLoginRoute) {
|
||||||
|
return MaterialPageRoute(builder: (context) {
|
||||||
|
return MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
textScaler: TextScaler.linear(1.0), padding: EdgeInsets.all(0)),
|
||||||
|
child: NoLoginScreen(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// home screen
|
// home screen
|
||||||
if (settings.name == homeRoute) {
|
if (settings.name == homeRoute) {
|
||||||
return MaterialPageRoute(builder: (context) {
|
return MaterialPageRoute(builder: (context) {
|
||||||
@@ -47,6 +78,16 @@ class AppRoute {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.name == noLoginHomeRoute) {
|
||||||
|
return MaterialPageRoute(builder: (context) {
|
||||||
|
return MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
textScaler: TextScaler.linear(1.0), padding: EdgeInsets.all(0)),
|
||||||
|
child: NoLoginHomeScreen(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// scan screen
|
// scan screen
|
||||||
if (settings.name == scanRoute) {
|
if (settings.name == scanRoute) {
|
||||||
return MaterialPageRoute(builder: (context) {
|
return MaterialPageRoute(builder: (context) {
|
||||||
@@ -58,6 +99,16 @@ class AppRoute {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.name == noLoginScanRoute) {
|
||||||
|
return MaterialPageRoute(builder: (context) {
|
||||||
|
return MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
textScaler: TextScaler.linear(1.0), padding: EdgeInsets.all(0)),
|
||||||
|
child: NoLoginScanScreen(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// edit screen
|
// edit screen
|
||||||
if (settings.name == editScanRoute) {
|
if (settings.name == editScanRoute) {
|
||||||
return MaterialPageRoute(builder: (context) {
|
return MaterialPageRoute(builder: (context) {
|
||||||
@@ -69,12 +120,22 @@ class AppRoute {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.name == noLoginEditScanRoute) {
|
||||||
|
return MaterialPageRoute(builder: (context) {
|
||||||
|
return MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
textScaler: TextScaler.linear(1.0), padding: EdgeInsets.all(0)),
|
||||||
|
child: NoLoginEditScanScreen(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return MaterialPageRoute(builder: (context) {
|
return MaterialPageRoute(builder: (context) {
|
||||||
return MediaQuery(
|
return MediaQuery(
|
||||||
data: MediaQuery.of(context).copyWith(
|
data: MediaQuery.of(context).copyWith(
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
textScaler: TextScaler.linear(1.0)),
|
textScaler: TextScaler.linear(1.0)),
|
||||||
child: SplashScreen(),
|
child: NoLoginSplashScreen(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class MyApp extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
initialRoute: splashRoute,
|
initialRoute: noLoginRoute,
|
||||||
onGenerateRoute: AppRoute.generateRoute,
|
onGenerateRoute: AppRoute.generateRoute,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
90
lib/model/no-login/no_login_auth_model.dart
Normal file
90
lib/model/no-login/no_login_auth_model.dart
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
class NoLoginAuthModel {
|
||||||
|
final String token;
|
||||||
|
final String host;
|
||||||
|
final String client_id;
|
||||||
|
final String expire_date;
|
||||||
|
final bool isLogin;
|
||||||
|
|
||||||
|
NoLoginAuthModel({
|
||||||
|
required this.host,
|
||||||
|
required this.token,
|
||||||
|
required this.client_id,
|
||||||
|
required this.expire_date,
|
||||||
|
required this.isLogin,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'host':host,
|
||||||
|
'token': token,
|
||||||
|
'client_id' :client_id,
|
||||||
|
'expire_date':expire_date,
|
||||||
|
'isLogin':isLogin,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// old
|
||||||
|
class UserModel {
|
||||||
|
final String userId;
|
||||||
|
final String username;
|
||||||
|
final String groupDashboard;
|
||||||
|
final String defaultSampleStationId;
|
||||||
|
final String staffName;
|
||||||
|
final String isCourier;
|
||||||
|
final String timeAutoLogout;
|
||||||
|
final String ip;
|
||||||
|
final String agent;
|
||||||
|
final String version;
|
||||||
|
final String lastLogin;
|
||||||
|
final int satelliteId;
|
||||||
|
|
||||||
|
UserModel({
|
||||||
|
required this.userId,
|
||||||
|
required this.username,
|
||||||
|
required this.groupDashboard,
|
||||||
|
required this.defaultSampleStationId,
|
||||||
|
required this.staffName,
|
||||||
|
required this.isCourier,
|
||||||
|
required this.timeAutoLogout,
|
||||||
|
required this.ip,
|
||||||
|
required this.agent,
|
||||||
|
required this.version,
|
||||||
|
required this.lastLogin,
|
||||||
|
required this.satelliteId,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return UserModel(
|
||||||
|
userId: json['M_UserID'] ?? '',
|
||||||
|
username: json['M_UserUsername'] ?? '',
|
||||||
|
groupDashboard: json['M_UserGroupDashboard'] ?? '',
|
||||||
|
defaultSampleStationId: json['M_UserDefaultT_SampleStationID'] ?? '',
|
||||||
|
staffName: json['M_StaffName'] ?? '',
|
||||||
|
isCourier: json['is_courier'] ?? '',
|
||||||
|
timeAutoLogout: json['time_autologout'] ?? '',
|
||||||
|
ip: json['ip'] ?? '',
|
||||||
|
agent: json['agent'] ?? '',
|
||||||
|
version: json['version'] ?? '',
|
||||||
|
lastLogin: json['last-login'] ?? '',
|
||||||
|
satelliteId: json['M_SatelliteID'] ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'M_UserID': userId,
|
||||||
|
'M_UserUsername': username,
|
||||||
|
'M_UserGroupDashboard': groupDashboard,
|
||||||
|
'M_UserDefaultT_SampleStationID': defaultSampleStationId,
|
||||||
|
'M_StaffName': staffName,
|
||||||
|
'is_courier': isCourier,
|
||||||
|
'time_autologout': timeAutoLogout,
|
||||||
|
'ip': ip,
|
||||||
|
'agent': agent,
|
||||||
|
'version': version,
|
||||||
|
'last-login': lastLogin,
|
||||||
|
'M_SatelliteID': satelliteId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
49
lib/model/no-login/no_login_edit_person_ktp_model.dart
Normal file
49
lib/model/no-login/no_login_edit_person_ktp_model.dart
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
class NoLoginEditPersonKtpModel {
|
||||||
|
final String personID;
|
||||||
|
final String personNIK;
|
||||||
|
final String personName;
|
||||||
|
final String personDob;
|
||||||
|
final String personSex;
|
||||||
|
final String personUrl;
|
||||||
|
final String personQrCode;
|
||||||
|
final String m_sexname;
|
||||||
|
|
||||||
|
NoLoginEditPersonKtpModel({
|
||||||
|
required this.personID,
|
||||||
|
required this.personNIK,
|
||||||
|
required this.personName,
|
||||||
|
required this.personDob,
|
||||||
|
required this.personSex,
|
||||||
|
required this.personUrl,
|
||||||
|
required this.personQrCode,
|
||||||
|
required this.m_sexname,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert JSON to Model
|
||||||
|
factory NoLoginEditPersonKtpModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return NoLoginEditPersonKtpModel(
|
||||||
|
personID: json['Person_ID'] ?? "",
|
||||||
|
personNIK: json['Person_NIK'] ?? "",
|
||||||
|
personName: json['Person_Name'] ?? "",
|
||||||
|
personDob: json['Person_Dob'] ?? DateTime.now(),
|
||||||
|
personSex: json['Person_Sex'] ?? "",
|
||||||
|
personUrl: json['Person_Url'] ?? "",
|
||||||
|
personQrCode: json['Person_QrCode'] ?? "",
|
||||||
|
m_sexname: json['m_sexname'] ?? "",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Model to JSON
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'Person_ID':personID,
|
||||||
|
'Person_NIK': personNIK,
|
||||||
|
'Person_Name': personName,
|
||||||
|
'Person_Dob': personDob,
|
||||||
|
'Person_Sex': personSex,
|
||||||
|
'Person_Url': personUrl,
|
||||||
|
'Person_QrCode': personQrCode,
|
||||||
|
'm_sexname':m_sexname,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
49
lib/model/no-login/no_login_person_ktp_model.dart
Normal file
49
lib/model/no-login/no_login_person_ktp_model.dart
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
class NoLoginPersonKtpModel {
|
||||||
|
final String personID;
|
||||||
|
final String personNIK;
|
||||||
|
final String personName;
|
||||||
|
final String personDob;
|
||||||
|
final String personSex;
|
||||||
|
final String personUrl;
|
||||||
|
final String personQrCode;
|
||||||
|
final String m_sexname;
|
||||||
|
|
||||||
|
NoLoginPersonKtpModel({
|
||||||
|
required this.personID,
|
||||||
|
required this.personNIK,
|
||||||
|
required this.personName,
|
||||||
|
required this.personDob,
|
||||||
|
required this.personSex,
|
||||||
|
required this.personUrl,
|
||||||
|
required this.personQrCode,
|
||||||
|
required this.m_sexname,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert JSON to Model
|
||||||
|
factory NoLoginPersonKtpModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return NoLoginPersonKtpModel(
|
||||||
|
personID: json['Person_ID'] ?? "",
|
||||||
|
personNIK: json['Person_NIK'] ?? "",
|
||||||
|
personName: json['Person_Name'] ?? "",
|
||||||
|
personDob: json['Person_Dob'] ?? DateTime.now(),
|
||||||
|
personSex: json['Person_Sex'] ?? "",
|
||||||
|
personUrl: json['Person_Url'] ?? "",
|
||||||
|
personQrCode: json['Person_QrCode'] ?? "",
|
||||||
|
m_sexname: json['m_sexname'] ?? "",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Model to JSON
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'Person_ID':personID,
|
||||||
|
'Person_NIK': personNIK,
|
||||||
|
'Person_Name': personName,
|
||||||
|
'Person_Dob': personDob,
|
||||||
|
'Person_Sex': personSex,
|
||||||
|
'Person_Url': personUrl,
|
||||||
|
'Person_QrCode': personQrCode,
|
||||||
|
'm_sexname':m_sexname,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
31
lib/model/no-login/no_login_sex_model.dart
Normal file
31
lib/model/no-login/no_login_sex_model.dart
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
class NoLoginSexModel {
|
||||||
|
final String M_SexID;
|
||||||
|
final String M_SexCode;
|
||||||
|
final String m_sexname;
|
||||||
|
final String M_SexNameLang;
|
||||||
|
|
||||||
|
NoLoginSexModel({
|
||||||
|
required this.M_SexID,
|
||||||
|
required this.M_SexCode,
|
||||||
|
required this.m_sexname,
|
||||||
|
required this.M_SexNameLang,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory NoLoginSexModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return NoLoginSexModel(
|
||||||
|
M_SexID: json['M_SexID'],
|
||||||
|
M_SexCode: json['M_SexCode'],
|
||||||
|
m_sexname: json['m_sexname'],
|
||||||
|
M_SexNameLang: json['M_SexNameLang'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'M_SexID': M_SexID,
|
||||||
|
'M_SexCode': M_SexCode,
|
||||||
|
'm_sexname': m_sexname,
|
||||||
|
'M_SexNameLang': M_SexNameLang,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
49
lib/model/no-login/no_login_sukses_person_model.dart
Normal file
49
lib/model/no-login/no_login_sukses_person_model.dart
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
class NoLoginSuksesPersonKtpModel {
|
||||||
|
final String personID;
|
||||||
|
final String personNIK;
|
||||||
|
final String personName;
|
||||||
|
final String personDob;
|
||||||
|
final String personSex;
|
||||||
|
final String personUrl;
|
||||||
|
final String personQrCode;
|
||||||
|
final String m_sexname;
|
||||||
|
|
||||||
|
NoLoginSuksesPersonKtpModel({
|
||||||
|
required this.personID,
|
||||||
|
required this.personNIK,
|
||||||
|
required this.personName,
|
||||||
|
required this.personDob,
|
||||||
|
required this.personSex,
|
||||||
|
required this.personUrl,
|
||||||
|
required this.personQrCode,
|
||||||
|
required this.m_sexname,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert JSON to Model
|
||||||
|
factory NoLoginSuksesPersonKtpModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return NoLoginSuksesPersonKtpModel(
|
||||||
|
personID: json['Person_ID'] ?? "",
|
||||||
|
personNIK: json['Person_NIK'] ?? "",
|
||||||
|
personName: json['Person_Name'] ?? "",
|
||||||
|
personDob: json['Person_Dob'] ?? DateTime.now(),
|
||||||
|
personSex: json['Person_Sex'] ?? "",
|
||||||
|
personUrl: json['Person_Url'] ?? "",
|
||||||
|
personQrCode: json['Person_QrCode'] ?? "",
|
||||||
|
m_sexname: json['m_sexname'] ?? "",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Model to JSON
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'Person_ID':personID,
|
||||||
|
'Person_NIK': personNIK,
|
||||||
|
'Person_Name': personName,
|
||||||
|
'Person_Dob': personDob,
|
||||||
|
'Person_Sex': personSex,
|
||||||
|
'Person_Url': personUrl,
|
||||||
|
'Person_QrCode': personQrCode,
|
||||||
|
'm_sexname':m_sexname,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
70
lib/provider/no-login-scan/no_login_scan_provider.dart
Normal file
70
lib/provider/no-login-scan/no_login_scan_provider.dart
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||||
|
import 'package:scanktpflutter/model/no-login/no_login_edit_person_ktp_model.dart';
|
||||||
|
import 'package:scanktpflutter/model/no-login/no_login_person_ktp_model.dart';
|
||||||
|
import 'package:scanktpflutter/model/no-login/no_login_sex_model.dart';
|
||||||
|
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
import '../../model/edit_person_model.dart';
|
||||||
|
|
||||||
|
// list scan
|
||||||
|
final listScanRwt = StateProvider<List<NoLoginPersonKtpModel>>(
|
||||||
|
(ref) => List.empty(
|
||||||
|
growable: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final selectedPersonIdx = StateProvider<String>((ref) => "0");
|
||||||
|
final selectedEdit = StateProvider<NoLoginEditPersonKtpModel>(
|
||||||
|
(ref) => NoLoginEditPersonKtpModel(
|
||||||
|
personID: "",
|
||||||
|
personNIK: "",
|
||||||
|
personName: "",
|
||||||
|
personDob: "",
|
||||||
|
personSex: "",
|
||||||
|
personUrl: "",
|
||||||
|
personQrCode: "",
|
||||||
|
m_sexname: "",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// inputan edit
|
||||||
|
final eQrCode = StateProvider<TextEditingController>(
|
||||||
|
(ref) => TextEditingController(text: ""),
|
||||||
|
);
|
||||||
|
|
||||||
|
final eNikCtr = StateProvider<TextEditingController>(
|
||||||
|
(ref) => TextEditingController(text: ""),
|
||||||
|
);
|
||||||
|
|
||||||
|
final eNamaCtr = StateProvider<TextEditingController>(
|
||||||
|
(ref) => TextEditingController(text: ""),
|
||||||
|
);
|
||||||
|
|
||||||
|
final eDobCtr = StateProvider<TextEditingController>(
|
||||||
|
(ref) => TextEditingController(text: ""),
|
||||||
|
);
|
||||||
|
|
||||||
|
final eDobDt = StateProvider<DateTime>(
|
||||||
|
(ref) => DateTime.now(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final eSexCtr = StateProvider<TextEditingController>(
|
||||||
|
(ref) => TextEditingController(text: ""),
|
||||||
|
);
|
||||||
|
|
||||||
|
final eSexSelected = StateProvider<NoLoginSexModel>(
|
||||||
|
(ref) => NoLoginSexModel(
|
||||||
|
M_SexID: "",
|
||||||
|
M_SexCode: "",
|
||||||
|
m_sexname: "",
|
||||||
|
M_SexNameLang: "",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final barcodeX = StateProvider<Barcode>(
|
||||||
|
(ref) => Barcode(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final noLoginprosesAksi = StateProvider<String>((ref) => Constant.getRiwayat);
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import '../../model/no-login/no_login_auth_model.dart';
|
||||||
|
|
||||||
|
final noLoginCurrentUserProvider = StateProvider<NoLoginAuthModel?>((ref) => null);
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||||
import 'package:scanktpflutter/model/sex_model.dart';
|
import 'package:scanktpflutter/model/sex_model.dart';
|
||||||
|
|
||||||
import '../model/edit_person_model.dart';
|
import '../../model/edit_person_model.dart';
|
||||||
import '../model/person_ktp_model.dart';
|
import '../../model/person_ktp_model.dart';
|
||||||
|
|
||||||
// list scan
|
// list scan
|
||||||
final listScanRwt = StateProvider<List<PersonKtp>>(
|
final listScanRwt = StateProvider<List<PersonKtp>>(
|
||||||
@@ -53,4 +54,4 @@ final eSexSelected = StateProvider<SexModel>(
|
|||||||
m_sexname: "",
|
m_sexname: "",
|
||||||
M_SexNameLang: "",
|
M_SexNameLang: "",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
118
lib/repository/no-login-scan/no_login_scan_repository.dart
Normal file
118
lib/repository/no-login-scan/no_login_scan_repository.dart
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import '../../model/no-login/no_login_person_ktp_model.dart';
|
||||||
|
import '../../model/no-login/no_login_sex_model.dart';
|
||||||
|
import '../../model/no-login/no_login_sukses_person_model.dart';
|
||||||
|
import '../base_repository.dart';
|
||||||
|
|
||||||
|
class NoLoginScanRepository extends BaseRepository {
|
||||||
|
NoLoginScanRepository({required super.dio});
|
||||||
|
|
||||||
|
Future<List<NoLoginPersonKtpModel>> listRiwayatScanRepo({
|
||||||
|
required String host,
|
||||||
|
required String client_id,
|
||||||
|
required String token,
|
||||||
|
}) async {
|
||||||
|
// final service = "${Constant.baseUrl}xauth/login";
|
||||||
|
final service =
|
||||||
|
"http://${host}/one-api/scan-ktpv2-no-login/Scanktpv2/listRiwayatScan";
|
||||||
|
final resp = await post(
|
||||||
|
param: {"client_id": client_id},
|
||||||
|
service: service,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = List<NoLoginPersonKtpModel>.empty(growable: true);
|
||||||
|
resp['data'].forEach((e) {
|
||||||
|
final model = NoLoginPersonKtpModel.fromJson(e);
|
||||||
|
result.add(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<NoLoginSuksesPersonKtpModel>> prosesScan({
|
||||||
|
required String host,
|
||||||
|
required String base64File,
|
||||||
|
required String client_id,
|
||||||
|
required String qr_code,
|
||||||
|
required String token,
|
||||||
|
}) async {
|
||||||
|
final service =
|
||||||
|
"http://${host}/one-api/scan-ktpv2-no-login/Scanktpv2/proses_scan";
|
||||||
|
final resp = await post(
|
||||||
|
param: {
|
||||||
|
"base64File": base64File,
|
||||||
|
"client_id": client_id,
|
||||||
|
"qr_code": qr_code,
|
||||||
|
},
|
||||||
|
service: service,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = List<NoLoginSuksesPersonKtpModel>.empty(growable: true);
|
||||||
|
resp['data'].forEach((e) {
|
||||||
|
final model = NoLoginSuksesPersonKtpModel.fromJson(e);
|
||||||
|
result.add(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sex
|
||||||
|
Future<List<NoLoginSexModel>> sexRepo({
|
||||||
|
required String host,
|
||||||
|
required String client_id,
|
||||||
|
required String token,
|
||||||
|
}) async {
|
||||||
|
// final service = "${Constant.baseUrl}xauth/login";
|
||||||
|
final service =
|
||||||
|
"http://${host}/one-api/scan-ktpv2-no-login/Scanktpv2/getSex";
|
||||||
|
final resp = await post(
|
||||||
|
param: {"client_id": client_id},
|
||||||
|
service: service,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = List<NoLoginSexModel>.empty(growable: true);
|
||||||
|
resp['data'].forEach((e) {
|
||||||
|
final model = NoLoginSexModel.fromJson(e);
|
||||||
|
result.add(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// edit
|
||||||
|
Future<String> prosesEdit({
|
||||||
|
required String token,
|
||||||
|
required String host,
|
||||||
|
required String client_id,
|
||||||
|
required String Person_ID,
|
||||||
|
required String Person_NIK,
|
||||||
|
required String Person_Name,
|
||||||
|
required String Person_Dob,
|
||||||
|
required String Person_Sex,
|
||||||
|
}) async {
|
||||||
|
final service =
|
||||||
|
"http://${host}/one-api/scan-ktpv2-no-login/Scanktpv2/proses_edit";
|
||||||
|
final resp = await post(
|
||||||
|
param: {
|
||||||
|
"Person_ID": Person_ID,
|
||||||
|
"Person_NIK": Person_NIK,
|
||||||
|
"Person_Name": Person_Name,
|
||||||
|
"Person_Dob": Person_Dob,
|
||||||
|
"Person_Sex": Person_Sex,
|
||||||
|
"client_id": client_id,
|
||||||
|
},
|
||||||
|
service: service,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resp['status'] == "OK") {
|
||||||
|
return "Sukses Update Data";
|
||||||
|
} else {
|
||||||
|
resp['message'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp['message'];
|
||||||
|
}
|
||||||
|
}
|
||||||
89
lib/repository/no-login/no_login_auth_repository.dart
Normal file
89
lib/repository/no-login/no_login_auth_repository.dart
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import '../../model/no-login/no_login_auth_model.dart';
|
||||||
|
|
||||||
|
import '../base_repository.dart';
|
||||||
|
|
||||||
|
class NoLoginAuthRepository extends BaseRepository {
|
||||||
|
NoLoginAuthRepository({required super.dio});
|
||||||
|
|
||||||
|
// loginF
|
||||||
|
Future<NoLoginAuthModel> loginF({
|
||||||
|
required String client_id,
|
||||||
|
required String host,
|
||||||
|
required String password,
|
||||||
|
}) async {
|
||||||
|
final param = {
|
||||||
|
"client_id": client_id,
|
||||||
|
"password": password,
|
||||||
|
"host": host,
|
||||||
|
};
|
||||||
|
final service = "http://$host/one-api/scan-ktpv2-no-login/Scanktpv2/loginF";
|
||||||
|
final resp = await post(param: param, service: service);
|
||||||
|
|
||||||
|
// if (resp['status'] != "OK") {
|
||||||
|
// return resp['message'];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return resp['status'];
|
||||||
|
|
||||||
|
final result = NoLoginAuthModel(
|
||||||
|
host: resp['data']['host'],
|
||||||
|
token: resp["data"]["token"],
|
||||||
|
client_id: resp['data']['client_id'],
|
||||||
|
expire_date: resp['data']['expire_date'],
|
||||||
|
isLogin: resp['data']['isLogin'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// createToken
|
||||||
|
Future<NoLoginAuthModel> createToken({
|
||||||
|
required String client_id,
|
||||||
|
required String host,
|
||||||
|
}) async {
|
||||||
|
final param = {
|
||||||
|
"client_id": client_id,
|
||||||
|
"host": host,
|
||||||
|
};
|
||||||
|
// final service = "${Constant.baseUrl}xauth/login";
|
||||||
|
final service =
|
||||||
|
"http://$host/one-api/scan-ktpv2-no-login/Scanktpv2/createToken";
|
||||||
|
final resp = await post(param: param, service: service);
|
||||||
|
|
||||||
|
final result = NoLoginAuthModel(
|
||||||
|
host: resp['data']['host'],
|
||||||
|
token: resp["data"]["token"],
|
||||||
|
client_id: resp['data']['client_id'],
|
||||||
|
expire_date: resp['data']['expire_date'],
|
||||||
|
isLogin: resp['data']['isLogin'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// refreshToken
|
||||||
|
Future<NoLoginAuthModel> refreshToken(
|
||||||
|
{required String client_id,
|
||||||
|
required String host,
|
||||||
|
required String expire_date}) async {
|
||||||
|
final param = {
|
||||||
|
"client_id": client_id,
|
||||||
|
"host": host,
|
||||||
|
"expire_date": expire_date,
|
||||||
|
};
|
||||||
|
// final service = "${Constant.baseUrl}xauth/login";
|
||||||
|
final service =
|
||||||
|
"http://$host/one-api/scan-ktpv2-no-login/Scanktpv2/refreshToken";
|
||||||
|
final resp = await post(param: param, service: service);
|
||||||
|
|
||||||
|
final result = NoLoginAuthModel(
|
||||||
|
host: resp['data']['host'],
|
||||||
|
token: resp["data"]["token"],
|
||||||
|
client_id: resp['data']['client_id'],
|
||||||
|
expire_date: resp['data']['expire_date'],
|
||||||
|
isLogin: resp['data']['isLogin'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import '../../provider/scan_provider.dart';
|
import 'package:scanktpflutter/provider/scan_provider.dart';
|
||||||
import '../../screen/home/card_riwayat_scan.dart';
|
import '../../screen/home/card_riwayat_scan.dart';
|
||||||
import '../../screen/home/list_riwayat_scan_provider.dart';
|
import '../../screen/home/list_riwayat_scan_provider.dart';
|
||||||
|
|
||||||
|
|||||||
184
lib/screen/no-login-home/no_login_card_riwayat_scan.dart
Normal file
184
lib/screen/no-login-home/no_login_card_riwayat_scan.dart
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import '../../model/no-login/no_login_edit_person_ktp_model.dart';
|
||||||
|
import '../../model/no-login/no_login_person_ktp_model.dart';
|
||||||
|
import '../../model/no-login/no_login_sex_model.dart';
|
||||||
|
import '../../app/app_extension.dart';
|
||||||
|
import '../../app/route.dart';
|
||||||
|
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
import '../../provider/no-login-scan/no_login_scan_provider.dart';
|
||||||
|
|
||||||
|
class NoLoginCardRiwayatScan extends HookConsumerWidget {
|
||||||
|
final NoLoginPersonKtpModel data;
|
||||||
|
const NoLoginCardRiwayatScan({
|
||||||
|
super.key,
|
||||||
|
required this.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [
|
||||||
|
SystemUiOverlay.bottom,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
FocusManager.instance.primaryFocus!.unfocus();
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
elevation: 2,
|
||||||
|
color: Constant.textWhite,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(
|
||||||
|
Constant.getActualYPhone(context: context, y: 12),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
data.personNIK,
|
||||||
|
style: Constant.titleInputan600(context: context).copyWith(
|
||||||
|
color: Constant.textBlack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
// print('id : ${data.personID}');
|
||||||
|
ref.read(selectedPersonIdx.notifier).state =
|
||||||
|
data.personID;
|
||||||
|
|
||||||
|
// set SEX
|
||||||
|
ref.read(eSexSelected.notifier).state = NoLoginSexModel(
|
||||||
|
M_SexID: data.personSex,
|
||||||
|
M_SexCode: "",
|
||||||
|
m_sexname: data.m_sexname,
|
||||||
|
M_SexNameLang: ""
|
||||||
|
);
|
||||||
|
|
||||||
|
ref.read(selectedEdit.notifier).state = NoLoginEditPersonKtpModel(
|
||||||
|
personID: data.personID,
|
||||||
|
personNIK: data.personNIK,
|
||||||
|
personName: data.personName,
|
||||||
|
m_sexname: data.m_sexname,
|
||||||
|
personDob: data.personDob,
|
||||||
|
personSex: data.personSex,
|
||||||
|
personUrl: data.personUrl,
|
||||||
|
personQrCode: data.personQrCode,
|
||||||
|
);
|
||||||
|
Navigator.of(context).pushNamed(
|
||||||
|
noLoginEditScanRoute,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.edit,
|
||||||
|
color: Constant.bgButton,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// row nama
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.person_2_outlined,
|
||||||
|
color: Constant.bgIcon,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
data.personName,
|
||||||
|
style: Constant.cardText(context: context).copyWith(
|
||||||
|
color: Constant.textCardGrey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// row nama
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// row dob
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.date_range_outlined,
|
||||||
|
color: Constant.bgIcon,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
formatDateJiffy(data.personDob),
|
||||||
|
style: Constant.cardText(context: context).copyWith(
|
||||||
|
color: Constant.textCardGrey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// row dob
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// row jenis kelamin
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'images/icongender.png',
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 20,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
data.m_sexname,
|
||||||
|
style: Constant.cardText(context: context).copyWith(
|
||||||
|
color: Constant.textCardGrey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// row jenis kelamin
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
357
lib/screen/no-login-home/no_login_home_screen.dart
Normal file
357
lib/screen/no-login-home/no_login_home_screen.dart
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
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/app_extension.dart';
|
||||||
|
import '../../screen/no-login-home/no_login_riwayat_scan_provider.dart';
|
||||||
|
// import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import '../../provider/no-login/no_login_current_user_provider.dart';
|
||||||
|
import '../../provider/no-login-scan/no_login_scan_provider.dart';
|
||||||
|
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
import '../../app/route.dart';
|
||||||
|
import '../../widget/customsnackbarwidget.dart';
|
||||||
|
import '../no-login/no_login_refresh_token_provider.dart';
|
||||||
|
import 'no_login_card_riwayat_scan.dart';
|
||||||
|
|
||||||
|
class NoLoginHomeScreen extends HookConsumerWidget {
|
||||||
|
const NoLoginHomeScreen({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 listScanArr = ref.watch(listScanRwt);
|
||||||
|
final client_id = currentUser?.client_id ?? "";
|
||||||
|
final token = currentUser?.token ?? "";
|
||||||
|
final expire_date = currentUser?.expire_date ?? "";
|
||||||
|
final readProsesAksi = ref.watch(noLoginprosesAksi);
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timestap) async {
|
||||||
|
ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan(
|
||||||
|
host: host,
|
||||||
|
client_id: client_id,
|
||||||
|
token: token,
|
||||||
|
expire_date: expire_date,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return () {};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
FocusManager.instance.primaryFocus!.unfocus();
|
||||||
|
},
|
||||||
|
child: Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
backgroundColor: Constant.bgGrey,
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
// atas
|
||||||
|
Image.asset(
|
||||||
|
'images/vektoratas.png',
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 34,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// button scan ktp
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 48,
|
||||||
|
),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pushNamed(noLoginScanRoute);
|
||||||
|
},
|
||||||
|
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: [
|
||||||
|
Image.asset(
|
||||||
|
'images/iconbarcode.png',
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 20,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'SCAN KTP BARU',
|
||||||
|
style:
|
||||||
|
Constant.titleButton500(context: context).copyWith(
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 56,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// judul
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// DEBUG
|
||||||
|
// Text(currentUser?.token ?? ""),
|
||||||
|
// SizedBox(
|
||||||
|
// height: Constant.getActualYPhone(
|
||||||
|
// context: context,
|
||||||
|
// y: 24,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Text(currentUser?.expire_date ?? ""),
|
||||||
|
// SizedBox(
|
||||||
|
// height: Constant.getActualYPhone(
|
||||||
|
// context: context,
|
||||||
|
// y: 24,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// DEBUG
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Riwayat',
|
||||||
|
style: Constant.titleRiwayat(context: context).copyWith(
|
||||||
|
color: Constant.textBlack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'20 Scan Terakhir',
|
||||||
|
style:
|
||||||
|
Constant.titleInputan500(context: context).copyWith(
|
||||||
|
color: Constant.inputanGrey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// loading
|
||||||
|
if (isLoading.value)
|
||||||
|
Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Constant.bgButton,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// belum ada riwayat
|
||||||
|
else if (listScanArr.isEmpty)
|
||||||
|
Expanded(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
color: Constant.bgButton,
|
||||||
|
onRefresh: () async {
|
||||||
|
getRiwayat();
|
||||||
|
},
|
||||||
|
child: ListView(
|
||||||
|
physics:
|
||||||
|
AlwaysScrollableScrollPhysics(), // Agar bisa di-refresh meski kosong
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height *
|
||||||
|
0.3), // Atur tinggi agar teks di tengah
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
'Belum Ada Riwayat',
|
||||||
|
style: Constant.titleInputan500(context: context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// list card
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
color: Constant.bgButton,
|
||||||
|
onRefresh: () async {
|
||||||
|
getRiwayat();
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(context: context, x: 13),
|
||||||
|
right: Constant.getActualXPhone(context: context, x: 13),
|
||||||
|
bottom: Constant.getActualYPhone(context: context, y: 20),
|
||||||
|
),
|
||||||
|
child: ListView.builder(
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: listScanArr.length,
|
||||||
|
itemBuilder: (context, i) {
|
||||||
|
final obj = listScanArr[i];
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: Constant.getActualYPhone(
|
||||||
|
context: context, y: 15),
|
||||||
|
),
|
||||||
|
child: NoLoginCardRiwayatScan(
|
||||||
|
data: obj,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
lib/screen/no-login-home/no_login_riwayat_scan_provider.dart
Normal file
88
lib/screen/no-login-home/no_login_riwayat_scan_provider.dart
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:scanktpflutter/model/no-login/no_login_person_ktp_model.dart';
|
||||||
|
import '../../repository/no-login-scan/no_login_scan_repository.dart';
|
||||||
|
import '../../provider/no-login-scan/no_login_scan_provider.dart';
|
||||||
|
|
||||||
|
import '../../provider/dio_provider.dart';
|
||||||
|
import '../../repository/base_repository.dart';
|
||||||
|
|
||||||
|
// 3. state provider
|
||||||
|
final noLoginRiwayatScanProvider =
|
||||||
|
StateNotifierProvider<NoLoginRiwayatScanNotifier, NoLoginRiwayatScanState>(
|
||||||
|
(ref) => NoLoginRiwayatScanNotifier(ref: ref));
|
||||||
|
|
||||||
|
// 2. notifier
|
||||||
|
class NoLoginRiwayatScanNotifier
|
||||||
|
extends StateNotifier<NoLoginRiwayatScanState> {
|
||||||
|
final Ref ref;
|
||||||
|
NoLoginRiwayatScanNotifier({required this.ref})
|
||||||
|
: super(NoLoginRiwayatScanStateInit());
|
||||||
|
void noLoginRiwayatScan({
|
||||||
|
required String host,
|
||||||
|
required String client_id,
|
||||||
|
required String token,
|
||||||
|
required String expire_date,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
state = NoLoginRiwayatScanStateLoading();
|
||||||
|
final resp = await NoLoginScanRepository(
|
||||||
|
dio: ref.read(dioProvider),
|
||||||
|
).listRiwayatScanRepo(
|
||||||
|
host: host,
|
||||||
|
client_id: client_id,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
|
||||||
|
// print(resp);
|
||||||
|
state = NoLoginRiwayatScanStateDone(model: resp);
|
||||||
|
ref.read(listScanRwt.notifier).state = resp;
|
||||||
|
|
||||||
|
// // fungsi check token expired
|
||||||
|
// if (await isTokenExpired()) {
|
||||||
|
// print("Token expired, refreshing...");
|
||||||
|
// ref.read(noLoginRefreshTokenProvider.notifier).refreshToken(
|
||||||
|
// client_id: userId,
|
||||||
|
// host: host,
|
||||||
|
// expire_date:expire_date,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
} catch (e) {
|
||||||
|
if (e is BaseRepositoryException) {
|
||||||
|
state = NoLoginRiwayatScanStateError(message: e.message);
|
||||||
|
} else {
|
||||||
|
state = NoLoginRiwayatScanStateError(message: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. state
|
||||||
|
abstract class NoLoginRiwayatScanState extends Equatable {
|
||||||
|
final DateTime date;
|
||||||
|
const NoLoginRiwayatScanState(this.date);
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [date];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginRiwayatScanStateInit extends NoLoginRiwayatScanState {
|
||||||
|
NoLoginRiwayatScanStateInit() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginRiwayatScanStateLoading extends NoLoginRiwayatScanState {
|
||||||
|
NoLoginRiwayatScanStateLoading() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginRiwayatScanStateError extends NoLoginRiwayatScanState {
|
||||||
|
final String message;
|
||||||
|
NoLoginRiwayatScanStateError({
|
||||||
|
required this.message,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginRiwayatScanStateDone extends NoLoginRiwayatScanState {
|
||||||
|
final List<NoLoginPersonKtpModel> model;
|
||||||
|
NoLoginRiwayatScanStateDone({
|
||||||
|
required this.model,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
81
lib/screen/no-login-scan/no_login_edit_scan_provider.dart
Normal file
81
lib/screen/no-login-scan/no_login_edit_scan_provider.dart
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:scanktpflutter/repository/no-login-scan/no_login_scan_repository.dart';
|
||||||
|
|
||||||
|
import '../../provider/dio_provider.dart';
|
||||||
|
import '../../repository/base_repository.dart';
|
||||||
|
|
||||||
|
// 3. state provider
|
||||||
|
final noLoginEditScanProvider = StateNotifierProvider<NoLoginEditScanNotifier, NoLoginEditScanState>(
|
||||||
|
(ref) => NoLoginEditScanNotifier(ref: ref));
|
||||||
|
|
||||||
|
// 2. notifier
|
||||||
|
class NoLoginEditScanNotifier extends StateNotifier<NoLoginEditScanState> {
|
||||||
|
final Ref ref;
|
||||||
|
NoLoginEditScanNotifier({required this.ref}) : super(NoLoginEditScanStateInit());
|
||||||
|
void editScan({
|
||||||
|
required String token,
|
||||||
|
required String host,
|
||||||
|
required String client_id,
|
||||||
|
required String Person_ID,
|
||||||
|
required String Person_NIK,
|
||||||
|
required String Person_Name,
|
||||||
|
required String Person_Dob,
|
||||||
|
required String Person_Sex,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
state = NoLoginEditScanStateLoading();
|
||||||
|
final resp = await NoLoginScanRepository(
|
||||||
|
dio: ref.read(dioProvider),
|
||||||
|
).prosesEdit(
|
||||||
|
token: token,
|
||||||
|
host: host,
|
||||||
|
Person_ID: Person_ID,
|
||||||
|
Person_NIK: Person_NIK,
|
||||||
|
Person_Name: Person_Name,
|
||||||
|
Person_Dob: Person_Dob,
|
||||||
|
Person_Sex: Person_Sex,
|
||||||
|
client_id: client_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// print(resp);
|
||||||
|
state = NoLoginEditScanStateDone(pesan: resp);
|
||||||
|
} catch (e) {
|
||||||
|
if (e is BaseRepositoryException) {
|
||||||
|
state = NoLoginEditScanStateError(message: e.message);
|
||||||
|
} else {
|
||||||
|
state = NoLoginEditScanStateError(message: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. state
|
||||||
|
abstract class NoLoginEditScanState extends Equatable {
|
||||||
|
final DateTime date;
|
||||||
|
const NoLoginEditScanState(this.date);
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [date];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginEditScanStateInit extends NoLoginEditScanState {
|
||||||
|
NoLoginEditScanStateInit() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginEditScanStateLoading extends NoLoginEditScanState {
|
||||||
|
NoLoginEditScanStateLoading() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginEditScanStateError extends NoLoginEditScanState {
|
||||||
|
final String message;
|
||||||
|
NoLoginEditScanStateError({
|
||||||
|
required this.message,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginEditScanStateDone extends NoLoginEditScanState {
|
||||||
|
final String pesan;
|
||||||
|
NoLoginEditScanStateDone({
|
||||||
|
required this.pesan,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
715
lib/screen/no-login-scan/no_login_edit_scan_screen.dart
Normal file
715
lib/screen/no-login-scan/no_login_edit_scan_screen.dart
Normal file
@@ -0,0 +1,715 @@
|
|||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
717
lib/screen/no-login-scan/no_login_scan_screen.dart
Normal file
717
lib/screen/no-login-scan/no_login_scan_screen.dart
Normal file
@@ -0,0 +1,717 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:camera/camera.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:image/image.dart' as img;
|
||||||
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||||
|
import 'package:scanktpflutter/model/no-login/no_login_edit_person_ktp_model.dart';
|
||||||
|
import 'package:scanktpflutter/model/no-login/no_login_sex_model.dart';
|
||||||
|
import '../../app/app_extension.dart';
|
||||||
|
import '../../app/route.dart';
|
||||||
|
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
import '../../provider/no-login-scan/no_login_scan_provider.dart';
|
||||||
|
import '../../provider/no-login/no_login_current_user_provider.dart';
|
||||||
|
import '../../widget/customsnackbarwidget.dart';
|
||||||
|
import '../no-login-home/no_login_riwayat_scan_provider.dart';
|
||||||
|
import '../no-login/no_login_refresh_token_provider.dart';
|
||||||
|
import 'no_login_upload_scan_provider.dart';
|
||||||
|
|
||||||
|
class NoLoginScanScreen extends HookConsumerWidget {
|
||||||
|
const NoLoginScanScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [
|
||||||
|
SystemUiOverlay.bottom,
|
||||||
|
]);
|
||||||
|
|
||||||
|
final judulAppBar = useState<String>("Scan QRCode");
|
||||||
|
final cameraOn = useState<bool>(true);
|
||||||
|
final qrCodeStr = useState<String>("Scan QRCode untuk dapat merekam");
|
||||||
|
final judulTombol = useState<String>("SCAN QR CODE");
|
||||||
|
final awalan = useState("Info : ");
|
||||||
|
final judulPosisiHp = useState<String>("Posisi HP Landscape");
|
||||||
|
// final scannerCtr = useState<MobileScanner?>(null);
|
||||||
|
final cameraOrScanner = useState<String>("SCAN");
|
||||||
|
final loadingScreen = useState<bool>(true);
|
||||||
|
|
||||||
|
final cameraController = useState<CameraController?>(null);
|
||||||
|
final initializeControllerFuture = useState<Future<void>?>(null);
|
||||||
|
final capturedImage = useState<XFile?>(null);
|
||||||
|
final croppedImage = useState<File?>(null);
|
||||||
|
final isLoading = useState<bool>(false);
|
||||||
|
final isLoadingUpload = useState<bool>(false);
|
||||||
|
|
||||||
|
// auth
|
||||||
|
final currentUser = ref.watch(noLoginCurrentUserProvider);
|
||||||
|
final host = currentUser?.host ?? "";
|
||||||
|
final client_id = currentUser?.client_id ?? "";
|
||||||
|
final expire_date = currentUser?.expire_date ?? "";
|
||||||
|
final token = currentUser?.token ?? "";
|
||||||
|
final selectedResolution =
|
||||||
|
useState<ResolutionPreset>(ResolutionPreset.medium);
|
||||||
|
final readProsesAksi = ref.watch(noLoginprosesAksi);
|
||||||
|
final base64Post = useState("");
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
Future<void> initializeCamera() async {
|
||||||
|
try {
|
||||||
|
final cameras = await availableCameras();
|
||||||
|
cameraController.value = CameraController(
|
||||||
|
cameras[0],
|
||||||
|
selectedResolution.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
initializeControllerFuture.value = cameraController.value!.initialize();
|
||||||
|
await initializeControllerFuture.value;
|
||||||
|
|
||||||
|
if (cameraController.value!.value.isInitialized) {
|
||||||
|
await cameraController.value!.setFlashMode(FlashMode.off);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error initializing camera: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
Future<void> initialize() async {
|
||||||
|
// delay 3 second e
|
||||||
|
await Future.delayed(
|
||||||
|
Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
await initializeCamera();
|
||||||
|
loadingScreen.value = false; // Hide loading screen untuk inisialisasi
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
return null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loadingScreen.value) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: (judulTombol.value == "SCAN")
|
||||||
|
? Text(
|
||||||
|
judulPosisiHp.value,
|
||||||
|
style: Constant.titlePosisiHP(context: context),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
judulAppBar.value,
|
||||||
|
style: Constant.titlePosisiHP(context: context),
|
||||||
|
),
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.black.withOpacity(0.5),
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
"Menunggu Camera Siap",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomNavigationBar: BottomAppBar(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 100,
|
||||||
|
),
|
||||||
|
shape: const CircularNotchedRectangle(),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
capturedImage.value = null;
|
||||||
|
croppedImage.value = null;
|
||||||
|
|
||||||
|
ref.read(barcodeX.notifier).state = Barcode();
|
||||||
|
judulTombol.value = "SCAN QR CODE";
|
||||||
|
qrCodeStr.value = "Scan QRCode untuk dapat merekam";
|
||||||
|
cameraOn.value = false;
|
||||||
|
awalan.value = "Info :";
|
||||||
|
cameraOrScanner.value = "SCAN";
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_back,
|
||||||
|
size: 17,
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
'Kembali',
|
||||||
|
style: Constant.cardText(context: context).copyWith(
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Constant.textCardGrey,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
elevation: 8,
|
||||||
|
shadowColor: Constant.bgButton.withOpacity(0.24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleBarcode(BarcodeCapture barcodes) async {
|
||||||
|
if (barcodes.barcodes.isNotEmpty) {
|
||||||
|
final scannedBarcode =
|
||||||
|
barcodes.barcodes.firstOrNull?.displayValue ?? "";
|
||||||
|
if (scannedBarcode.isNotEmpty) {
|
||||||
|
await initializeCamera();
|
||||||
|
ref.read(barcodeX.notifier).state = barcodes.barcodes.first;
|
||||||
|
qrCodeStr.value = scannedBarcode;
|
||||||
|
awalan.value = "QrCode : ";
|
||||||
|
cameraOn.value = true;
|
||||||
|
judulTombol.value = "FOTO";
|
||||||
|
cameraOrScanner.value = "CAMERA";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
Future<void> initializeCamera() async {
|
||||||
|
try {
|
||||||
|
final cameras = await availableCameras();
|
||||||
|
cameraController.value = CameraController(
|
||||||
|
cameras[0],
|
||||||
|
selectedResolution.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
initializeControllerFuture.value =
|
||||||
|
cameraController.value!.initialize();
|
||||||
|
await initializeControllerFuture.value;
|
||||||
|
|
||||||
|
if (cameraController.value!.value.isInitialized) {
|
||||||
|
await cameraController.value!.setFlashMode(FlashMode.off);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error initializing camera: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeCamera();
|
||||||
|
|
||||||
|
return () {};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// void prosesUpload
|
||||||
|
void prosesUpload() async {
|
||||||
|
print("Token expired, refreshing...");
|
||||||
|
print("client_id : $client_id");
|
||||||
|
print("host : $host");
|
||||||
|
print("expire_date : $expire_date");
|
||||||
|
print("token : $token");
|
||||||
|
print("qrCodeStr.value : ${qrCodeStr.value}");
|
||||||
|
|
||||||
|
if (await isTokenExpired() == true) {
|
||||||
|
ref.read(noLoginprosesAksi.notifier).state = Constant.postUploadFoto;
|
||||||
|
print("Token expired, refreshing...");
|
||||||
|
ref.read(noLoginRefreshTokenProvider.notifier).refreshToken(
|
||||||
|
client_id: client_id,
|
||||||
|
host: host,
|
||||||
|
expire_date: expire_date,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ref.read(noLoginUploadScanProvider.notifier).uploadScan(
|
||||||
|
host: host,
|
||||||
|
base64File: base64Post.value,
|
||||||
|
client_id: client_id,
|
||||||
|
qr_code: qrCodeStr.value,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initializeCameraAfterChangeResolution() async {
|
||||||
|
final cameras = await availableCameras();
|
||||||
|
cameraController.value = CameraController(
|
||||||
|
cameras[0],
|
||||||
|
// ResolutionPreset.max,
|
||||||
|
// ResolutionPreset.medium,
|
||||||
|
selectedResolution.value);
|
||||||
|
initializeControllerFuture.value = cameraController.value!.initialize();
|
||||||
|
await initializeControllerFuture.value;
|
||||||
|
await cameraController.value!.setFlashMode(FlashMode.off);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<File> rotateAndCropImage(File imageFile) async {
|
||||||
|
Uint8List imageBytes = await imageFile.readAsBytes();
|
||||||
|
img.Image? original = img.decodeImage(imageBytes);
|
||||||
|
if (original == null) return imageFile;
|
||||||
|
|
||||||
|
img.Image rotated = img.bakeOrientation(original);
|
||||||
|
|
||||||
|
int width = rotated.width;
|
||||||
|
int height = rotated.height;
|
||||||
|
bool isPortrait = height > width;
|
||||||
|
|
||||||
|
int cropWidth, cropHeight;
|
||||||
|
|
||||||
|
if (isPortrait) {
|
||||||
|
cropHeight = (height * 0.7).toInt();
|
||||||
|
cropWidth = (cropHeight ~/ 1.59).toInt();
|
||||||
|
} else {
|
||||||
|
cropWidth = (width * 0.7).toInt();
|
||||||
|
cropHeight = (cropWidth ~/ 1.59).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
int left = ((width - cropWidth) ~/ 2).toInt();
|
||||||
|
int top = ((height - cropHeight) ~/ 2).toInt();
|
||||||
|
|
||||||
|
img.Image cropped = img.copyCrop(rotated,
|
||||||
|
x: left, y: top, width: cropWidth, height: cropHeight);
|
||||||
|
|
||||||
|
File croppedFile = File('${imageFile.path}_cropped.jpg');
|
||||||
|
await croppedFile.writeAsBytes(img.encodeJpg(cropped));
|
||||||
|
|
||||||
|
return croppedFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<File> rotateImage(File imageFile) async {
|
||||||
|
Uint8List bytes = await imageFile.readAsBytes();
|
||||||
|
img.Image? image = img.decodeImage(bytes);
|
||||||
|
|
||||||
|
if (image == null) return imageFile;
|
||||||
|
|
||||||
|
img.Image rotated = img.copyRotate(image, angle: -90);
|
||||||
|
|
||||||
|
File rotatedFile = File('${imageFile.path}_rotated.jpg');
|
||||||
|
await rotatedFile.writeAsBytes(img.encodeJpg(rotated));
|
||||||
|
|
||||||
|
return rotatedFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> captureAndCropImage() async {
|
||||||
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
|
await initializeControllerFuture.value;
|
||||||
|
|
||||||
|
// Ambil gambar dari kamera
|
||||||
|
final image = await cameraController.value!.takePicture();
|
||||||
|
File cropped = await rotateAndCropImage(File(image.path));
|
||||||
|
|
||||||
|
// Rotate gambar setelah cropping
|
||||||
|
File rotatedImage = await rotateImage(cropped);
|
||||||
|
|
||||||
|
// Convert to grayscale
|
||||||
|
Uint8List bytes = await rotatedImage.readAsBytes();
|
||||||
|
img.Image? coloredImage = img.decodeImage(bytes);
|
||||||
|
if (coloredImage != null) {
|
||||||
|
img.Image grayscaleImage = img.grayscale(coloredImage);
|
||||||
|
rotatedImage.writeAsBytesSync(img.encodeJpg(grayscaleImage));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simpan hasil yang sudah di-crop dan di-rotate
|
||||||
|
capturedImage.value = image;
|
||||||
|
croppedImage.value = rotatedImage;
|
||||||
|
|
||||||
|
// post ke BE
|
||||||
|
Uint8List finalBytes = await croppedImage.value!.readAsBytes();
|
||||||
|
String base64String = base64Encode(finalBytes);
|
||||||
|
base64Post.value = base64String;
|
||||||
|
prosesUpload();
|
||||||
|
} catch (e) {
|
||||||
|
print("Error capturing image: $e");
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.postUploadFoto) {
|
||||||
|
ref.read(noLoginUploadScanProvider.notifier).uploadScan(
|
||||||
|
host: host,
|
||||||
|
base64File: base64Post.value,
|
||||||
|
client_id: client_id,
|
||||||
|
qr_code: qrCodeStr.value,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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).pushNamed(
|
||||||
|
noLoginEditScanRoute,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// proses upload
|
||||||
|
ref.listen(noLoginUploadScanProvider, (prev, next) {
|
||||||
|
if (next is NoLoginUploadScanStateLoading) {
|
||||||
|
isLoadingUpload.value = true;
|
||||||
|
} else if (next is NoLoginUploadScanStateError) {
|
||||||
|
isLoadingUpload.value = false;
|
||||||
|
// errorMessage.value = next.message;
|
||||||
|
print("Err : ${next.message}");
|
||||||
|
snackbarWidget(
|
||||||
|
context,
|
||||||
|
next.message,
|
||||||
|
snackbarType.error,
|
||||||
|
Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
} else if (next is NoLoginUploadScanStateDone) {
|
||||||
|
isLoadingUpload.value = false;
|
||||||
|
ref.read(selectedPersonIdx.notifier).state = next.model[0].personID;
|
||||||
|
|
||||||
|
// set SEX
|
||||||
|
ref.read(eSexSelected.notifier).state = NoLoginSexModel(
|
||||||
|
M_SexID: next.model[0].personSex,
|
||||||
|
M_SexCode: "",
|
||||||
|
m_sexname: next.model[0].m_sexname,
|
||||||
|
M_SexNameLang: "");
|
||||||
|
|
||||||
|
ref.read(selectedEdit.notifier).state = NoLoginEditPersonKtpModel(
|
||||||
|
personID: next.model[0].personID,
|
||||||
|
personNIK: next.model[0].personNIK,
|
||||||
|
personName: next.model[0].personName,
|
||||||
|
m_sexname: next.model[0].m_sexname,
|
||||||
|
personDob: next.model[0].personDob,
|
||||||
|
personSex: next.model[0].personSex,
|
||||||
|
personUrl: next.model[0].personUrl,
|
||||||
|
personQrCode: next.model[0].personQrCode,
|
||||||
|
);
|
||||||
|
getRiwayat();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// loading proses upload useEffect
|
||||||
|
useEffect(() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timestamp) {
|
||||||
|
if (isLoadingUpload.value == true) {
|
||||||
|
snackbarWidget(
|
||||||
|
context,
|
||||||
|
'Sedang Upload Foto...',
|
||||||
|
snackbarType.warning,
|
||||||
|
Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () {};
|
||||||
|
}, [isLoadingUpload.value]);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: (cameraOrScanner.value == "SCAN")
|
||||||
|
? Text(
|
||||||
|
judulAppBar.value,
|
||||||
|
style: Constant.titlePosisiHP(context: context),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
judulPosisiHp.value,
|
||||||
|
style: Constant.titlePosisiHP(context: context),
|
||||||
|
),
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
actions: [
|
||||||
|
if (cameraOn.value == true)
|
||||||
|
DropdownButton<ResolutionPreset>(
|
||||||
|
value: selectedResolution.value,
|
||||||
|
onChanged: (ResolutionPreset? newValue) {
|
||||||
|
if (newValue != null) {
|
||||||
|
selectedResolution.value = newValue;
|
||||||
|
initializeCameraAfterChangeResolution();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
items: ResolutionPreset.values.map((ResolutionPreset value) {
|
||||||
|
return DropdownMenuItem<ResolutionPreset>(
|
||||||
|
value: value,
|
||||||
|
child: Text(value.toString().split('.').last),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
underline: SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.black.withOpacity(0.5),
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
// jika camera scan ktp true show
|
||||||
|
if (cameraOrScanner.value == "CAMERA") ...[
|
||||||
|
if (cameraController.value != null && croppedImage.value == null)
|
||||||
|
FutureBuilder<void>(
|
||||||
|
future: initializeControllerFuture.value,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
return CameraPreview(cameraController.value!);
|
||||||
|
} else {
|
||||||
|
return Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
// jika sudah ada foto
|
||||||
|
if (croppedImage.value != null) ...[
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 300,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Image.file(
|
||||||
|
croppedImage.value!,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
// bingkai foto
|
||||||
|
Positioned.fill(
|
||||||
|
top: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 40,
|
||||||
|
),
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: OverlayPainter(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
if (cameraOrScanner.value == "SCAN") ...[
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
child: MobileScanner(
|
||||||
|
// controller: scannerCtr.value?.controller,
|
||||||
|
onDetect: handleBarcode,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|
||||||
|
// Info
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 100,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 10),
|
||||||
|
color: Constant.inputanGrey,
|
||||||
|
child: Text(
|
||||||
|
"${awalan.value} ${qrCodeStr.value}",
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// loading
|
||||||
|
if (isLoading.value)
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
color: Colors.black.withOpacity(0.5),
|
||||||
|
child: const Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
bottomNavigationBar: BottomAppBar(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 100,
|
||||||
|
),
|
||||||
|
shape: const CircularNotchedRectangle(),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
capturedImage.value = null;
|
||||||
|
croppedImage.value = null;
|
||||||
|
|
||||||
|
ref.read(barcodeX.notifier).state = Barcode();
|
||||||
|
judulTombol.value = "SCAN QR CODE";
|
||||||
|
qrCodeStr.value = "Scan QRCode untuk dapat merekam";
|
||||||
|
cameraOn.value = false;
|
||||||
|
awalan.value = "Info :";
|
||||||
|
cameraOrScanner.value = "SCAN";
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_back,
|
||||||
|
size: 17,
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
'Kembali',
|
||||||
|
style: Constant.cardText(context: context).copyWith(
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Constant.textCardGrey,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
elevation: 8,
|
||||||
|
shadowColor: Constant.bgButton.withOpacity(0.24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
// scan
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: captureAndCropImage,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.camera,
|
||||||
|
size: 17,
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
judulTombol.value,
|
||||||
|
style: Constant.cardText(context: context).copyWith(
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Constant.bgButton,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
elevation: 8,
|
||||||
|
shadowColor: Constant.bgButton.withOpacity(0.24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OverlayPainter extends CustomPainter {
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final paint = Paint()
|
||||||
|
..color = Colors.black.withOpacity(0.6)
|
||||||
|
..style = PaintingStyle.fill;
|
||||||
|
|
||||||
|
final borderPaint = Paint()
|
||||||
|
..color = Colors.yellow
|
||||||
|
..strokeWidth = 4
|
||||||
|
..style = PaintingStyle.stroke;
|
||||||
|
|
||||||
|
double boxHeight = size.height * 0.7;
|
||||||
|
double boxWidth = boxHeight / 1.59;
|
||||||
|
|
||||||
|
double left = (size.width - boxWidth) / 2;
|
||||||
|
double top = (size.height - boxHeight) / 2;
|
||||||
|
|
||||||
|
Path path = Path()
|
||||||
|
..addRect(Rect.fromLTWH(0, 0, size.width, size.height))
|
||||||
|
..addRect(Rect.fromLTWH(left, top, boxWidth, boxHeight))
|
||||||
|
..fillType = PathFillType.evenOdd;
|
||||||
|
|
||||||
|
canvas.drawPath(path, paint);
|
||||||
|
canvas.drawRect(Rect.fromLTWH(left, top, boxWidth, boxHeight), borderPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(CustomPainter oldDelegate) => false;
|
||||||
|
}
|
||||||
73
lib/screen/no-login-scan/no_login_sex_provider.dart
Normal file
73
lib/screen/no-login-scan/no_login_sex_provider.dart
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:scanktpflutter/model/no-login/no_login_sex_model.dart';
|
||||||
|
import 'package:scanktpflutter/repository/no-login-scan/no_login_scan_repository.dart';
|
||||||
|
|
||||||
|
import '../../provider/dio_provider.dart';
|
||||||
|
import '../../repository/base_repository.dart';
|
||||||
|
|
||||||
|
// 3. state provider
|
||||||
|
final noLoginSexProvider =
|
||||||
|
StateNotifierProvider<NoLoginSexNotifier, NoLoginSexState>(
|
||||||
|
(ref) => NoLoginSexNotifier(ref: ref));
|
||||||
|
|
||||||
|
// 2. notifier
|
||||||
|
class NoLoginSexNotifier extends StateNotifier<NoLoginSexState> {
|
||||||
|
final Ref ref;
|
||||||
|
NoLoginSexNotifier({required this.ref}) : super(NoLoginSexStateInit());
|
||||||
|
void sex({
|
||||||
|
required String host,
|
||||||
|
required String client_id,
|
||||||
|
required String token,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
state = NoLoginSexStateLoading();
|
||||||
|
final resp = await NoLoginScanRepository(
|
||||||
|
dio: ref.read(dioProvider),
|
||||||
|
).sexRepo(
|
||||||
|
host: host,
|
||||||
|
client_id: client_id,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
|
||||||
|
// print(resp);
|
||||||
|
state = NoLoginSexStateDone(model: resp);
|
||||||
|
} catch (e) {
|
||||||
|
if (e is BaseRepositoryException) {
|
||||||
|
state = NoLoginSexStateError(message: e.message);
|
||||||
|
} else {
|
||||||
|
state = NoLoginSexStateError(message: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. state
|
||||||
|
abstract class NoLoginSexState extends Equatable {
|
||||||
|
final DateTime date;
|
||||||
|
const NoLoginSexState(this.date);
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [date];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginSexStateInit extends NoLoginSexState {
|
||||||
|
NoLoginSexStateInit() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginSexStateLoading extends NoLoginSexState {
|
||||||
|
NoLoginSexStateLoading() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginSexStateError extends NoLoginSexState {
|
||||||
|
final String message;
|
||||||
|
NoLoginSexStateError({
|
||||||
|
required this.message,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginSexStateDone extends NoLoginSexState {
|
||||||
|
final List<NoLoginSexModel> model;
|
||||||
|
NoLoginSexStateDone({
|
||||||
|
required this.model,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
78
lib/screen/no-login-scan/no_login_upload_scan_provider.dart
Normal file
78
lib/screen/no-login-scan/no_login_upload_scan_provider.dart
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import '../../model/no-login/no_login_sukses_person_model.dart';
|
||||||
|
import '../../repository/no-login-scan/no_login_scan_repository.dart';
|
||||||
|
|
||||||
|
import '../../provider/dio_provider.dart';
|
||||||
|
import '../../repository/base_repository.dart';
|
||||||
|
|
||||||
|
// 3. state provider
|
||||||
|
final noLoginUploadScanProvider =
|
||||||
|
StateNotifierProvider<NoLoginUploadScanNotifier, NoLoginUploadScanState>(
|
||||||
|
(ref) => NoLoginUploadScanNotifier(ref: ref));
|
||||||
|
|
||||||
|
// 2. notifier
|
||||||
|
class NoLoginUploadScanNotifier extends StateNotifier<NoLoginUploadScanState> {
|
||||||
|
final Ref ref;
|
||||||
|
NoLoginUploadScanNotifier({required this.ref})
|
||||||
|
: super(NoLoginUploadScanStateInit());
|
||||||
|
void uploadScan({
|
||||||
|
required String host,
|
||||||
|
required String client_id,
|
||||||
|
required String base64File,
|
||||||
|
required String qr_code,
|
||||||
|
required String token,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
state = NoLoginUploadScanStateLoading();
|
||||||
|
final resp = await NoLoginScanRepository(
|
||||||
|
dio: ref.read(dioProvider),
|
||||||
|
).prosesScan(
|
||||||
|
host: host,
|
||||||
|
base64File: base64File,
|
||||||
|
client_id: client_id,
|
||||||
|
qr_code: qr_code,
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
|
||||||
|
// print(resp);
|
||||||
|
state = NoLoginUploadScanStateDone(model: resp);
|
||||||
|
} catch (e) {
|
||||||
|
if (e is BaseRepositoryException) {
|
||||||
|
state = NoLoginUploadScanStateError(message: e.message);
|
||||||
|
} else {
|
||||||
|
state = NoLoginUploadScanStateError(message: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. state
|
||||||
|
abstract class NoLoginUploadScanState extends Equatable {
|
||||||
|
final DateTime date;
|
||||||
|
const NoLoginUploadScanState(this.date);
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [date];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginUploadScanStateInit extends NoLoginUploadScanState {
|
||||||
|
NoLoginUploadScanStateInit() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginUploadScanStateLoading extends NoLoginUploadScanState {
|
||||||
|
NoLoginUploadScanStateLoading() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginUploadScanStateError extends NoLoginUploadScanState {
|
||||||
|
final String message;
|
||||||
|
NoLoginUploadScanStateError({
|
||||||
|
required this.message,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginUploadScanStateDone extends NoLoginUploadScanState {
|
||||||
|
final List<NoLoginSuksesPersonKtpModel> model;
|
||||||
|
NoLoginUploadScanStateDone({
|
||||||
|
required this.model,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
101
lib/screen/no-login-splash/no_login_splash_screen.dart
Normal file
101
lib/screen/no-login-splash/no_login_splash_screen.dart
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
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 '../../model/no-login/no_login_auth_model.dart';
|
||||||
|
import '../../provider/no-login/no_login_current_user_provider.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
import '../../app/route.dart';
|
||||||
|
|
||||||
|
class NoLoginSplashScreen extends HookConsumerWidget {
|
||||||
|
const NoLoginSplashScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [
|
||||||
|
SystemUiOverlay.bottom,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// useEffect(() {
|
||||||
|
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
|
// final shared = await SharedPreferences.getInstance();
|
||||||
|
// final bearerString = shared.getString(Constant.bearerName);
|
||||||
|
|
||||||
|
// if (bearerString == null || bearerString == "null") {
|
||||||
|
// Timer(const Duration(seconds: 3), () {
|
||||||
|
// Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
|
// noLoginRoute,
|
||||||
|
// (route) => false,
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// final xmodel = jsonDecode(bearerString);
|
||||||
|
// if (xmodel == null) return;
|
||||||
|
|
||||||
|
// final authModel = NoLoginAuthModel(
|
||||||
|
// host: xmodel["host"],
|
||||||
|
// token: xmodel["token"],
|
||||||
|
// client_id: xmodel['client_id'],
|
||||||
|
// expire_date: xmodel['expire_date'],
|
||||||
|
// isLogin: xmodel['isLogin'],
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ref.read(noLoginCurrentUserProvider.notifier).state = authModel;
|
||||||
|
|
||||||
|
// Timer(const Duration(seconds: 2), () {
|
||||||
|
// Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
|
// noLoginHomeRoute,
|
||||||
|
// (route) => false,
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// return () {};
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Constant.textWhite,
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
// Bagian atas
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topRight,
|
||||||
|
child: SizedBox(
|
||||||
|
width: Constant.getActualXPhone(context: context, x: 246),
|
||||||
|
child: Image.asset(
|
||||||
|
'images/splashatas.png',
|
||||||
|
fit: BoxFit.fitWidth,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
// Logo di tengah
|
||||||
|
Center(
|
||||||
|
child: Image.asset(
|
||||||
|
'images/logo.png',
|
||||||
|
width: Constant.getActualXPhone(context: context, x: 164),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
// Bagian bawah
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomLeft,
|
||||||
|
child: SizedBox(
|
||||||
|
width: Constant.getActualXPhone(context: context, x: 246),
|
||||||
|
child: Image.asset(
|
||||||
|
'images/splashbawah.png',
|
||||||
|
fit: BoxFit.fitWidth,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
lib/screen/no-login/no_login_provider.dart
Normal file
79
lib/screen/no-login/no_login_provider.dart
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:scanktpflutter/model/no-login/no_login_auth_model.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
import '../../provider/no-login/no_login_current_user_provider.dart';
|
||||||
|
import '../../repository/no-login/no_login_auth_repository.dart';
|
||||||
|
import '../../provider/dio_provider.dart';
|
||||||
|
|
||||||
|
// 3. state provider
|
||||||
|
final noLoginProvider = StateNotifierProvider<NoLoginNotifier, NoLoginState>(
|
||||||
|
(ref) => NoLoginNotifier(ref: ref));
|
||||||
|
|
||||||
|
// 2. notifier
|
||||||
|
class NoLoginNotifier extends StateNotifier<NoLoginState> {
|
||||||
|
final Ref ref;
|
||||||
|
NoLoginNotifier({required this.ref}) : super(NoLoginStateInit());
|
||||||
|
|
||||||
|
void prosesLoginF({
|
||||||
|
required String client_id,
|
||||||
|
required String host,
|
||||||
|
required String password,
|
||||||
|
}) async {
|
||||||
|
state = NoLoginStateLoading();
|
||||||
|
final resp = await NoLoginAuthRepository(
|
||||||
|
dio: ref.read(dioProvider),
|
||||||
|
).loginF(
|
||||||
|
client_id: client_id,
|
||||||
|
host: host,
|
||||||
|
password: password,
|
||||||
|
);
|
||||||
|
|
||||||
|
state = NoLoginStateDone(model: resp);
|
||||||
|
|
||||||
|
// Simpan ke SharedPreferences
|
||||||
|
final shared = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
// Simpan token dalam format JSON
|
||||||
|
final token = jsonEncode({
|
||||||
|
"expire_date": resp.expire_date,
|
||||||
|
"host": resp.host,
|
||||||
|
"date": DateTime.now().toIso8601String(),
|
||||||
|
"token": resp.token,
|
||||||
|
"client_id": resp.client_id,
|
||||||
|
"isLogin": resp.isLogin,
|
||||||
|
});
|
||||||
|
|
||||||
|
await shared.setString(Constant.bearerName, token);
|
||||||
|
ref.read(noLoginCurrentUserProvider.notifier).state = resp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. state
|
||||||
|
abstract class NoLoginState extends Equatable {
|
||||||
|
final DateTime date;
|
||||||
|
const NoLoginState(this.date);
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [date];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginStateInit extends NoLoginState {
|
||||||
|
NoLoginStateInit() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginStateLoading extends NoLoginState {
|
||||||
|
NoLoginStateLoading() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginStateError extends NoLoginState {
|
||||||
|
final String message;
|
||||||
|
NoLoginStateError({required this.message}) : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginStateDone extends NoLoginState {
|
||||||
|
final NoLoginAuthModel model;
|
||||||
|
NoLoginStateDone({required this.model}) : super(DateTime.now());
|
||||||
|
}
|
||||||
197
lib/screen/no-login/no_login_refresh_token_provider.dart
Normal file
197
lib/screen/no-login/no_login_refresh_token_provider.dart
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
// import 'dart:convert';
|
||||||
|
|
||||||
|
// import 'package:equatable/equatable.dart';
|
||||||
|
// import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
// import '../../provider/no-login/no_login_current_user_provider.dart';
|
||||||
|
// import '../../model/no-login/no_login_auth_model.dart';
|
||||||
|
// import '../../repository/no-login/no_login_auth_repository.dart';
|
||||||
|
// import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
// import '../../app/constant.dart';
|
||||||
|
// import '../../provider/dio_provider.dart';
|
||||||
|
// import '../../repository/base_repository.dart';
|
||||||
|
|
||||||
|
// // 3. state provider
|
||||||
|
// final noLoginRefreshTokenProvider = StateNotifierProvider<
|
||||||
|
// NoLoginRefreshTokenNotifier,
|
||||||
|
// NoLoginRefreshTokenState>((ref) => NoLoginRefreshTokenNotifier(ref: ref));
|
||||||
|
|
||||||
|
// // 2. notifier
|
||||||
|
// class NoLoginRefreshTokenNotifier
|
||||||
|
// extends StateNotifier<NoLoginRefreshTokenState> {
|
||||||
|
// final Ref ref;
|
||||||
|
// NoLoginRefreshTokenNotifier({required this.ref})
|
||||||
|
// : super(NoLoginRefreshTokenStateInit());
|
||||||
|
// void refreshToken({
|
||||||
|
// required String client_id,
|
||||||
|
// required String host,
|
||||||
|
// required String expire_date,
|
||||||
|
// }) async {
|
||||||
|
// try {
|
||||||
|
// state = NoLoginRefreshTokenStateLoading();
|
||||||
|
// final resp = await NoLoginAuthRepository(
|
||||||
|
// dio: ref.read(dioProvider),
|
||||||
|
// ).refreshToken(
|
||||||
|
// client_id: client_id,
|
||||||
|
// host: host,
|
||||||
|
// expire_date: expire_date,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // print(resp);
|
||||||
|
// state = NoLoginRefreshTokenStateDone(model: resp);
|
||||||
|
// //Simpan ke token
|
||||||
|
// final shared = await SharedPreferences.getInstance();
|
||||||
|
// String expireDateStr = resp.expire_date;
|
||||||
|
// if (!expireDateStr.contains("T")) {
|
||||||
|
// expireDateStr = expireDateStr.replaceFirst(" ", "T");
|
||||||
|
// }
|
||||||
|
// // Simpan dalam format ISO 8601
|
||||||
|
// // await shared.setString("expire_date", expireDateStr);
|
||||||
|
|
||||||
|
// final token = jsonEncode({
|
||||||
|
// "host": host,
|
||||||
|
// "date": DateTime.now().toString(),
|
||||||
|
// "token": resp.token,
|
||||||
|
// "client_id": resp.client_id,
|
||||||
|
// // "expire_date": resp.expire_date,
|
||||||
|
// "expire_date": expireDateStr,
|
||||||
|
// "isLogin": resp.isLogin,
|
||||||
|
// });
|
||||||
|
// await shared.setString(Constant.bearerName, token);
|
||||||
|
// ref.read(noLoginCurrentUserProvider.notifier).state = resp;
|
||||||
|
|
||||||
|
// // print(shared.getString(Constant.bearerName));
|
||||||
|
// } catch (e) {
|
||||||
|
// if (e is BaseRepositoryException) {
|
||||||
|
// state = NoLoginRefreshTokenStateError(message: e.message);
|
||||||
|
// } else {
|
||||||
|
// state = NoLoginRefreshTokenStateError(message: e.toString());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 1. state
|
||||||
|
// abstract class NoLoginRefreshTokenState extends Equatable {
|
||||||
|
// final DateTime date;
|
||||||
|
// const NoLoginRefreshTokenState(this.date);
|
||||||
|
// @override
|
||||||
|
// List<Object?> get props => [date];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class NoLoginRefreshTokenStateInit extends NoLoginRefreshTokenState {
|
||||||
|
// NoLoginRefreshTokenStateInit() : super(DateTime.now());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class NoLoginRefreshTokenStateLoading extends NoLoginRefreshTokenState {
|
||||||
|
// NoLoginRefreshTokenStateLoading() : super(DateTime.now());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class NoLoginRefreshTokenStateError extends NoLoginRefreshTokenState {
|
||||||
|
// final String message;
|
||||||
|
// NoLoginRefreshTokenStateError({
|
||||||
|
// required this.message,
|
||||||
|
// }) : super(DateTime.now());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class NoLoginRefreshTokenStateDone extends NoLoginRefreshTokenState {
|
||||||
|
// final NoLoginAuthModel model;
|
||||||
|
// NoLoginRefreshTokenStateDone({
|
||||||
|
// required this.model,
|
||||||
|
// }) : super(DateTime.now());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// new
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import '../../provider/no-login/no_login_current_user_provider.dart';
|
||||||
|
import '../../model/no-login/no_login_auth_model.dart';
|
||||||
|
import '../../repository/no-login/no_login_auth_repository.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
import '../../provider/dio_provider.dart';
|
||||||
|
import '../../repository/base_repository.dart';
|
||||||
|
|
||||||
|
// 3. State Provider
|
||||||
|
final noLoginRefreshTokenProvider = StateNotifierProvider<
|
||||||
|
NoLoginRefreshTokenNotifier,
|
||||||
|
NoLoginRefreshTokenState>((ref) => NoLoginRefreshTokenNotifier(ref: ref));
|
||||||
|
|
||||||
|
// 2. Notifier
|
||||||
|
class NoLoginRefreshTokenNotifier
|
||||||
|
extends StateNotifier<NoLoginRefreshTokenState> {
|
||||||
|
final Ref ref;
|
||||||
|
NoLoginRefreshTokenNotifier({required this.ref})
|
||||||
|
: super(NoLoginRefreshTokenStateInit());
|
||||||
|
|
||||||
|
void refreshToken({
|
||||||
|
required String client_id,
|
||||||
|
required String host,
|
||||||
|
required String expire_date,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
state = NoLoginRefreshTokenStateLoading();
|
||||||
|
final resp = await NoLoginAuthRepository(
|
||||||
|
dio: ref.read(dioProvider),
|
||||||
|
).refreshToken(
|
||||||
|
client_id: client_id,
|
||||||
|
host: host,
|
||||||
|
expire_date: expire_date,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Simpan token ke SharedPreferences
|
||||||
|
final shared = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
final tokenData = jsonEncode({
|
||||||
|
"expire_date": resp.expire_date,
|
||||||
|
"host": host,
|
||||||
|
"date": DateTime.now().toString(),
|
||||||
|
"token": resp.token,
|
||||||
|
"client_id": resp.client_id,
|
||||||
|
"isLogin": resp.isLogin,
|
||||||
|
});
|
||||||
|
|
||||||
|
await shared.setString(Constant.bearerName, tokenData);
|
||||||
|
ref.read(noLoginCurrentUserProvider.notifier).state = resp;
|
||||||
|
state = NoLoginRefreshTokenStateDone(model: resp);
|
||||||
|
} catch (e) {
|
||||||
|
if (e is BaseRepositoryException) {
|
||||||
|
state = NoLoginRefreshTokenStateError(message: e.message);
|
||||||
|
} else {
|
||||||
|
state = NoLoginRefreshTokenStateError(message: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. State
|
||||||
|
abstract class NoLoginRefreshTokenState extends Equatable {
|
||||||
|
final DateTime date;
|
||||||
|
const NoLoginRefreshTokenState(this.date);
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [date];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginRefreshTokenStateInit extends NoLoginRefreshTokenState {
|
||||||
|
NoLoginRefreshTokenStateInit() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginRefreshTokenStateLoading extends NoLoginRefreshTokenState {
|
||||||
|
NoLoginRefreshTokenStateLoading() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginRefreshTokenStateError extends NoLoginRefreshTokenState {
|
||||||
|
final String message;
|
||||||
|
NoLoginRefreshTokenStateError({
|
||||||
|
required this.message,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoLoginRefreshTokenStateDone extends NoLoginRefreshTokenState {
|
||||||
|
final NoLoginAuthModel model;
|
||||||
|
NoLoginRefreshTokenStateDone({
|
||||||
|
required this.model,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
439
lib/screen/no-login/no_login_screen.dart
Normal file
439
lib/screen/no-login/no_login_screen.dart
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
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:shared_preferences/shared_preferences.dart';
|
||||||
|
import '../../app/route.dart';
|
||||||
|
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
import '../../model/no-login/no_login_auth_model.dart';
|
||||||
|
import '../../provider/no-login/no_login_current_user_provider.dart';
|
||||||
|
import '../../widget/customsnackbarwidget.dart';
|
||||||
|
import 'no_login_provider.dart';
|
||||||
|
|
||||||
|
class NoLoginScreen extends HookConsumerWidget {
|
||||||
|
const NoLoginScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [
|
||||||
|
SystemUiOverlay.bottom,
|
||||||
|
]);
|
||||||
|
|
||||||
|
final clientIdCtr = useTextEditingController(
|
||||||
|
text: "sasdev",
|
||||||
|
);
|
||||||
|
|
||||||
|
final passwordCtr = useTextEditingController(
|
||||||
|
text: "sasdev123",
|
||||||
|
);
|
||||||
|
|
||||||
|
final hostCtr = useTextEditingController(
|
||||||
|
text: "devone.aplikasi.web.id",
|
||||||
|
);
|
||||||
|
|
||||||
|
final isLoading = useState(false);
|
||||||
|
final isSuccess = useState(false);
|
||||||
|
final hasNavigated = useState(false);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
|
final shared = await SharedPreferences.getInstance();
|
||||||
|
final bearerString = shared.getString(Constant.bearerName);
|
||||||
|
|
||||||
|
if (bearerString == null || bearerString.isEmpty) return;
|
||||||
|
|
||||||
|
final xmodel = jsonDecode(bearerString);
|
||||||
|
if (xmodel == null) return;
|
||||||
|
|
||||||
|
final authModel = NoLoginAuthModel(
|
||||||
|
host: xmodel["host"],
|
||||||
|
token: xmodel["token"],
|
||||||
|
client_id: xmodel['client_id'],
|
||||||
|
expire_date: xmodel['expire_date'],
|
||||||
|
isLogin: xmodel['isLogin'],
|
||||||
|
);
|
||||||
|
|
||||||
|
ref.read(noLoginCurrentUserProvider.notifier).state = authModel;
|
||||||
|
|
||||||
|
if (!hasNavigated.value) {
|
||||||
|
hasNavigated.value = true;
|
||||||
|
Timer(const Duration(seconds: 2), () {
|
||||||
|
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
|
noLoginHomeRoute,
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Timer(const Duration(seconds: 2), () {
|
||||||
|
// Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
|
// noLoginHomeRoute,
|
||||||
|
// (route) => false,
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
return () {};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// loginF
|
||||||
|
ref.listen(noLoginProvider, (prev, next) {
|
||||||
|
if (next is NoLoginStateLoading) {
|
||||||
|
isLoading.value = true;
|
||||||
|
} else if (next is NoLoginStateError) {
|
||||||
|
isLoading.value = false;
|
||||||
|
final errorMessage = next.message.isNotEmpty
|
||||||
|
? next.message
|
||||||
|
: "Terjadi kesalahan, silakan coba lagi.";
|
||||||
|
print('ERROR: $errorMessage');
|
||||||
|
snackbarWidget(
|
||||||
|
context,
|
||||||
|
next.message,
|
||||||
|
snackbarType.error,
|
||||||
|
const Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
} else if (next is NoLoginStateDone) {
|
||||||
|
isLoading.value = false;
|
||||||
|
isSuccess.value = true;
|
||||||
|
|
||||||
|
if (!hasNavigated.value) {
|
||||||
|
hasNavigated.value = true;
|
||||||
|
Navigator.of(context)
|
||||||
|
.pushNamedAndRemoveUntil(noLoginHomeRoute, (route) => false);
|
||||||
|
}
|
||||||
|
// Navigator.of(context)
|
||||||
|
// .pushNamedAndRemoveUntil(noLoginHomeRoute, (route) => false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
void login() {
|
||||||
|
if (clientIdCtr.text.isEmpty ||
|
||||||
|
passwordCtr.text.isEmpty ||
|
||||||
|
hostCtr.text.isEmpty) {
|
||||||
|
snackbarWidget(
|
||||||
|
context,
|
||||||
|
'Inputan wajib diisi',
|
||||||
|
snackbarType.error,
|
||||||
|
const Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// print('proses login');
|
||||||
|
ref.read(noLoginProvider.notifier).prosesLoginF(
|
||||||
|
client_id: clientIdCtr.text,
|
||||||
|
host: hostCtr.text,
|
||||||
|
password: passwordCtr.text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
FocusManager.instance.primaryFocus!.unfocus();
|
||||||
|
},
|
||||||
|
child: Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
backgroundColor: Constant.textWhite,
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// atas
|
||||||
|
Image.asset(
|
||||||
|
'images/vektoratas.png',
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 40,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// konten didalamnya
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Selamat Datang',
|
||||||
|
style: Constant.title_700(context: context).copyWith(
|
||||||
|
color: Constant.textBlack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Silahkan masuk untuk mengakses akun Anda',
|
||||||
|
style: Constant.title_400(context: context).copyWith(
|
||||||
|
color: Constant.textBlack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 64,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// inputan
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Client ID',
|
||||||
|
style: Constant.title_400(context: context).copyWith(
|
||||||
|
color: Constant.inputanGrey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: TextField(
|
||||||
|
controller: clientIdCtr,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "Masukkan Client ID",
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.grey,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.blue,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Password',
|
||||||
|
style: Constant.title_400(context: context).copyWith(
|
||||||
|
color: Constant.inputanGrey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: TextField(
|
||||||
|
controller: passwordCtr,
|
||||||
|
obscureText: true,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "Masukkan Password",
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.grey,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.blue,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Host',
|
||||||
|
style: Constant.title_400(context: context).copyWith(
|
||||||
|
color: Constant.inputanGrey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: TextField(
|
||||||
|
controller: hostCtr,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "Masukkan Host",
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.grey,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.blue,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 64,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// inputan
|
||||||
|
|
||||||
|
// button login
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
right: Constant.getActualXPhone(
|
||||||
|
context: context,
|
||||||
|
x: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 48,
|
||||||
|
),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
(isLoading.value || (isSuccess.value == true))
|
||||||
|
? null
|
||||||
|
: login();
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Constant.bgButton,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
elevation: 8,
|
||||||
|
shadowColor: Constant.bgButton.withOpacity(0.24),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
(isLoading.value) ? 'Loading...' : 'LOGIN',
|
||||||
|
style: Constant.titleButton500(context: context).copyWith(
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// konten didalamnya
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context,
|
||||||
|
y: 60,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,8 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:scanktpflutter/screen/scan/edit_scan_provider.dart';
|
import 'package:scanktpflutter/screen/scan/edit_scan_provider.dart';
|
||||||
import '../../model/sex_model.dart';
|
import '../../model/sex_model.dart';
|
||||||
import '../../screen/scan/sex_provider.dart';
|
|
||||||
import '../../provider/scan_provider.dart';
|
import '../../provider/scan_provider.dart';
|
||||||
|
import '../../screen/scan/sex_provider.dart';
|
||||||
import '../../widget/customsnackbarwidget.dart';
|
import '../../widget/customsnackbarwidget.dart';
|
||||||
|
|
||||||
import '../../app/app_extension.dart';
|
import '../../app/app_extension.dart';
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import mobile_scanner
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,6 +352,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.15.0"
|
||||||
|
mobile_scanner:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: mobile_scanner
|
||||||
|
sha256: "91d28b825784e15572fdc39165c5733099ce0e69c6f6f0964ebdbf98a62130fd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.6"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ dependencies:
|
|||||||
image: ^4.1.3
|
image: ^4.1.3
|
||||||
permission_handler: ^11.3.0
|
permission_handler: ^11.3.0
|
||||||
dropdown_button2: ^2.3.9
|
dropdown_button2: ^2.3.9
|
||||||
|
mobile_scanner: ^6.0.6
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user