step 10 : check distance selfie or not from back end
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import 'package:absensi_sas_flutter/screen/home/home_screen_v1.dart';
|
import 'package:absensi_sas_flutter/screen/home/home_screen_v1.dart';
|
||||||
import 'package:absensi_sas_flutter/screen/presensi/presensi_screen.dart';
|
import 'package:absensi_sas_flutter/screen/presensi/presensi_screen.dart';
|
||||||
|
import 'package:absensi_sas_flutter/screen/presensi/presensi_selfie_screen.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../screen/home/home_screen.dart';
|
import '../screen/home/home_screen.dart';
|
||||||
import '../test_flutter_map.dart';
|
import '../test_flutter_map.dart';
|
||||||
@@ -11,10 +12,10 @@ const splashRoute = "/splashRoute";
|
|||||||
const testFlutterMapRoute = "/testFlutterMapRoute";
|
const testFlutterMapRoute = "/testFlutterMapRoute";
|
||||||
const homeRoute = "/homeRoute";
|
const homeRoute = "/homeRoute";
|
||||||
const presensiRoute = "/presensiRoute";
|
const presensiRoute = "/presensiRoute";
|
||||||
|
const presensiSelfieRoute = "/presensiSelfieRoute";
|
||||||
|
|
||||||
class AppRoute {
|
class AppRoute {
|
||||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||||
|
|
||||||
// test flutter map
|
// test flutter map
|
||||||
if (settings.name == testFlutterMapRoute) {
|
if (settings.name == testFlutterMapRoute) {
|
||||||
return MaterialPageRoute(builder: (context) {
|
return MaterialPageRoute(builder: (context) {
|
||||||
@@ -48,6 +49,17 @@ class AppRoute {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// presensi selfie route
|
||||||
|
if (settings.name == presensiSelfieRoute) {
|
||||||
|
return MaterialPageRoute(builder: (context) {
|
||||||
|
return MediaQuery(
|
||||||
|
data: MediaQuery.of(context)
|
||||||
|
.copyWith(textScaleFactor: 1.0, padding: EdgeInsets.all(0)),
|
||||||
|
child: PresensiSelfieScreen(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// splash screen
|
// splash screen
|
||||||
if (settings.name == splashRoute) {
|
if (settings.name == splashRoute) {
|
||||||
return MaterialPageRoute(builder: (context) {
|
return MaterialPageRoute(builder: (context) {
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ class MyApp extends StatelessWidget {
|
|||||||
primarySwatch: Colors.orange,
|
primarySwatch: Colors.orange,
|
||||||
),
|
),
|
||||||
// home: TestMap(),
|
// home: TestMap(),
|
||||||
// initialRoute: loginRoute,
|
initialRoute: loginRoute,
|
||||||
initialRoute: presensiRoute,
|
// initialRoute: presensiRoute,
|
||||||
// initialRoute: testFlutterMapRoute,
|
// initialRoute: testFlutterMapRoute,
|
||||||
onGenerateRoute: AppRoute.generateRoute,
|
onGenerateRoute: AppRoute.generateRoute,
|
||||||
);
|
);
|
||||||
|
|||||||
36
lib/model/check_distance_model.dart
Normal file
36
lib/model/check_distance_model.dart
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
class CheckDistanceModel {
|
||||||
|
String? status;
|
||||||
|
String? message;
|
||||||
|
String? selfie;
|
||||||
|
String? unit;
|
||||||
|
String? maxDistance;
|
||||||
|
String? currentDistance;
|
||||||
|
|
||||||
|
CheckDistanceModel(
|
||||||
|
{this.status,
|
||||||
|
this.message,
|
||||||
|
this.selfie,
|
||||||
|
this.unit,
|
||||||
|
this.maxDistance,
|
||||||
|
this.currentDistance});
|
||||||
|
|
||||||
|
CheckDistanceModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
status = json['status'];
|
||||||
|
message = json['message'];
|
||||||
|
selfie = json['selfie'];
|
||||||
|
unit = json['unit'];
|
||||||
|
maxDistance = json['max_distance'];
|
||||||
|
currentDistance = json['current_distance'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['status'] = this.status;
|
||||||
|
data['message'] = this.message;
|
||||||
|
data['selfie'] = this.selfie;
|
||||||
|
data['unit'] = this.unit;
|
||||||
|
data['max_distance'] = this.maxDistance;
|
||||||
|
data['current_distance'] = this.currentDistance;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lib/provider/current_check_distance_provider.dart
Normal file
4
lib/provider/current_check_distance_provider.dart
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import 'package:absensi_sas_flutter/model/check_distance_model.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
final currentCheckDistanceProvider = StateProvider<CheckDistanceModel?>((ref) => null);
|
||||||
45
lib/repository/presensi_repository.dart
Normal file
45
lib/repository/presensi_repository.dart
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import 'package:absensi_sas_flutter/model/check_distance_model.dart';
|
||||||
|
import 'base_repository.dart';
|
||||||
|
|
||||||
|
class PresensiRepository extends BaseRepository {
|
||||||
|
PresensiRepository({required super.graphql, required super.dio});
|
||||||
|
|
||||||
|
// check distance
|
||||||
|
Future<CheckDistanceModel> checkDistance(
|
||||||
|
String M_StaffID,
|
||||||
|
String M_CompanyID,
|
||||||
|
String CurrentLatitude,
|
||||||
|
String CurrentLongitude,
|
||||||
|
) async {
|
||||||
|
const String query =
|
||||||
|
r'''query($M_StaffID:String!, $M_CompanyID:String!, $CurrentLatitude:String!, $CurrentLongitude:String!){ queryCheckDistance(M_StaffID:$M_StaffID, M_CompanyID:$M_CompanyID, CurrentLatitude:$CurrentLatitude, CurrentLongitude:$CurrentLongitude){ status message selfie unit max_distance current_distance } }''';
|
||||||
|
|
||||||
|
Map<String, dynamic> inpVariables = {
|
||||||
|
"M_StaffID": M_StaffID,
|
||||||
|
"M_CompanyID": M_CompanyID,
|
||||||
|
"CurrentLatitude": CurrentLatitude,
|
||||||
|
"CurrentLongitude": CurrentLongitude,
|
||||||
|
};
|
||||||
|
final resp = await postGraphQlMutation(query, inpVariables);
|
||||||
|
// final loginData = AuthModel.fromJson(resp['userLogin']);
|
||||||
|
|
||||||
|
print(inpVariables);
|
||||||
|
|
||||||
|
print('obj queryCheckDistance : ${resp["queryCheckDistance"]}');
|
||||||
|
|
||||||
|
// final result = AuthModel(
|
||||||
|
// token: resp["loginAttendance"]["token"],
|
||||||
|
// model: StaffModel.fromJson(resp["loginAttendance"]),
|
||||||
|
// );
|
||||||
|
|
||||||
|
final result = CheckDistanceModel(
|
||||||
|
currentDistance: resp['queryCheckDistance']['current_distance'],
|
||||||
|
maxDistance: resp['queryCheckDistance']['max_distance'],
|
||||||
|
message: resp['queryCheckDistance']['message'],
|
||||||
|
selfie: resp['queryCheckDistance']['selfie'],
|
||||||
|
status: resp['queryCheckDistance']['status'],
|
||||||
|
unit: resp['queryCheckDistance']['unit']
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,134 @@
|
|||||||
import 'package:absensi_sas_flutter/app/constant.dart';
|
import 'package:absensi_sas_flutter/app/constant.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:geocoding/geocoding.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:google_sign_in/google_sign_in.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
import '../../app/route.dart';
|
||||||
|
import '../../provider/current_check_distance_provider.dart';
|
||||||
|
import '../../provider/current_user_provider.dart';
|
||||||
|
import '../../provider/google_login_provider.dart';
|
||||||
import '../../widget/real_date.dart';
|
import '../../widget/real_date.dart';
|
||||||
import '../../widget/real_time.dart';
|
import '../../widget/real_time.dart';
|
||||||
|
import '../../widget/sankbar_widget.dart';
|
||||||
|
import '../presensi/check_distance_provider.dart';
|
||||||
|
|
||||||
class HomeScreen extends HookConsumerWidget {
|
class HomeScreen extends HookConsumerWidget {
|
||||||
const HomeScreen({super.key});
|
const HomeScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final selectedUser = ref.read(currentUserProvider);
|
||||||
|
final isLoadingProsesCheckDistance = useState<bool>(false);
|
||||||
|
final varCurrentDistanceProvider = ref.watch(currentCheckDistanceProvider);
|
||||||
|
final positionLatitude = useState<String>("");
|
||||||
|
final positionLongitude = useState<String>("");
|
||||||
|
GoogleSignInAccount? currentUserGoogle =
|
||||||
|
ref.watch(currentUserGoogleProvider);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
|
final staffID = ref.read(currentUserProvider)?.model.staffId ?? "0";
|
||||||
|
if (staffID == "0") {
|
||||||
|
//not login
|
||||||
|
Navigator.of(context)
|
||||||
|
.pushNamedAndRemoveUntil(loginRoute, (route) => true);
|
||||||
|
|
||||||
|
// Navigator.popAndPushNamed(context, loginRoute);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () {};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
Future<void> getAddressFromLocation() async {
|
||||||
|
try {
|
||||||
|
isLoadingProsesCheckDistance.value = true;
|
||||||
|
// Mendapatkan posisi pengguna
|
||||||
|
LocationPermission permission = await Geolocator.requestPermission();
|
||||||
|
|
||||||
|
if (permission == LocationPermission.denied) {
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
SanckbarWidget(context, 'Izin lokasi ditolak', snackbarType.error);
|
||||||
|
// Handle jika pengguna menolak izin lokasi
|
||||||
|
print("Izin lokasi ditolak");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position position = await Geolocator.getCurrentPosition(
|
||||||
|
desiredAccuracy: LocationAccuracy.high);
|
||||||
|
|
||||||
|
// Mendapatkan alamat dari posisi
|
||||||
|
List<Placemark> placemarks = await placemarkFromCoordinates(
|
||||||
|
position.latitude, position.longitude);
|
||||||
|
|
||||||
|
if (placemarks.isNotEmpty) {
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
Placemark placemark = placemarks.first;
|
||||||
|
// String address =
|
||||||
|
// "${placemark.thoroughfare}, ${placemark.locality}, ${placemark.administrativeArea}, ${placemark.country},";
|
||||||
|
|
||||||
|
String address =
|
||||||
|
"${placemark.street}, ${placemark.subLocality}, ${placemark.subAdministrativeArea}, ${placemark.postalCode}";
|
||||||
|
print("Alamat: $address");
|
||||||
|
|
||||||
|
positionLatitude.value = position.latitude.toString();
|
||||||
|
positionLongitude.value = position.longitude.toString();
|
||||||
|
|
||||||
|
// panggil check distance provider
|
||||||
|
ref.read(checkDistanceProvider.notifier).checkDistance(
|
||||||
|
selectedUser?.model.staffId ?? "",
|
||||||
|
selectedUser?.model.companyId ?? "",
|
||||||
|
positionLatitude.value,
|
||||||
|
positionLongitude.value,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
SanckbarWidget(
|
||||||
|
context, 'Tidak dapat menemukan alamat.', snackbarType.error);
|
||||||
|
print("Tidak dapat menemukan alamat.");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("Error: $e");
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
SanckbarWidget(context, 'Error : $e', snackbarType.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> requestLocationPermission() async {
|
||||||
|
var status = await Permission.location.request();
|
||||||
|
isLoadingProsesCheckDistance.value = true;
|
||||||
|
if (status.isGranted) {
|
||||||
|
// Izin diberikan, lanjutkan dengan mendapatkan lokasi
|
||||||
|
getAddressFromLocation();
|
||||||
|
} else {
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
// Izin ditolak, berikan pemberitahuan atau instruksi
|
||||||
|
// print('Izin lokasi ditolak');
|
||||||
|
SanckbarWidget(context, 'Izin Ditolak', snackbarType.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check distance provider
|
||||||
|
ref.listen(checkDistanceProvider, (prev, next) {
|
||||||
|
if (next is CheckDistanceStateLoading) {
|
||||||
|
isLoadingProsesCheckDistance.value = true;
|
||||||
|
} else if (next is CheckDistanceStateError) {
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
SanckbarWidget(context, next.message, snackbarType.warning);
|
||||||
|
} else if (next is CheckDistanceStateDone) {
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
|
||||||
|
if (varCurrentDistanceProvider?.selfie == "TRUE") {
|
||||||
|
Navigator.pushNamed(context, presensiSelfieRoute);
|
||||||
|
} else {
|
||||||
|
Navigator.pushNamed(context, presensiRoute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||||
@@ -18,21 +136,33 @@ class HomeScreen extends HookConsumerWidget {
|
|||||||
width: Constant.getActualXPhone(context: context, x: 100),
|
width: Constant.getActualXPhone(context: context, x: 100),
|
||||||
height: Constant.getActualYPhone(context: context, y: 100),
|
height: Constant.getActualYPhone(context: context, y: 100),
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
child: FloatingActionButton(
|
child: (isLoadingProsesCheckDistance.value)
|
||||||
onPressed: () {},
|
? SizedBox(
|
||||||
backgroundColor: Color(0xFFFFFFFF),
|
width: Constant.getActualXPhone(context: context, x: 50),
|
||||||
shape: CircleBorder(),
|
height: Constant.getActualYPhone(context: context, y: 50),
|
||||||
child: Container(
|
child: Center(
|
||||||
width: Constant.getActualXPhone(context: context, x: 50),
|
child: CircularProgressIndicator(
|
||||||
height: Constant.getActualYPhone(context: context, y: 50),
|
color: Constant.textOrange,
|
||||||
decoration: BoxDecoration(
|
),
|
||||||
image: DecorationImage(
|
),
|
||||||
image: AssetImage(
|
)
|
||||||
'images/finger_tap_orange_botnav.png'), // Ganti dengan path gambar Anda
|
: FloatingActionButton(
|
||||||
|
onPressed: () async {
|
||||||
|
requestLocationPermission();
|
||||||
|
},
|
||||||
|
backgroundColor: Color(0xFFFFFFFF),
|
||||||
|
shape: CircleBorder(),
|
||||||
|
child: Container(
|
||||||
|
width: Constant.getActualXPhone(context: context, x: 50),
|
||||||
|
height: Constant.getActualYPhone(context: context, y: 50),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(
|
||||||
|
'images/finger_tap_orange_botnav.png'), // Ganti dengan path gambar Anda
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
bottomNavigationBar: Container(
|
bottomNavigationBar: Container(
|
||||||
@@ -102,61 +232,73 @@ class HomeScreen extends HookConsumerWidget {
|
|||||||
right: Constant.getActualXPhone(context: context, x: 27),
|
right: Constant.getActualXPhone(context: context, x: 27),
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
child: ListTile(
|
child: (currentUserGoogle == null)
|
||||||
leading: Container(
|
? Center(
|
||||||
width:
|
child: CircularProgressIndicator(),
|
||||||
Constant.getActualXPhone(context: context, x: 36),
|
)
|
||||||
height:
|
: ListTile(
|
||||||
Constant.getActualYPhone(context: context, y: 36),
|
// leading: Container(
|
||||||
child: Image(
|
// width: Constant.getActualXPhone(
|
||||||
image: AssetImage('images/avatar_c.png'),
|
// context: context, x: 36),
|
||||||
)),
|
// height: Constant.getActualYPhone(
|
||||||
title: Text(
|
// context: context, y: 36),
|
||||||
"Stephen Kusumo",
|
// child: Image(
|
||||||
overflow: TextOverflow.ellipsis,
|
// image: AssetImage('images/avatar_c.png'),
|
||||||
style: Constant.titleH1_700(context: context)
|
// ),
|
||||||
..copyWith(
|
// ),
|
||||||
color: Constant.textBlack,
|
leading: GoogleUserCircleAvatar(
|
||||||
),
|
identity: currentUserGoogle,
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
"Step@example.com",
|
|
||||||
style:
|
|
||||||
Constant.subtitle_500_12(context: context).copyWith(
|
|
||||||
color: Constant.textLightGrey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: Container(
|
|
||||||
width:
|
|
||||||
Constant.getActualXPhone(context: context, x: 36),
|
|
||||||
height:
|
|
||||||
Constant.getActualYPhone(context: context, y: 36),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
|
||||||
color: Colors.white,
|
|
||||||
shape: BoxShape.rectangle,
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
offset: Offset(0, 12),
|
|
||||||
blurRadius: 24,
|
|
||||||
color: Color.fromRGBO(145, 158, 171, 0.12),
|
|
||||||
),
|
),
|
||||||
],
|
title: Text(
|
||||||
),
|
// "Stephen Kusumo",
|
||||||
child: IconButton(
|
// selectedUser?.model.name ?? "",
|
||||||
onPressed: () {},
|
currentUserGoogle.displayName ?? "",
|
||||||
icon: Container(
|
overflow: TextOverflow.ellipsis,
|
||||||
width: Constant.getActualXPhone(
|
style: Constant.titleH1_700(context: context)
|
||||||
context: context, x: 20),
|
..copyWith(
|
||||||
height: Constant.getActualYPhone(
|
color: Constant.textBlack,
|
||||||
context: context, y: 20),
|
),
|
||||||
child: Image(
|
),
|
||||||
image: AssetImage('images/alert_badge.png'),
|
subtitle: Text(
|
||||||
|
// "Step@example.com",
|
||||||
|
// currentUserGoogle?.email ?? "",
|
||||||
|
selectedUser?.model.email ?? "",
|
||||||
|
style: Constant.subtitle_500_12(context: context)
|
||||||
|
.copyWith(
|
||||||
|
color: Constant.textLightGrey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Container(
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context, x: 36),
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context, y: 36),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
offset: Offset(0, 12),
|
||||||
|
blurRadius: 24,
|
||||||
|
color: Color.fromRGBO(145, 158, 171, 0.12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: Container(
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context, x: 20),
|
||||||
|
height: Constant.getActualYPhone(
|
||||||
|
context: context, y: 20),
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage('images/alert_badge.png'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@@ -163,51 +163,47 @@ class LoginScreen extends HookConsumerWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: Constant.getActualYPhone(context: context, y: 100),
|
height: Constant.getActualYPhone(context: context, y: 100),
|
||||||
),
|
),
|
||||||
(currentUserGoogle != null)
|
// (currentUserGoogle != null)
|
||||||
? Container(
|
// ? Container(
|
||||||
child: ListTile(
|
// child: ListTile(
|
||||||
leading: GoogleUserCircleAvatar(
|
// leading: GoogleUserCircleAvatar(
|
||||||
identity: currentUserGoogle,
|
// identity: currentUserGoogle,
|
||||||
),
|
// ),
|
||||||
title: Text(
|
// title: Text(
|
||||||
currentUserGoogle.displayName ?? "",
|
// currentUserGoogle.displayName ?? "",
|
||||||
),
|
// ),
|
||||||
subtitle: Text(
|
// subtitle: Text(
|
||||||
currentUserGoogle.email,
|
// currentUserGoogle.email,
|
||||||
),
|
// ),
|
||||||
trailing: IconButton(
|
// trailing: IconButton(
|
||||||
icon: Icon(Icons.logout_outlined),
|
// icon: Icon(Icons.logout_outlined),
|
||||||
onPressed: () async {
|
// onPressed: () async {
|
||||||
await googleSignIn.disconnect();
|
// await googleSignIn.disconnect();
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
)
|
// )
|
||||||
:
|
// :
|
||||||
// Logo Landscape
|
// Logo Landscape
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
top: Constant.getActualYPhone(context: context, y: 120),
|
top: Constant.getActualYPhone(context: context, y: 120),
|
||||||
left: Constant.getActualXPhone(context: context, x: 91),
|
left: Constant.getActualXPhone(context: context, x: 91),
|
||||||
right:
|
right: Constant.getActualXPhone(context: context, x: 90),
|
||||||
Constant.getActualXPhone(context: context, x: 90),
|
// bottom: Constant.getActualYPhone(context: context, y: y)
|
||||||
// bottom: Constant.getActualYPhone(context: context, y: y)
|
),
|
||||||
),
|
child: Container(
|
||||||
child: Container(
|
width: Constant.getActualXPhone(context: context, x: 209),
|
||||||
width:
|
height: Constant.getActualYPhone(context: context, y: 70),
|
||||||
Constant.getActualXPhone(context: context, x: 209),
|
decoration: BoxDecoration(
|
||||||
height:
|
// color: Colors.green,
|
||||||
Constant.getActualYPhone(context: context, y: 70),
|
image: DecorationImage(
|
||||||
decoration: BoxDecoration(
|
// fit: BoxFit.contain,
|
||||||
// color: Colors.green,
|
image: AssetImage('images/logo_sismedika_landscape.png'),
|
||||||
image: DecorationImage(
|
|
||||||
// fit: BoxFit.contain,
|
|
||||||
image: AssetImage(
|
|
||||||
'images/logo_sismedika_landscape.png'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: Constant.getActualYPhone(context: context, y: 100),
|
height: Constant.getActualYPhone(context: context, y: 100),
|
||||||
@@ -282,33 +278,51 @@ class LoginScreen extends HookConsumerWidget {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await handleSignInGmail();
|
await handleSignInGmail();
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Stack(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Container(
|
(isLoading.value)
|
||||||
width:
|
? SizedBox(
|
||||||
Constant.getActualXPhone(context: context, x: 24),
|
width: Constant.getActualXPhone(
|
||||||
height:
|
context: context, x: 24),
|
||||||
Constant.getActualYPhone(context: context, y: 24),
|
height: Constant.getActualYPhone(
|
||||||
decoration: BoxDecoration(
|
context: context, y: 24),
|
||||||
// color: Colors.green,
|
child: Center(
|
||||||
image: DecorationImage(
|
child: CircularProgressIndicator(
|
||||||
// fit: BoxFit.contain,
|
color: Constant.textTrueBlack,
|
||||||
image: AssetImage('images/icon_google.png'),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
: Row(
|
||||||
SizedBox(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
width:
|
children: [
|
||||||
Constant.getActualXPhone(context: context, x: 2),
|
Container(
|
||||||
),
|
width: Constant.getActualXPhone(
|
||||||
Text(
|
context: context, x: 24),
|
||||||
'Continue with Google',
|
height: Constant.getActualYPhone(
|
||||||
style: Constant.logintitle_700(context: context)
|
context: context, y: 24),
|
||||||
.copyWith(
|
decoration: BoxDecoration(
|
||||||
color: Constant.textBlack,
|
// color: Colors.green,
|
||||||
),
|
image: DecorationImage(
|
||||||
),
|
// fit: BoxFit.contain,
|
||||||
|
image: AssetImage(
|
||||||
|
'images/icon_google.png'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: Constant.getActualXPhone(
|
||||||
|
context: context, x: 2),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Continue with Google',
|
||||||
|
style: Constant.logintitle_700(
|
||||||
|
context: context)
|
||||||
|
.copyWith(
|
||||||
|
color: Constant.textBlack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
|
|||||||
81
lib/screen/presensi/check_distance_provider.dart
Normal file
81
lib/screen/presensi/check_distance_provider.dart
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import 'package:absensi_sas_flutter/repository/presensi_repository.dart';
|
||||||
|
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import '../../model/check_distance_model.dart';
|
||||||
|
import '../../provider/current_check_distance_provider.dart';
|
||||||
|
import '../../provider/dio_provider.dart';
|
||||||
|
import '../../provider/graphql_provider.dart';
|
||||||
|
import '../../repository/base_repository.dart';
|
||||||
|
|
||||||
|
abstract class CheckDistanceState extends Equatable {
|
||||||
|
final DateTime date;
|
||||||
|
const CheckDistanceState(this.date);
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [date];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CheckDistanceStateInit extends CheckDistanceState {
|
||||||
|
CheckDistanceStateInit() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class CheckDistanceStateLoading extends CheckDistanceState {
|
||||||
|
CheckDistanceStateLoading() : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class CheckDistanceStateError extends CheckDistanceState {
|
||||||
|
final String message;
|
||||||
|
CheckDistanceStateError({
|
||||||
|
required this.message,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
class CheckDistanceStateDone extends CheckDistanceState {
|
||||||
|
final CheckDistanceModel model;
|
||||||
|
CheckDistanceStateDone({
|
||||||
|
required this.model,
|
||||||
|
}) : super(DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
//notifier
|
||||||
|
class CheckDistanceNotifier extends StateNotifier<CheckDistanceState> {
|
||||||
|
final Ref ref;
|
||||||
|
CheckDistanceNotifier({
|
||||||
|
required this.ref,
|
||||||
|
}) : super(CheckDistanceStateInit());
|
||||||
|
|
||||||
|
void checkDistance(
|
||||||
|
String M_StaffID,
|
||||||
|
String M_CompanyID,
|
||||||
|
String CurrentLatitude,
|
||||||
|
String CurrentLongitude,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
state = CheckDistanceStateLoading();
|
||||||
|
final graphql = ref.read(graphqlProvider(''));
|
||||||
|
final dio = ref.read(dioProvider);
|
||||||
|
final resp =
|
||||||
|
await PresensiRepository(graphql: graphql, dio: dio).checkDistance(
|
||||||
|
M_StaffID,
|
||||||
|
M_CompanyID,
|
||||||
|
CurrentLatitude,
|
||||||
|
CurrentLongitude,
|
||||||
|
);
|
||||||
|
// set ke global state provider
|
||||||
|
ref.read(currentCheckDistanceProvider.notifier).update((state) => resp);
|
||||||
|
state = CheckDistanceStateDone(model: resp);
|
||||||
|
} catch (e) {
|
||||||
|
if (e is BaseRepositoryException) {
|
||||||
|
state = CheckDistanceStateError(message: e.message ?? "");
|
||||||
|
} else {
|
||||||
|
state = CheckDistanceStateError(message: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// provider
|
||||||
|
final checkDistanceProvider =
|
||||||
|
StateNotifierProvider<CheckDistanceNotifier, CheckDistanceState>(
|
||||||
|
(ref) => CheckDistanceNotifier(ref: ref),
|
||||||
|
);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:absensi_sas_flutter/screen/presensi/check_distance_provider.dart';
|
||||||
import 'package:absensi_sas_flutter/widget/real_date.dart';
|
import 'package:absensi_sas_flutter/widget/real_date.dart';
|
||||||
import 'package:absensi_sas_flutter/widget/sankbar_widget.dart';
|
import 'package:absensi_sas_flutter/widget/sankbar_widget.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -7,8 +8,10 @@ import 'package:geolocator/geolocator.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
|
||||||
import '../../app/constant.dart';
|
import '../../app/constant.dart';
|
||||||
|
import '../../app/route.dart';
|
||||||
|
import '../../provider/current_check_distance_provider.dart';
|
||||||
|
import '../../provider/current_user_provider.dart';
|
||||||
import '../../widget/real_time.dart';
|
import '../../widget/real_time.dart';
|
||||||
|
|
||||||
class PresensiScreen extends HookConsumerWidget {
|
class PresensiScreen extends HookConsumerWidget {
|
||||||
@@ -18,6 +21,13 @@ class PresensiScreen extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final currentAddressUserLocation = useState<String>("");
|
final currentAddressUserLocation = useState<String>("");
|
||||||
final isLoadingAddressUserLocation = useState<bool>(false);
|
final isLoadingAddressUserLocation = useState<bool>(false);
|
||||||
|
final isLoadingProsesCheckDistance = useState<bool>(false);
|
||||||
|
final varCurrentDistanceProvider = ref.watch(currentCheckDistanceProvider);
|
||||||
|
final selectedUser = ref.read(currentUserProvider);
|
||||||
|
|
||||||
|
final positionLatitude = useState<String>("");
|
||||||
|
final positionLongitude = useState<String>("");
|
||||||
|
// final tTransactionCurrentDistance = useState<String>("NULL");
|
||||||
|
|
||||||
Future<void> getAddressFromLocation() async {
|
Future<void> getAddressFromLocation() async {
|
||||||
try {
|
try {
|
||||||
@@ -40,24 +50,42 @@ class PresensiScreen extends HookConsumerWidget {
|
|||||||
List<Placemark> placemarks = await placemarkFromCoordinates(
|
List<Placemark> placemarks = await placemarkFromCoordinates(
|
||||||
position.latitude, position.longitude);
|
position.latitude, position.longitude);
|
||||||
|
|
||||||
if (placemarks.isNotEmpty) {
|
if (positionLongitude.value.isEmpty && positionLatitude.value.isEmpty) {
|
||||||
isLoadingAddressUserLocation.value = false;
|
if (placemarks.isNotEmpty) {
|
||||||
Placemark placemark = placemarks.first;
|
isLoadingAddressUserLocation.value = false;
|
||||||
// String address =
|
Placemark placemark = placemarks.first;
|
||||||
// "${placemark.thoroughfare}, ${placemark.locality}, ${placemark.administrativeArea}, ${placemark.country},";
|
// String address =
|
||||||
|
// "${placemark.thoroughfare}, ${placemark.locality}, ${placemark.administrativeArea}, ${placemark.country},";
|
||||||
|
|
||||||
String address =
|
String address =
|
||||||
"${placemark.street}, ${placemark.subLocality}, ${placemark.subAdministrativeArea}, ${placemark.postalCode}";
|
"${placemark.street}, ${placemark.subLocality}, ${placemark.subAdministrativeArea}, ${placemark.postalCode}";
|
||||||
print("Alamat: $address");
|
print("Alamat: $address");
|
||||||
|
|
||||||
if (address != "") {
|
positionLatitude.value = position.latitude.toString();
|
||||||
currentAddressUserLocation.value = address;
|
positionLongitude.value = position.longitude.toString();
|
||||||
|
|
||||||
|
if (address != "") {
|
||||||
|
currentAddressUserLocation.value = address;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isLoadingAddressUserLocation.value = false;
|
||||||
|
SanckbarWidget(
|
||||||
|
context, 'Tidak dapat menemukan alamat.', snackbarType.error);
|
||||||
|
print("Tidak dapat menemukan alamat.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
isLoadingAddressUserLocation.value = false;
|
// jika sudah dapat latitude dan logitude baru panggil check distance provider
|
||||||
SanckbarWidget(
|
if (positionLatitude.value.isNotEmpty &&
|
||||||
context, 'Tidak dapat menemukan alamat.', snackbarType.error);
|
positionLongitude.value.isNotEmpty) {
|
||||||
print("Tidak dapat menemukan alamat.");
|
print('check distance provider');
|
||||||
|
// panggil check distance provider
|
||||||
|
ref.read(checkDistanceProvider.notifier).checkDistance(
|
||||||
|
selectedUser?.model.staffId ?? "",
|
||||||
|
selectedUser?.model.companyId ?? "",
|
||||||
|
positionLatitude.value,
|
||||||
|
positionLongitude.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("Error: $e");
|
print("Error: $e");
|
||||||
@@ -66,7 +94,7 @@ class PresensiScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _requestLocationPermission() async {
|
Future<void> requestLocationPermission() async {
|
||||||
var status = await Permission.location.request();
|
var status = await Permission.location.request();
|
||||||
isLoadingAddressUserLocation.value = true;
|
isLoadingAddressUserLocation.value = true;
|
||||||
if (status.isGranted) {
|
if (status.isGranted) {
|
||||||
@@ -76,17 +104,161 @@ class PresensiScreen extends HookConsumerWidget {
|
|||||||
isLoadingAddressUserLocation.value = false;
|
isLoadingAddressUserLocation.value = false;
|
||||||
// Izin ditolak, berikan pemberitahuan atau instruksi
|
// Izin ditolak, berikan pemberitahuan atau instruksi
|
||||||
// print('Izin lokasi ditolak');
|
// print('Izin lokasi ditolak');
|
||||||
SanckbarWidget(context, 'Izin Ditolak', snackbarType.error);
|
SanckbarWidget(context, 'Izin Ditolak', snackbarType.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check distance provider
|
||||||
|
ref.listen(checkDistanceProvider, (prev, next) {
|
||||||
|
print('status check distance ' + next.toString());
|
||||||
|
if (next is CheckDistanceStateLoading) {
|
||||||
|
isLoadingProsesCheckDistance.value = true;
|
||||||
|
} else if (next is CheckDistanceStateError) {
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
SanckbarWidget(context, next.message, snackbarType.warning);
|
||||||
|
} else if (next is CheckDistanceStateDone) {
|
||||||
|
isLoadingProsesCheckDistance.value = false;
|
||||||
|
|
||||||
|
// tTransactionCurrentDistance.value =
|
||||||
|
// varCurrentDistanceProvider?.currentDistance ?? "";
|
||||||
|
|
||||||
|
// print("distance XXX : "+tTransactionCurrentDistance.value);
|
||||||
|
|
||||||
|
if (varCurrentDistanceProvider?.selfie == "TRUE") {
|
||||||
|
Navigator.pushNamed(context, presensiSelfieRoute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
getAddressFromLocation();
|
final staffID = ref.read(currentUserProvider)?.model.staffId ?? "0";
|
||||||
|
if (staffID == "0") {
|
||||||
|
//not login
|
||||||
|
Navigator.of(context)
|
||||||
|
.pushNamedAndRemoveUntil(loginRoute, (route) => true);
|
||||||
|
|
||||||
|
// Navigator.popAndPushNamed(context, loginRoute);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// requestLocationPermission();
|
||||||
});
|
});
|
||||||
return () {};
|
return () {};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
|
requestLocationPermission();
|
||||||
|
|
||||||
|
// // jika sudah dapat latitude dan logitude baru panggil check distance provider
|
||||||
|
// if (positionLatitude.value.isNotEmpty &&
|
||||||
|
// positionLongitude.value.isNotEmpty) {
|
||||||
|
// // panggil check distance provider
|
||||||
|
// ref.read(checkDistanceProvider.notifier).checkDistance(
|
||||||
|
// selectedUser?.model.staffId ?? "",
|
||||||
|
// selectedUser?.model.companyId ?? "",
|
||||||
|
// positionLatitude.value,
|
||||||
|
// positionLongitude.value,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
return () {};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// show konfirmasi dialog
|
||||||
|
Future<void> showConfirmationDialog(
|
||||||
|
BuildContext context,
|
||||||
|
String T_TransactionM_StaffID,
|
||||||
|
String T_TransactionM_CompanyID,
|
||||||
|
String T_TransactionCurrentLatitude,
|
||||||
|
String T_TransactionCurrentLongitude,
|
||||||
|
String T_TransactionCurrentDistance,
|
||||||
|
String T_TransactionSelfiePhoto,
|
||||||
|
String token,
|
||||||
|
) async {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('Konfirmasi'),
|
||||||
|
content: Text('Apakah anda yakin untuk melakukan Clock In?'),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
T_TransactionSelfiePhoto = "";
|
||||||
|
|
||||||
|
var param = {
|
||||||
|
"T_TransactionM_StaffID": T_TransactionM_StaffID,
|
||||||
|
"T_TransactionM_CompanyID": T_TransactionM_CompanyID,
|
||||||
|
"T_TransactionCurrentLatitude":
|
||||||
|
T_TransactionCurrentLatitude,
|
||||||
|
"T_TransactionCurrentLongitude":
|
||||||
|
T_TransactionCurrentLongitude,
|
||||||
|
"T_TransactionCurrentDistance":
|
||||||
|
T_TransactionCurrentDistance,
|
||||||
|
"T_TransactionSelfiePhoto": T_TransactionSelfiePhoto,
|
||||||
|
"token":token,
|
||||||
|
};
|
||||||
|
print(param);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Yakin',
|
||||||
|
style: Constant.logintitle_700(context: context).copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Constant.textWhite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateColor.resolveWith(
|
||||||
|
(st) => Constant.textOrange),
|
||||||
|
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
side: BorderSide(
|
||||||
|
color: Constant.textOrange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
shadowColor: MaterialStateProperty.all(Color(0xffff48423d)),
|
||||||
|
elevation: MaterialStateProperty.all(4.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(); // Tutup dialog
|
||||||
|
},
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateColor.resolveWith(
|
||||||
|
(st) => Constant.textWhite),
|
||||||
|
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
side: BorderSide(
|
||||||
|
color: Constant.textBlack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
shadowColor: MaterialStateProperty.all(Color(0xffff48423d)),
|
||||||
|
elevation: MaterialStateProperty.all(4.0),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Batal',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Constant.textTrueBlack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
top: Constant.getActualYPhone(context: context, y: 30),
|
top: Constant.getActualYPhone(context: context, y: 30),
|
||||||
@@ -121,6 +293,7 @@ class PresensiScreen extends HookConsumerWidget {
|
|||||||
height: MediaQuery.of(context).size.height,
|
height: MediaQuery.of(context).size.height,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
// Text(ref.watch(currentCheckDistanceProvider)?.currentDistance ?? "NULL"),
|
||||||
// Spacer(),
|
// Spacer(),
|
||||||
// tanggal sekarang
|
// tanggal sekarang
|
||||||
RealTimeFormattedDate(),
|
RealTimeFormattedDate(),
|
||||||
@@ -198,6 +371,19 @@ class PresensiScreen extends HookConsumerWidget {
|
|||||||
InkWell(
|
InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
getAddressFromLocation();
|
getAddressFromLocation();
|
||||||
|
// jika sudah dapat latitude dan logitude baru panggil check distance provider
|
||||||
|
if (positionLatitude.value.isNotEmpty &&
|
||||||
|
positionLongitude.value.isNotEmpty) {
|
||||||
|
// panggil check distance provider
|
||||||
|
ref
|
||||||
|
.read(checkDistanceProvider.notifier)
|
||||||
|
.checkDistance(
|
||||||
|
selectedUser?.model.staffId ?? "",
|
||||||
|
selectedUser?.model.companyId ?? "",
|
||||||
|
positionLatitude.value,
|
||||||
|
positionLongitude.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: Constant.getActualXPhone(
|
width: Constant.getActualXPhone(
|
||||||
@@ -235,7 +421,31 @@ class PresensiScreen extends HookConsumerWidget {
|
|||||||
height: Constant.getActualYPhone(context: context, y: 130),
|
height: Constant.getActualYPhone(context: context, y: 130),
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
child: FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
onPressed: () {},
|
onPressed: () async {
|
||||||
|
final T_TransactionM_StaffID =
|
||||||
|
selectedUser?.model.staffId ?? "";
|
||||||
|
final T_TransactionM_CompanyID =
|
||||||
|
selectedUser?.model.companyId ?? "";
|
||||||
|
final T_TransactionCurrentLatitude =
|
||||||
|
positionLatitude.value;
|
||||||
|
final T_TransactionCurrentLongitude =
|
||||||
|
positionLongitude.value;
|
||||||
|
final T_TransactionCurrentDistance =
|
||||||
|
varCurrentDistanceProvider?.currentDistance ?? "";
|
||||||
|
final T_TransactionSelfiePhoto = "";
|
||||||
|
final token = selectedUser?.token ?? "";
|
||||||
|
|
||||||
|
showConfirmationDialog(
|
||||||
|
context,
|
||||||
|
T_TransactionM_StaffID,
|
||||||
|
T_TransactionM_CompanyID,
|
||||||
|
T_TransactionCurrentLatitude,
|
||||||
|
T_TransactionCurrentLongitude,
|
||||||
|
T_TransactionCurrentDistance,
|
||||||
|
T_TransactionSelfiePhoto,
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
},
|
||||||
backgroundColor: Color(0xFFFFFFFF),
|
backgroundColor: Color(0xFFFFFFFF),
|
||||||
shape: CircleBorder(),
|
shape: CircleBorder(),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|||||||
48
lib/screen/presensi/presensi_selfie_screen.dart
Normal file
48
lib/screen/presensi/presensi_selfie_screen.dart
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../app/constant.dart';
|
||||||
|
|
||||||
|
class PresensiSelfieScreen extends StatelessWidget {
|
||||||
|
const PresensiSelfieScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: Constant.getActualYPhone(context: context, y: 30),
|
||||||
|
),
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(
|
||||||
|
// 'Home Screen',
|
||||||
|
'Presensi',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Constant.textBlack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Constant.textWhite,
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: Constant.textBlack,
|
||||||
|
),
|
||||||
|
// elevation: 1.0,
|
||||||
|
elevation: 0.5,
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: Constant.getActualYPhone(context: context, y: 58),
|
||||||
|
left: Constant.getActualXPhone(context: context, x: 33),
|
||||||
|
right: Constant.getActualXPhone(context: context, x: 27),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
width: Constant.getActualXPhone(context: context, x: 390),
|
||||||
|
height: 100,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user