step 9 : add fungsi permission handler, get current latitude, longitude

This commit is contained in:
sindhu
2024-01-26 08:01:33 +07:00
parent b85e3515e8
commit 757b72a9f0
15 changed files with 535 additions and 22 deletions

BIN
images/clockin_presensi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

BIN
images/finger_presensi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

BIN
images/presensi_finger1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
images/presensi_finger2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
images/sync_white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

View File

@@ -19,6 +19,8 @@ class Constant {
static Color textLightGrey = const Color(0xff919EAB);
static Color textOrange = const Color(0xffF15A29);
static Color textDarkGrey = const Color(0xff637381);
static Color bgAddressPresensi = const Color.fromRGBO(241, 90, 41, 0.08);
static Color textWhite = Color(0xffFDFDFD);
// size convertion
static double getActualXPhone({
@@ -57,6 +59,13 @@ class Constant {
);
}
static TextStyle titleH2_400_12({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 12),
fontWeight: FontWeight.w400,
fontFamily: 'Public Sans');
}
static TextStyle titleH2_600_14({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 14),
@@ -79,6 +88,14 @@ class Constant {
);
}
static TextStyle titlePresensiH2_700({required BuildContext context}) {
return TextStyle(
fontFamily: 'Quicksand',
fontSize: Constant.getActualYPhone(context: context, y: 24),
fontWeight: FontWeight.w700,
);
}
static TextStyle time_700({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 28),

View File

@@ -1,4 +1,5 @@
import 'package:absensi_sas_flutter/screen/home/home_screen_v1.dart';
import 'package:absensi_sas_flutter/screen/presensi/presensi_screen.dart';
import 'package:flutter/material.dart';
import '../screen/home/home_screen.dart';
import '../test_flutter_map.dart';
@@ -9,6 +10,7 @@ const loginRoute = "/loginRoute";
const splashRoute = "/splashRoute";
const testFlutterMapRoute = "/testFlutterMapRoute";
const homeRoute = "/homeRoute";
const presensiRoute = "/presensiRoute";
class AppRoute {
static Route<dynamic> generateRoute(RouteSettings settings) {
@@ -35,6 +37,17 @@ class AppRoute {
});
}
// presensi route
if (settings.name == presensiRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context)
.copyWith(textScaleFactor: 1.0, padding: EdgeInsets.all(0)),
child: PresensiScreen(),
);
});
}
// splash screen
if (settings.name == splashRoute) {
return MaterialPageRoute(builder: (context) {

View File

@@ -46,7 +46,7 @@ class MyApp extends StatelessWidget {
),
// home: TestMap(),
// initialRoute: loginRoute,
initialRoute: homeRoute,
initialRoute: presensiRoute,
// initialRoute: testFlutterMapRoute,
onGenerateRoute: AppRoute.generateRoute,
);

View File

@@ -163,27 +163,27 @@ class LoginScreen extends HookConsumerWidget {
SizedBox(
height: Constant.getActualYPhone(context: context, y: 100),
),
// (currentUserGoogle != null)
// ? Container(
// child: ListTile(
// leading: GoogleUserCircleAvatar(
// identity: currentUserGoogle,
// ),
// title: Text(
// currentUserGoogle.displayName ?? "",
// ),
// subtitle: Text(
// currentUserGoogle.email,
// ),
// trailing: IconButton(
// icon: Icon(Icons.logout_outlined),
// onPressed: () async {
// await googleSignIn.disconnect();
// },
// ),
// ),
// )
// :
(currentUserGoogle != null)
? Container(
child: ListTile(
leading: GoogleUserCircleAvatar(
identity: currentUserGoogle,
),
title: Text(
currentUserGoogle.displayName ?? "",
),
subtitle: Text(
currentUserGoogle.email,
),
trailing: IconButton(
icon: Icon(Icons.logout_outlined),
onPressed: () async {
await googleSignIn.disconnect();
},
),
),
)
:
// Logo Landscape
Padding(
padding: EdgeInsets.only(

View File

@@ -0,0 +1,438 @@
import 'package:absensi_sas_flutter/widget/real_date.dart';
import 'package:absensi_sas_flutter/widget/sankbar_widget.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:hooks_riverpod/hooks_riverpod.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../app/constant.dart';
import '../../widget/real_time.dart';
class PresensiScreen extends HookConsumerWidget {
const PresensiScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentAddressUserLocation = useState<String>("");
final isLoadingAddressUserLocation = useState<bool>(false);
Future<void> getAddressFromLocation() async {
try {
isLoadingAddressUserLocation.value = true;
// Mendapatkan posisi pengguna
LocationPermission permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
isLoadingAddressUserLocation.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) {
isLoadingAddressUserLocation.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");
if (address != "") {
currentAddressUserLocation.value = address;
}
} else {
isLoadingAddressUserLocation.value = false;
SanckbarWidget(
context, 'Tidak dapat menemukan alamat.', snackbarType.error);
print("Tidak dapat menemukan alamat.");
}
} catch (e) {
print("Error: $e");
isLoadingAddressUserLocation.value = false;
SanckbarWidget(context, 'Error : $e', snackbarType.error);
}
}
Future<void> _requestLocationPermission() async {
var status = await Permission.location.request();
isLoadingAddressUserLocation.value = true;
if (status.isGranted) {
// Izin diberikan, lanjutkan dengan mendapatkan lokasi
getAddressFromLocation();
} else {
isLoadingAddressUserLocation.value = false;
// Izin ditolak, berikan pemberitahuan atau instruksi
// print('Izin lokasi ditolak');
SanckbarWidget(context, 'Izin Ditolak', snackbarType.error);
}
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
getAddressFromLocation();
});
return () {};
}, []);
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: Constant.getActualYPhone(context: context, y: 844),
height: MediaQuery.of(context).size.height,
child: Column(
children: [
// Spacer(),
// tanggal sekarang
RealTimeFormattedDate(),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 8),
),
// jam sekarang
RealTimeClock(),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 24),
),
// address dan refresh
Container(
width: Constant.getActualXPhone(context: context, x: 350),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Constant.bgAddressPresensi,
),
child: Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(context: context, x: 12),
right:
Constant.getActualXPhone(context: context, x: 12),
top: Constant.getActualYPhone(context: context, y: 12),
bottom:
Constant.getActualYPhone(context: context, y: 12),
),
child: Row(
children: [
// Bagian kiri
Padding(
padding: EdgeInsets.only(left: 12, right: 8),
child: Column(
children: [
Icon(
Icons.location_on_outlined,
color: Constant.textDarkGrey,
),
],
),
),
// Bagian tengah
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Column(
children: [
(isLoadingAddressUserLocation.value)
? Center(
child: CircularProgressIndicator(),
)
: Text(
// 'Perumahan Grand House of Klodran No. 5, Klodran, Kec. Colomadu, Kabupaten Karanganyar, Jawa Tengah 57172',
currentAddressUserLocation.value,
overflow: TextOverflow.ellipsis,
maxLines: 10,
style: Constant.titleH2_400_12(
context: context)
.copyWith(
color: Constant.textDarkGrey,
),
),
],
),
),
),
// Bagian kanan
Padding(
padding: EdgeInsets.only(right: 12),
child: Column(
children: [
InkWell(
onTap: () async {
getAddressFromLocation();
},
child: Container(
width: Constant.getActualXPhone(
context: context, x: 36),
height: Constant.getActualYPhone(
context: context, y: 36),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Constant.textOrange,
),
child: Image.asset(
'images/sync_white.png', // Path gambar untuk "Refresh"
width: Constant.getActualXPhone(
context: context, x: 20),
height: Constant.getActualYPhone(
context: context, y: 20),
),
),
),
],
),
),
],
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 50),
),
// gambar icon presensi
Container(
width: Constant.getActualXPhone(context: context, x: 130),
height: Constant.getActualYPhone(context: context, y: 130),
child: FittedBox(
child: FloatingActionButton(
onPressed: () {},
backgroundColor: Color(0xFFFFFFFF),
shape: CircleBorder(),
child: Container(
width: Constant.getActualXPhone(
context: context, x: 130),
height: Constant.getActualYPhone(
context: context, y: 130),
decoration: BoxDecoration(
image: DecorationImage(
// fit: BoxFit.cover,
image: AssetImage(
'images/presensi_finger2.png'), // Ganti dengan path gambar Anda
),
),
),
),
),
),
// Container(
// width: Constant.getActualXPhone(context: context, x: 100),
// height: Constant.getActualYPhone(context: context, y: 200),
// decoration: BoxDecoration(
// image: DecorationImage(
// fit: BoxFit.cover,
// image: AssetImage(
// 'images/presensi_finger1.png'), // Ganti dengan path gambar Anda
// ),
// ),
// ),
Spacer(),
// Expanded(
// child: SizedBox(),
// ),
// clock in & clock out
Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(context: context, x: 70),
right: Constant.getActualXPhone(context: context, x: 70),
// top:
// Constant.getActualYPhone(context: context, y: 12),
bottom: Constant.getActualYPhone(context: context, y: 55),
),
child: Container(
width: Constant.getActualXPhone(context: context, x: 390),
// color: Colors.amber,
child: Row(
children: [
Column(
children: [
Image.asset(
'images/clockin_presensi.png', // Path gambar untuk "Clock In"
width: Constant.getActualXPhone(
context: context, x: 22),
height: Constant.getActualYPhone(
context: context, y: 22),
),
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 8),
),
Text(
'--:--',
style: Constant.titlePresensiH2_700(
context: context)
.copyWith(
color: Constant.textTrueBlack,
),
),
Text(
'Clock In',
style: Constant.titleH2_700(context: context)
.copyWith(
color: Constant.textTrueBlack,
),
),
],
),
Spacer(),
Column(
children: [
Image.asset(
'images/clockout_presensi.png', // Path gambar untuk "Check In"
width: Constant.getActualXPhone(
context: context, x: 22),
height: Constant.getActualYPhone(
context: context, y: 22),
),
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 8),
),
Text(
'--:--',
style: Constant.titlePresensiH2_700(
context: context)
.copyWith(
color: Constant.textTrueBlack,
),
),
Text(
'Clock Out',
style: Constant.titleH2_700(context: context)
.copyWith(
color: Constant.textTrueBlack,
),
),
],
),
],
),
),
),
],
),
),
),
),
// bottomNavigationBar: Container(
// width: Constant.getActualXPhone(context: context, x: 390),
// height: Constant.getActualYPhone(context: context, y: 84),
// decoration: BoxDecoration(
// color: Color(0xFFFFFFFF),
// boxShadow: [
// BoxShadow(
// offset: Offset(0, -1),
// blurRadius: 8,
// spreadRadius: -8,
// color: Color.fromRGBO(0, 0, 0, 0.10),
// ),
// ],
// ),
// child: Row(
// children: <Widget>[
// Expanded(
// child: Container(
// child: Column(
// children: [
// Image.asset(
// 'images/clockin_presensi.png', // Path gambar untuk "Clock In"
// width: Constant.getActualXPhone(context: context, x: 22),
// height: Constant.getActualYPhone(context: context, y: 22),
// ),
// SizedBox(
// height: Constant.getActualYPhone(context: context, y: 8),
// ),
// Text(
// '--:--',
// style: Constant.titlePresensiH2_700(context: context)
// .copyWith(
// color: Constant.textTrueBlack,
// ),
// ),
// Text(
// 'Clock In',
// style: Constant.titleH2_700(context: context).copyWith(
// color: Constant.textTrueBlack,
// ),
// ),
// ],
// ),
// )),
// Expanded(
// child: Container(
// child: Column(
// children: [
// Image.asset(
// 'images/clockout_presensi.png', // Path gambar untuk "Check In"
// width: Constant.getActualXPhone(context: context, x: 22),
// height: Constant.getActualYPhone(context: context, y: 22),
// ),
// SizedBox(
// height: Constant.getActualYPhone(context: context, y: 8),
// ),
// Text(
// '--:--',
// style: Constant.titlePresensiH2_700(context: context)
// .copyWith(
// color: Constant.textTrueBlack,
// ),
// ),
// Text(
// 'Clock Out',
// style: Constant.titleH2_700(context: context).copyWith(
// color: Constant.textTrueBlack,
// ),
// ),
// ],
// ),
// )),
// ],
// ),
// ),
),
);
}
}

View File

@@ -576,6 +576,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.1"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8"
url: "https://pub.dev"
source: hosted
version: "11.0.1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
url: "https://pub.dev"
source: hosted
version: "11.1.0"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
url: "https://pub.dev"
source: hosted
version: "9.1.4"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
url: "https://pub.dev"
source: hosted
version: "3.12.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
url: "https://pub.dev"
source: hosted
version: "0.1.3"
pigeon:
dependency: transitive
description:

View File

@@ -51,6 +51,7 @@ dependencies:
intl: ^0.17.0
graphql: ^5.1.3
top_snackbar_flutter: ^3.1.0
permission_handler: ^11.0.0
dev_dependencies:
flutter_test:

View File

@@ -7,8 +7,11 @@
#include "generated_plugin_registrant.h"
#include <geolocator_windows/geolocator_windows.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
}

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
geolocator_windows
permission_handler_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST