18 Commits

Author SHA1 Message Date
sindhu
40d6f5ec2f step 18 : perbaikan login pada useeffect dan logout pada mutation 2024-08-29 11:05:18 +07:00
sindhu
5bf87205cc step 17 : fix cors ketika menggunakan base url graphql di server 2024-08-28 17:13:32 +07:00
sindhu
1dd0e61a02 step 16 : get user location google map, refresh location google map absen selfie dan absen normal (bukan selfie) 2024-08-28 16:29:54 +07:00
sindhu
ab9089f8b4 step 15 : buat custom google map widget 2024-08-28 15:46:38 +07:00
sindhu
e06bd02a21 step 14 : custom marker google map 2024-08-28 15:26:42 +07:00
sindhu
6bab2dbef3 step 13 : show google map flutter web & current location function 2024-08-28 12:02:47 +07:00
sindhu
40453ab16b step 12 : ubah dari nomitism ke google apis untuk address 2024-08-28 10:34:27 +07:00
sindhu
102760babc step 11 : get location now using javascript in flutter 2024-08-27 17:58:59 +07:00
sindhu
7bd8527df2 step 10 : clock in & clock out tanpa wajah 2024-08-27 15:40:54 +07:00
sindhu
a7d83956ec step 9 : clock in & clock out selfie sudah bisa, need fix at clear camera controller after take photo 2024-08-27 14:13:00 +07:00
sindhu
3806462d7b step 8 : fix issue camera prevent hardware karena OBS terinstall 2024-08-27 08:48:34 +07:00
sindhu
f85afe7103 step 7 : presensi selfie get address and coordinate fix 2024-08-26 12:39:53 +07:00
sindhu
62409f9feb step 6 : perbaikan login_screen jsonDecode obj and redirect home 2024-08-26 07:46:59 +07:00
sindhu
08dd105fe5 step 5 : perbaikan login logic di login screen 2024-08-25 12:22:15 +07:00
sindhu1993
b4dea334d1 step 4 : setting google sign di platform web 2024-08-23 11:16:20 +07:00
sindhu1993
461fb00aed step 3 : create index.html and backup 2024-08-23 09:07:43 +07:00
sindhu1993
6e15b23ded step 2 : update dependecy 2024-08-23 09:01:06 +07:00
sindhu1993
9a78ec1d92 step 1 : create branch from main, upgrade flutter 3.24.1 2024-08-23 08:22:07 +07:00
31 changed files with 2568 additions and 414 deletions

31
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,31 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "absensi_sas_flutter_web",
"request": "launch",
"type": "dart",
"args": ["--web-port","5000"]
},
{
"name": "absensi_sas_flutter",
"request": "launch",
"type": "dart"
},
{
"name": "absensi_sas_flutter (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "absensi_sas_flutter (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}

BIN
images/custom_marker1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -4,12 +4,26 @@ class Constant {
// static double designHeight = 1024;
// static double designWidth = 1440;
// base url graphql
static String baseURLGraphQl = "http://devone.aplikasi.web.id:3300/query";
// base url graphql web di server
static String baseURLGraphQl = "https://devone.aplikasi.web.id/query";
// base url graphql web pada waktu develop
// static String baseURLGraphQl = "http://devone.aplikasi.web.id:3300/query";
// static String baseURLGraphQl = "http://localhost:8080/query";
static String bearerName = "absensi-sas";
static String accountGoogle = "absensi-google-account";
// api key google map
static String apikeyGoogleMap = "AIzaSyCiN7EeJsUpXVLQKFfrj3sE5OTKebjpzek";
static String baseUrlGoogleMapApis =
"https://maps.googleapis.com/maps/api/geocode/json?";
// position longitude dan latitude awal
static double positionLatitudeAwal = -7.566957;
static double positionLongitudeAwal = 110.8080284;
static double designHeightPhone = 844;
static double designWidthPhone = 390;

View File

@@ -0,0 +1,41 @@
import 'package:dio/dio.dart';
import 'package:latlong2/latlong.dart';
Future<String> searchPosition(LatLng position) async {
const apiKey = "AIzaSyAVUr4Ku4O1HlSkK8n9KGnUyqvsXBL-yfs";
final latitudeString = position.latitude.toString();
final longitudeString = position.longitude.toString();
const url = 'https://maps.googleapis.com/maps/api/geocode/json';
try {
final response = await Dio().get(
url,
queryParameters: {
'latlng': '$latitudeString,$longitudeString',
'key': apiKey,
},
);
if (response.statusCode == 200) {
final data = response.data;
// Mengambil compound_code dari hasil JSON
final plusCode = data['plus_code'];
if (plusCode != null && plusCode['compound_code'] != null) {
return plusCode['compound_code'];
} else {
print('Alamat Tidak Ditemukan');
return "";
}
} else {
print('Failed to load data');
return "";
}
} catch (e) {
print('Error: $e');
return "";
}
}
Future<String> positionToAddressGoogleApis(LatLng position) async {
return await searchPosition(position);
}

View File

@@ -3,6 +3,7 @@ import 'package:absensi_sas/screen/approval_detail/approval_detail_screen.dart';
import 'package:absensi_sas/screen/home/home_screen_v1.dart';
import 'package:absensi_sas/screen/presensi/presensi_screen.dart';
import 'package:absensi_sas/screen/presensi/presensi_selfie_screen.dart';
import 'package:absensi_sas/test_flutter_web_map.dart';
import 'package:flutter/material.dart';
import '../screen/home/home_screen.dart';
import '../test_flutter_map.dart';
@@ -17,9 +18,21 @@ const presensiRoute = "/presensiRoute";
const presensiSelfieRoute = "/presensiSelfieRoute";
const approvalRoute = "/approvalRoute";
const approvalDetailRoute = "/approvalDetailRoute";
const testFlutterWebMapRoute = "/testFlutterWebMapRoute";
class AppRoute {
static Route<dynamic> generateRoute(RouteSettings settings) {
// flutter web map
if (settings.name == testFlutterWebMapRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context)
.copyWith(textScaleFactor: 1.0, padding: EdgeInsets.all(0)),
child: TestFlutterWebMap(),
);
});
}
// test flutter map
if (settings.name == testFlutterMapRoute) {
return MaterialPageRoute(builder: (context) {

View File

@@ -1,9 +1,12 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/test_flutter_web_map.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../app/route.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'test_map_x.dart';
// import '../test_map.dart';
// final routerProvider = Provider((_) => GlobalKey<NavigatorState>());
@@ -63,7 +66,8 @@ class MyApp extends StatelessWidget {
// home: TestMap(),
initialRoute: loginRoute,
// initialRoute: presensiSelfieRoute,
// home: TestMapFlutterWeb(),
// home:TestMapX(),
// initialRoute: testFlutterMapRoute,
onGenerateRoute: AppRoute.generateRoute,
);

View File

@@ -0,0 +1,68 @@
import 'dart:convert';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
final imgPhotoWebProvider = StateProvider<String?>((ref) => "");
final cameraControllerProvider =
StateNotifierProvider<CameraControllerNotifier, CameraController?>((ref) {
return CameraControllerNotifier();
});
// StateNotifier to manage CameraController
class CameraControllerNotifier extends StateNotifier<CameraController?> {
CameraControllerNotifier() : super(null) {
_initializeCamera();
}
Future<void> _initializeCamera() async {
WidgetsFlutterBinding.ensureInitialized();
try {
final cameras = await availableCameras();
if (cameras.isNotEmpty) {
final controller = CameraController(cameras[0], ResolutionPreset.max);
await controller.initialize();
state = controller;
}
} catch (e) {
print('Error initializing camera: $e');
}
}
Future<void> takePicture(
// ValueNotifier<XFile?> imageFile,
ValueNotifier<String> base64ImageString,
ValueNotifier<String> imagePath,
) async {
// final flag = ValueNotifier<bool>(false);
if (state != null) {
try {
final image = await state!.takePicture();
final bytes = await image.readAsBytes();
String base64Image = base64Encode(bytes);
// imageFile.value = image;
base64ImageString.value = base64Image;
imagePath.value = image.path;
// final shared = await SharedPreferences.getInstance();
// shared.setString("base64Image", base64Image);
} catch (e) {
print('Error taking picture: $e');
// flag.value = false;
// RespErr(flag: false, message: 'Error taking picture: $e');
}
} else {
print('CameraController is not initialized.');
}
}
@override
void dispose() {
state?.dispose();
super.dispose();
}
}

View File

@@ -1,8 +1,12 @@
import 'package:google_sign_in/google_sign_in.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
const String googleClientID =
"856240587825-klh0dfjc44bovajg1rpq5vbvs4g7rh5j.apps.googleusercontent.com";
final googleSignInProvider = StateProvider<GoogleSignIn>((ref) {
return GoogleSignIn(
clientId: googleClientID,
scopes: [
'email',
'https://www.googleapis.com/auth/contacts.readonly',
@@ -10,6 +14,10 @@ final googleSignInProvider = StateProvider<GoogleSignIn>((ref) {
);
});
final currentUserGoogleProvider = StateProvider<GoogleSignInAccount?>((ref) => null);
final currentUserGoogleProvider =
StateProvider<GoogleSignInAccount?>((ref) => null);
final isNotifyFromLogout = StateProvider<bool>((ref) => false);
final isNotifyFromLogout = StateProvider<bool>((ref) => false);
final currentLatitudeProvider = StateProvider<double>((ref) => 0.0);
final currentLongitudeProvider = StateProvider<double>((ref) => 0.0);

View File

@@ -0,0 +1,10 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
final locationProvider = StateProvider<Map<String, dynamic>>((ref) {
return {
'status': null,
'message': null,
'latitude': null,
'longitude': null,
};
});

View File

@@ -36,7 +36,7 @@ class AuthRepository extends BaseRepository {
Future<LogoutResponseModel> logout(
String email, String idGoogleSignIn) async {
const String query =
r'''mutation($emailParam:String!, $id_google_sign_in_Param:String!){ logoutAttendance(email:$emailParam, id_google_sign_in:$id_google_sign_in_Param){ staff_id nip name email phone_number token id_google_sign_in company_id company_name } }''';
r'''mutation($emailParam:String!, $id_google_sign_in_Param:String!){ logoutAttendance(email:$emailParam, id_google_sign_in:$id_google_sign_in_Param){ status message } }''';
Map<String, dynamic> inpVariables = {
"emailParam": email,

View File

@@ -0,0 +1,45 @@
import 'package:dio/dio.dart';
import 'base_repository.dart';
class GoogleApisRepository extends BaseRepository {
GoogleApisRepository({required super.graphql, required super.dio});
Future<String> getAddressFromCoordinates(
String latitude, String longitude) async {
final dio = Dio();
const apiKey = "AIzaSyAVUr4Ku4O1HlSkK8n9KGnUyqvsXBL-yfs";
final latitudeString = latitude.toString();
final longitudeString = longitude.toString();
const url =
'https://maps.googleapis.com/maps/api/geocode/json';
try {
final response = await dio.get(
url,
queryParameters: {
'latlng': '$latitudeString,$longitudeString',
'key': apiKey,
},
);
if (response.statusCode == 200) {
final data = response.data;
// Mengambil compound_code dari hasil JSON
final plusCode = data['plus_code'];
if (plusCode != null && plusCode['compound_code'] != null) {
return plusCode['compound_code'];
} else {
print('Alamat Tidak Ditemukan');
return "";
}
} else {
print('Failed to load data');
return "";
}
} catch (e) {
print('Error: $e');
return "";
}
}
}

View File

@@ -0,0 +1,75 @@
import 'package:absensi_sas/repository/googleapis_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../provider/dio_provider.dart';
import '../../provider/graphql_provider.dart';
import '../../repository/base_repository.dart';
abstract class GoogleApisProviderState extends Equatable {
final DateTime date;
const GoogleApisProviderState(this.date);
@override
List<Object?> get props => [date];
}
class GoogleApisProviderStateInit extends GoogleApisProviderState {
GoogleApisProviderStateInit() : super(DateTime.now());
}
class GoogleApisProviderStateLoading extends GoogleApisProviderState {
GoogleApisProviderStateLoading() : super(DateTime.now());
}
class GoogleApisProviderStateError extends GoogleApisProviderState {
final String message;
GoogleApisProviderStateError({
required this.message,
}) : super(DateTime.now());
}
class GoogleApisProviderStateDone extends GoogleApisProviderState {
final String model;
GoogleApisProviderStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class GoogleApisProviderNotifier extends StateNotifier<GoogleApisProviderState> {
final Ref ref;
GoogleApisProviderNotifier({
required this.ref,
}) : super(GoogleApisProviderStateInit());
void googleApisProvider(
String latitude,
String longitude,
) async {
try {
state = GoogleApisProviderStateLoading();
final graphql = ref.read(graphqlProvider(''));
final dio = ref.read(dioProvider);
final resp = await GoogleApisRepository(graphql: graphql, dio: dio)
.getAddressFromCoordinates(
latitude,
longitude
);
state = GoogleApisProviderStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
print(e.message);
state = GoogleApisProviderStateError(message: e.message ?? "");
} else {
state = GoogleApisProviderStateError(message: e.toString());
}
}
}
}
// provider
final googleApisProviderProvider =
StateNotifierProvider<GoogleApisProviderNotifier, GoogleApisProviderState>(
(ref) => GoogleApisProviderNotifier(ref: ref),
);

View File

@@ -1,14 +1,19 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/provider/current_rekap_kehadiran_home_provider.dart';
import 'package:absensi_sas/widget/custom_google_map_widget.dart';
import 'package:dart_nominatim/dart_nominatim.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: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:permission_handler/permission_handler.dart';
import 'package:latlong2/latlong.dart';
import 'package:location/location.dart';
// import 'package:permission_handler/permission_handler.dart';
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import '../../app/googleapis_location.dart';
import '../../app/route.dart';
import '../../provider/current_check_distance_provider.dart';
import '../../provider/current_check_jam_presensi_provider.dart';
@@ -42,6 +47,8 @@ class HomeScreen extends HookConsumerWidget {
// ref.watch(currentUserGoogleProvider);
final googleSignIn = ref.watch(googleSignInProvider);
Location location = new Location();
LocationData _locationData;
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
@@ -95,9 +102,10 @@ class HomeScreen extends HookConsumerWidget {
try {
isLoadingProsesCheckDistance.value = true;
// Mendapatkan posisi pengguna
LocationPermission permission = await Geolocator.requestPermission();
// LocationPermission permission = await Geolocator.requestPermission();
final permission = await location.hasPermission();
if (permission == LocationPermission.denied) {
if (permission == PermissionStatus.denied) {
isLoadingProsesCheckDistance.value = false;
SanckbarWidget(context, 'Izin lokasi ditolak', snackbarType.error);
// Handle jika pengguna menolak izin lokasi
@@ -105,25 +113,29 @@ class HomeScreen extends HookConsumerWidget {
return;
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
// Position position = await Geolocator.getCurrentPosition(
// desiredAccuracy: LocationAccuracy.high);
location.changeSettings(
accuracy: LocationAccuracy.high,
interval: 1000,
distanceFilter: 5,
);
_locationData = await location.getLocation();
// positionLatitude.value = position.latitude.toString();
// positionLongitude.value = position.longitude.toString();
positionLatitude.value = _locationData.latitude.toString();
positionLongitude.value = _locationData.longitude.toString();
// Mendapatkan alamat dari posisi
List<Placemark> placemarks = await placemarkFromCoordinates(
position.latitude, position.longitude);
final address = await positionToAddressGoogleApis(
LatLng(_locationData.latitude!, _locationData.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();
if (address != "") {
positionLatitude.value = _locationData.latitude.toString();
positionLongitude.value = _locationData.longitude.toString();
// panggil check distance provider
ref.read(checkDistanceProvider.notifier).checkDistance(
@@ -146,10 +158,12 @@ class HomeScreen extends HookConsumerWidget {
}
Future<void> requestLocationPermission() async {
var status = await Permission.location.request();
// var status = await Permission.location.request();
final status = await location.serviceEnabled();
isLoadingProsesCheckDistance.value = true;
if (status.isGranted) {
if (status) {
// Izin diberikan, lanjutkan dengan mendapatkan lokasi
// print('izin diberikan');
getAddressFromLocation();
} else {
isLoadingProsesCheckDistance.value = false;
@@ -309,7 +323,16 @@ class HomeScreen extends HookConsumerWidget {
)
: FloatingActionButton(
onPressed: () async {
await requestLocationPermission();
// await requestLocationPermission();
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => CustomGoogleMapWidget(
positionLastLatitudeParam: positionLatitude,
positionLastLongitudeParam: positionLongitude,
),
),
);
},
backgroundColor: Color(0xFFFFFFFF),
shape: CircleBorder(),

View File

@@ -1,7 +1,7 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:geocoding/geocoding.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';
@@ -89,39 +89,39 @@ class HomeScreenV1 extends HookConsumerWidget {
return;
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
// Position position = await Geolocator.getCurrentPosition(
// desiredAccuracy: LocationAccuracy.high);
// Mendapatkan alamat dari posisi
List<Placemark> placemarks = await placemarkFromCoordinates(
position.latitude, position.longitude);
// // 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},";
// 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");
// String address =
// "${placemark.street}, ${placemark.subLocality}, ${placemark.subAdministrativeArea}, ${placemark.postalCode}";
// print("Alamat: $address");
positionLatitude.value = position.latitude.toString();
positionLongitude.value = position.longitude.toString();
// 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.");
}
// // 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;

View File

@@ -43,20 +43,27 @@ class LoginScreen extends HookConsumerWidget {
final authModel = AuthModel(
token: xmodel["token"],
model: StaffModel(
companyId: xmodel["model"]["companyId"],
companyName: xmodel["model"]["companyName"],
companyId: xmodel["model"]["company_id"],
companyName: xmodel["model"]["company_name"],
email: xmodel["model"]["email"],
staffId: xmodel["model"]["staffId"],
idGoogleSignIn: xmodel['model']['idGoogleSignIn'],
staffId: xmodel["model"]["staff_id"],
idGoogleSignIn: xmodel['model']['id_google_sign_in'],
name: xmodel['model']['name'],
nip: xmodel['model']['nip'],
phoneNumber: xmodel["model"]["phoneNumber"],
phoneNumber: xmodel["model"]["phone_number"],
token: xmodel["model"]["token"],
),
);
// ref.read(currentUserProvider.notifier).state = authModel;
// NEW
// if (xmodel != null) {
// ref.read(loginProvider.notifier).login(
// authModel.model.email ?? "",
// authModel.model.idGoogleSignIn ?? "",
// );
// }
// OLD
await googleSignIn.signInSilently();
googleSignIn.onCurrentUserChanged.listen((account) {
// ref
@@ -140,7 +147,7 @@ class LoginScreen extends HookConsumerWidget {
});
googleSignIn.signInSilently();
// kalau sudah pernah login
// kalau sudah pernah login
if (googleSignIn.currentUser?.email != null) {
googleSignIn.signInSilently();
// ref.read(currentUserGoogleProvider.notifier).update(

View File

@@ -0,0 +1,356 @@
import 'dart:convert';
import 'package:absensi_sas/screen/login/login_provider.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../app/constant.dart';
import '../../app/route.dart';
import '../../model/auth_model.dart';
import '../../provider/current_menu_provider.dart';
import '../../provider/current_user_provider.dart';
import '../../provider/google_login_provider.dart';
import '../../widget/sankbar_widget.dart';
class LoginScreenV1 extends HookConsumerWidget {
const LoginScreenV1({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// inisialisasi
// ref.watch itu sama spt memantau state terus menerus
// ref.read itu memantau namun hny 1x saja
// GoogleSignInAccount? currentUserGoogle =
// ref.watch(currentUserGoogleProvider);
final googleSignIn = ref.watch(googleSignInProvider);
final isLoading = useState(false);
// final errorMessage = useState("");
final isSuccess = useState(false);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
// final staffID = ref.read(currentUserProvider)?.model.staffId ?? "0";
final shared = await SharedPreferences.getInstance();
final bearerString = shared.get(Constant.bearerName).toString();
final xmodel = jsonDecode(bearerString);
if (xmodel == null) return;
final authModel = AuthModel(
token: xmodel["token"],
model: StaffModel(
companyId: xmodel["model"]["companyId"],
companyName: xmodel["model"]["companyName"],
email: xmodel["model"]["email"],
staffId: xmodel["model"]["staffId"],
idGoogleSignIn: xmodel['model']['idGoogleSignIn'],
name: xmodel['model']['name'],
nip: xmodel['model']['nip'],
phoneNumber: xmodel["model"]["phoneNumber"],
token: xmodel["model"]["token"],
),
);
// ref.read(currentUserProvider.notifier).state = authModel;
await googleSignIn.signInSilently();
googleSignIn.onCurrentUserChanged.listen((account) {
// ref
// .read(currentUserGoogleProvider.notifier)
// .update((state) => account);
ref.read(currentUserProvider.notifier).state = authModel;
ref.read(currentPageProvider.state).update((state) => 0);
if (account != null &&
ref.read(currentUserProvider)?.model.staffId == "0") {
// Lakukan login
ref.read(loginProvider.notifier).login(
account.email,
account.id,
);
}
});
// kalau sudah pernah login
if (googleSignIn.currentUser?.email != null) {
googleSignIn.signInSilently();
// ref.read(currentUserGoogleProvider.notifier).update(
// (state) => googleSignIn.currentUser,
// );
ref.read(loginProvider.notifier).login(
googleSignIn.currentUser?.email ?? "",
googleSignIn.currentUser?.id ?? "",
);
}
// ref.read(currentUserProvider.notifier).state = authModel;
// Navigator.of(context).pushNamedAndRemoveUntil(
// homeRoute,
// (route) => false,
// );
});
return () {};
}, []);
// LISTEN PROVIDER
ref.listen(loginProvider, (prev, next) {
if (next is LoginStateLoading) {
isLoading.value = true;
} else if (next is LoginStateError) {
isLoading.value = false;
// errorMessage.value = next.message;
// Timer(const Duration(seconds: 3), () {
// errorMessage.value = "";
// });
SanckbarWidget(context, next.message, snackbarType.warning);
} else if (next is LoginStateDone) {
print("Login Done");
isLoading.value = false;
isSuccess.value = true;
ref.read(currentPageProvider.state).update((state) => 0);
Navigator.of(context)
.pushNamedAndRemoveUntil(homeRoute, (route) => false);
}
});
// fungsi untuk sync ke google mail
Future<void> handleSignInGmail() async {
try {
// auth ke email
await googleSignIn.signIn();
googleSignIn.onCurrentUserChanged.listen((account) async {
// ref
// .read(currentUserGoogleProvider.notifier)
// .update((state) => account);
// Check jika account tidak null dan belum login
if (account != null &&
ref.read(currentUserProvider)?.model.staffId == "0") {
// Lakukan login
ref.read(loginProvider.notifier).login(
account.email,
account.id,
);
}
});
googleSignIn.signInSilently();
// kalau sudah pernah login
if (googleSignIn.currentUser?.email != null) {
googleSignIn.signInSilently();
// ref.read(currentUserGoogleProvider.notifier).update(
// (state) => googleSignIn.currentUser,
// );
ref.read(loginProvider.notifier).login(
googleSignIn.currentUser?.email ?? "",
googleSignIn.currentUser?.id ?? "",
);
}
// Navigator.of(context)
// .pushNamedAndRemoveUntil(homeRoute, (route) => false);
} catch (error) {
if (kDebugMode) {
print(error);
}
}
}
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
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();
// },
// ),
// ),
// )
// :
// Logo Landscape
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 120),
left: Constant.getActualXPhone(context: context, x: 91),
right: Constant.getActualXPhone(context: context, x: 90),
// bottom: Constant.getActualYPhone(context: context, y: y)
),
child: Container(
width: Constant.getActualXPhone(context: context, x: 209),
height: Constant.getActualYPhone(context: context, y: 70),
decoration: BoxDecoration(
// color: Colors.green,
image: DecorationImage(
// fit: BoxFit.contain,
image: AssetImage('images/logo_sismedika_landscape.png'),
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 100),
),
// title
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Selamat Datang',
style: Constant.titleH1_700(context: context)
.copyWith(color: Constant.textBlack),
),
Container(
width: Constant.getActualXPhone(context: context, x: 24),
height: Constant.getActualYPhone(context: context, y: 24),
// color: Colors.redAccent,
decoration: BoxDecoration(
// color: Colors.green,
image: DecorationImage(
// fit: BoxFit.contain,
image: AssetImage('images/emoji_handshake.png'),
),
),
),
],
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 7),
),
// title grey
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Silahkan masuk untuk mengakses akun Anda',
style: Constant.titleH2_600(context: context).copyWith(
color: Constant.textLightGrey,
),
),
],
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
// ElevatedButton(
// onPressed: () async {
// await googleSignIn.disconnect();
// },
// child: Text('logout'),
// ),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 64),
),
// button login
Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(context: context, x: 23),
right: Constant.getActualXPhone(context: context, x: 23),
),
child: SizedBox(
width: Constant.getActualXPhone(context: context, x: 344),
height: Constant.getActualYPhone(context: context, y: 48),
child: ElevatedButton(
onPressed: () async {
await handleSignInGmail();
},
child: Stack(
children: [
(isLoading.value)
? SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 24),
child: Center(
child: CircularProgressIndicator(
color: Constant.textTrueBlack,
),
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 24),
decoration: BoxDecoration(
// 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(
backgroundColor: MaterialStateColor.resolveWith(
(st) => Colors.white,
),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
side: BorderSide(
color: Color.fromRGBO(145, 158, 171, 0.32),
),
),
),
),
),
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,225 @@
import 'dart:convert';
import 'dart:io';
import 'package:absensi_sas/widget/sankbar_widget.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../app/constant.dart';
import '../../provider/camera_controller_provider.dart';
class CameraPage extends HookConsumerWidget {
const CameraPage({
Key? key,
required this.filePathParam,
required this.fileDataBase64Param,
}) : super(key: key);
final ValueNotifier<String> filePathParam;
final ValueNotifier<String> fileDataBase64Param;
@override
Widget build(BuildContext context, WidgetRef ref) {
final controller = ref.watch(cameraControllerProvider);
// final imagePath = useState<String?>("");
// final fileDataBase64 = useState<String?>("");
// final fileData = useState<XFile?>(null);
final filePath = useState<String>("");
final fileDataBase64 = useState<String>("");
final cameraController = useState<CameraController?>(null);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
try {
final cameras = await availableCameras();
if (cameras.isNotEmpty) {
final controller =
CameraController(cameras[0], ResolutionPreset.max);
await controller.initialize();
cameraController.value = controller;
}
} catch (e) {
print('Error initializing camera: $e');
}
});
return () {};
}, []);
Future<void> initializeCamera() async {
try {
final cameras = await availableCameras();
if (cameras.isNotEmpty) {
final controller = CameraController(cameras[0], ResolutionPreset.max);
await controller.initialize();
cameraController.value = controller;
}
} catch (e) {
print('Error initializing camera: $e');
}
}
Future<void> takePicture(
// ValueNotifier<XFile?> imageFile,
ValueNotifier<String> base64ImageString,
ValueNotifier<String> imagePath,
) async {
// final flag = ValueNotifier<bool>(false);
if (cameraController.value != null) {
try {
final image = await cameraController.value!.takePicture();
final bytes = await image.readAsBytes();
String base64Image = base64Encode(bytes);
// imageFile.value = image;
base64ImageString.value = base64Image;
imagePath.value = image.path;
fileDataBase64Param.value = base64Image;
filePathParam.value = image.path;
// final shared = await SharedPreferences.getInstance();
// shared.setString("base64Image", base64Image);
} catch (e) {
print('Error taking picture: $e');
// flag.value = false;
// RespErr(flag: false, message: 'Error taking picture: $e');
}
} else {
print('CameraController is not initialized.');
}
}
processFoto(BuildContext context) async {
await takePicture(
// fileData,
fileDataBase64,
filePath,
);
print("fileDataBase64 : ${fileDataBase64.value}");
if (fileDataBase64.value != "") {
// ref.read(imgPhotoWebProvider.notifier).state = fileDataBase64.value;
Navigator.of(context).pop();
} else {
print('Error: Image result is null');
}
// proses base64
// final shared = await SharedPreferences.getInstance();
// final imageBase64 = shared.getString("base64Image");
// if (imageBase64 != "") {
// fileDataBase64.value = imageBase64;
// ref.read(imgPhotoWebProvider.notifier).state = imageBase64;
// Navigator.of(context).pop();
// } else {
// print('Error: Image result is null');
// }
}
return Scaffold(
appBar: AppBar(
title: const Text('Take Photo'),
),
body: SafeArea(
child: Center(
child: controller == null || !controller.value.isInitialized
? const CircularProgressIndicator()
: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
// const SizedBox(height: 50),
Expanded(
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: CameraPreview(controller),
),
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
TextButton(
onPressed: () async {
fileDataBase64Param.value = "";
filePathParam.value = "";
// cameraController.dispose();
// await controller.dispose();
// await initializeCamera();
},
child: const Text("Clear Foto"),
),
Spacer(),
ElevatedButton(
// onPressed: () {
// Navigator.of(context).pushNamed(homeRoute);
// },
onPressed: () async {
processFoto(context);
},
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(st) => (fileDataBase64.value != "")
? Constant.textDarkGrey
: 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),
),
child: Stack(
children: [
(fileDataBase64.value != "")
? SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 32),
child: Center(
child: CircularProgressIndicator(
color: Constant.textOrange,
),
),
)
: Align(
alignment: Alignment.center,
child: Text(
'Process Photo',
style: Constant.titleH1_500_18(
context: context)
.copyWith(
color: Constant.textWhite,
),
),
),
],
),
),
// if (imagePath.value.isNotEmpty)
// Container(
// width: 300,
// height: 300,
// child: Image.network(imagePath.value),
// ),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,92 @@
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
class CameraPageV1 extends StatefulWidget {
const CameraPageV1({Key? key}) : super(key: key);
@override
State<CameraPageV1> createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPageV1> {
CameraController? controller;
String imagePath = "";
List<CameraDescription>? cameras;
@override
void initState() {
super.initState();
initializeCamera();
}
Future<void> initializeCamera() async {
// Initialize cameras
WidgetsFlutterBinding.ensureInitialized();
try {
cameras = await availableCameras();
if (cameras != null && cameras!.isNotEmpty) {
// Use the first available camera
controller = CameraController(cameras![0], ResolutionPreset.max);
await controller?.initialize();
if (mounted) {
setState(() {});
}
}
} catch (e) {
print('Error initializing camera: $e');
}
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (controller == null || !controller!.value.isInitialized) {
return Center(child: CircularProgressIndicator());
}
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
children: [
SizedBox(height: 50),
Container(
width: 200,
height: 200,
child: AspectRatio(
aspectRatio: controller!.value.aspectRatio,
child: CameraPreview(controller!),
),
),
TextButton(
onPressed: () async {
try {
final image = await controller!.takePicture();
setState(() {
imagePath = image.path;
});
} catch (e) {
print('Error taking picture: $e');
}
},
child: Text("Take Photo"),
),
if (imagePath.isNotEmpty)
Container(
width: 300,
height: 300,
child: Image.file(File(imagePath)),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,69 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../provider/dio_provider.dart';
import '../../provider/graphql_provider.dart';
import '../../repository/base_repository.dart';
import '../../repository/googleapis_repository.dart';
// 3. state provider
final googleApisProvider =
StateNotifierProvider<GoogleApisNotifier, GoogleApisState>(
(ref) => GoogleApisNotifier(ref: ref));
// 2. notifier
class GoogleApisNotifier extends StateNotifier<GoogleApisState> {
final Ref ref;
GoogleApisNotifier({required this.ref}) : super(GoogleApisStateInit());
void getAddressGoogleApis(
{required String latitude, required String longitude}) async {
try {
state = GoogleApisStateLoading();
final graphql = ref.read(graphqlProvider(''));
final dio = ref.read(dioProvider);
final resp = await GoogleApisRepository(graphql: graphql, dio: dio).getAddressFromCoordinates(
latitude,
longitude,
);
// print(resp);
state = GoogleApisStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = GoogleApisStateError(message: e.message ?? "");
} else {
state = GoogleApisStateError(message: e.toString());
}
}
}
}
// 1. state
abstract class GoogleApisState extends Equatable {
final DateTime date;
const GoogleApisState(this.date);
@override
List<Object?> get props => [date];
}
class GoogleApisStateInit extends GoogleApisState {
GoogleApisStateInit() : super(DateTime.now());
}
class GoogleApisStateLoading extends GoogleApisState {
GoogleApisStateLoading() : super(DateTime.now());
}
class GoogleApisStateError extends GoogleApisState {
final String message;
GoogleApisStateError({
required this.message,
}) : super(DateTime.now());
}
class GoogleApisStateDone extends GoogleApisState {
final String? model;
GoogleApisStateDone({
required this.model,
}) : super(DateTime.now());
}

View File

@@ -1,25 +1,46 @@
import 'dart:convert';
import 'package:absensi_sas/provider/current_check_jam_presensi_provider.dart';
import 'package:absensi_sas/screen/presensi/check_distance_provider.dart';
import 'package:absensi_sas/screen/presensi/check_presensi_jam_provider.dart';
import 'package:absensi_sas/app/googleapis_location.dart';
import 'package:camera/camera.dart';
import 'package:camera_web/camera_web.dart';
import 'dart:io';
import 'package:absensi_sas/repository/googleapis_repository.dart';
import 'package:absensi_sas/screen/presensi/presensi_clock_in_provider.dart';
import 'package:absensi_sas/widget/real_date.dart';
import 'package:absensi_sas/widget/sankbar_widget.dart';
import 'package:absensi_sas/screen/presensi/presensi_clock_out_provider.dart';
import 'package:absensi_sas/widget/custom_drawer.dart';
import 'package:dart_nominatim/dart_nominatim.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.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:flutter_image_compress/flutter_image_compress.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 'package:image_picker/image_picker.dart';
import 'package:image_picker_web/image_picker_web.dart';
import 'package:latlong2/latlong.dart';
import 'package:location/location.dart';
import 'package:mobkit_dashed_border/mobkit_dashed_border.dart';
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:permission_handler/permission_handler.dart';
import '../../app/constant.dart';
import '../../app/route.dart';
import '../../provider/camera_controller_provider.dart';
import '../../provider/current_check_distance_provider.dart';
import '../../provider/current_check_jam_presensi_provider.dart';
import '../../provider/current_user_provider.dart';
import '../../widget/custom_drawer.dart';
import '../../provider/google_login_provider.dart';
import '../../widget/custom_dialog_presensi_selfie_sukses.dart';
import '../../widget/custom_google_map_widget.dart';
import '../../widget/real_date.dart';
import '../../widget/real_time.dart';
import 'presensi_clock_out_provider.dart';
import '../../widget/sankbar_widget.dart';
import 'camera_page.dart';
import 'check_distance_provider.dart';
import 'check_presensi_jam_provider.dart';
import 'googleapis_provider.dart';
import 'dart:io' as io;
class PresensiScreen extends HookConsumerWidget {
const PresensiScreen({super.key});
@@ -36,15 +57,32 @@ class PresensiScreen extends HookConsumerWidget {
final positionLatitude = useState<String>("");
final positionLongitude = useState<String>("");
final currLatProvider = ref.read(currentLatitudeProvider);
final currLongProvider = ref.read(currentLongitudeProvider);
// final tTransactionCurrentDistance = useState<String>("NULL");
Location location = new Location();
LocationData _locationData;
Future<void> getAddressFromLocation() async {
try {
isLoadingAddressUserLocation.value = true;
// Mendapatkan posisi pengguna
LocationPermission permission = await Geolocator.requestPermission();
// LocationPermission permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// 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;
// }
final permission = await location.hasPermission();
if (permission == PermissionStatus.denied) {
isLoadingAddressUserLocation.value = false;
SanckbarWidget(context, 'Izin lokasi ditolak', snackbarType.error);
// Handle jika pengguna menolak izin lokasi
@@ -52,30 +90,45 @@ class PresensiScreen extends HookConsumerWidget {
return;
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
// Position position = await Geolocator.getCurrentPosition(
// desiredAccuracy: LocationAccuracy.high);
location.changeSettings(
accuracy: LocationAccuracy.high,
interval: 1000,
distanceFilter: 5,
);
_locationData = await location.getLocation();
// Mendapatkan alamat dari posisi
List<Placemark> placemarks = await placemarkFromCoordinates(
position.latitude, position.longitude);
// final address = await positionToAddressGoogleApis(
// LatLng(_locationData.latitude!, _locationData.longitude!));
final address = await positionToAddressGoogleApis(
LatLng(currLatProvider, currLongProvider));
if (positionLongitude.value.isEmpty && positionLatitude.value.isEmpty) {
if (placemarks.isNotEmpty) {
if (address != "") {
isLoadingAddressUserLocation.value = false;
Placemark placemark = placemarks.first;
// ref.read(googleApisProvider.notifier).getAddressGoogleApis(
// latitude: _locationData.latitude.toString(),
// longitude: _locationData.longitude.toString(),
// );
ref.read(googleApisProvider.notifier).getAddressGoogleApis(
latitude: currLatProvider.toString(),
longitude: currLongProvider.toString(),
);
// 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 = _locationData.latitude.toString();
// positionLongitude.value = _locationData.longitude.toString();
positionLatitude.value = position.latitude.toString();
positionLongitude.value = position.longitude.toString();
if (address != "") {
currentAddressUserLocation.value = address;
}
positionLatitude.value = currLatProvider.toString();
positionLongitude.value = currLongProvider.toString();
} else {
isLoadingAddressUserLocation.value = false;
SanckbarWidget(
@@ -105,9 +158,10 @@ class PresensiScreen extends HookConsumerWidget {
}
Future<void> requestLocationPermission() async {
var status = await Permission.location.request();
// var status = await Permission.location.request();
final status = await location.serviceEnabled();
isLoadingAddressUserLocation.value = true;
if (status.isGranted) {
if (status) {
// Izin diberikan, lanjutkan dengan mendapatkan lokasi
getAddressFromLocation();
} else {
@@ -118,6 +172,19 @@ class PresensiScreen extends HookConsumerWidget {
}
}
// googleApis address
ref.listen(googleApisProvider, (prev, next) {
if (next is GoogleApisStateLoading) {
isLoadingProsesCheckDistance.value = true;
} else if (next is GoogleApisStateError) {
isLoadingProsesCheckDistance.value = false;
SanckbarWidget(context, next.message, snackbarType.warning);
} else if (next is GoogleApisStateDone) {
isLoadingProsesCheckDistance.value = false;
currentAddressUserLocation.value = next.model ?? "0";
}
});
// check distance provider
ref.listen(checkDistanceProvider, (prev, next) {
print('status check distance ' + next.toString());
@@ -461,20 +528,34 @@ class PresensiScreen extends HookConsumerWidget {
children: [
InkWell(
onTap: () async {
getAddressFromLocation();
// getAddressFromLocation();
// refresh location
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => CustomGoogleMapWidget(
positionLastLatitudeParam:
positionLatitude,
positionLastLongitudeParam:
positionLongitude,
),
),
);
// 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,
);
}
// 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(
width: Constant.getActualXPhone(

View File

@@ -1,31 +1,45 @@
import 'dart:convert';
import 'package:camera/camera.dart';
import 'package:camera_web/camera_web.dart';
import 'dart:io';
import 'package:absensi_sas/repository/googleapis_repository.dart';
import 'package:absensi_sas/screen/presensi/presensi_clock_in_provider.dart';
import 'package:absensi_sas/screen/presensi/presensi_clock_out_provider.dart';
import 'package:absensi_sas/widget/custom_drawer.dart';
import 'package:dart_nominatim/dart_nominatim.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.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:flutter_image_compress/flutter_image_compress.dart';
// import 'package:geocoding/geocoding.dart';
// import 'package:geolocator/geolocator.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_picker_web/image_picker_web.dart';
import 'package:latlong2/latlong.dart';
import 'package:location/location.dart';
import 'package:mobkit_dashed_border/mobkit_dashed_border.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:permission_handler/permission_handler.dart';
import '../../app/constant.dart';
import '../../app/googleapis_location.dart';
import '../../app/route.dart';
import '../../provider/camera_controller_provider.dart';
import '../../provider/current_check_distance_provider.dart';
import '../../provider/current_check_jam_presensi_provider.dart';
import '../../provider/current_user_provider.dart';
import '../../provider/google_login_provider.dart';
import '../../widget/custom_dialog_presensi_selfie_sukses.dart';
import '../../widget/custom_google_map_widget.dart';
import '../../widget/real_date.dart';
import '../../widget/real_time.dart';
import '../../widget/sankbar_widget.dart';
import 'camera_page.dart';
import 'check_distance_provider.dart';
import 'check_presensi_jam_provider.dart';
import 'presensi_selfie_upload_area.dart';
import 'googleapis_provider.dart';
import 'dart:io' as io;
class PresensiSelfieScreen extends HookConsumerWidget {
@@ -41,17 +55,25 @@ class PresensiSelfieScreen extends HookConsumerWidget {
ref.watch(currentCheckJamPresensiProvider);
final selectedUser = ref.watch(currentUserProvider);
final currLatProvider = ref.read(currentLatitudeProvider);
final currLongProvider = ref.read(currentLongitudeProvider);
final positionLatitude = useState<String>("");
final positionLongitude = useState<String>("");
// final tTransactionCurrentDistance = useState<String>("NULL");
final fileData = useState<XFile?>(null);
final fileDataBase64 = useState<String>("");
final filePath = useState<String>("");
final isImage = useState(false);
final fileEkstension = useState("");
final fileSize = useState(0);
final fileName = useState("");
Location location = new Location();
LocationData _locationData;
// final imgPhotoWeb = ref.read(imgPhotoWebProvider);
getBase64() async {
// List<int> imageBytes = await fileData.value?.readAsBytes();
if (fileData.value != null) {
@@ -71,38 +93,26 @@ class PresensiSelfieScreen extends HookConsumerWidget {
}
final ImagePicker _picker = ImagePicker();
pickImage() async {
final XFile? pickedFile = await _picker.pickImage(
source: ImageSource.camera,
// maxWidth set untuk width image
maxWidth: 640,
// maxHeight set untuk width image
maxHeight: 480);
if (pickedFile != null) {
final tmpFile = FilePickerResult([
PlatformFile(
name: pickedFile.name,
size: await pickedFile.length(),
path: pickedFile.path,
)
]);
if (await pickedFile.length() > 10000000) {
SanckbarWidget(context, "File tidak boleh lebih dari 10 MB",
snackbarType.warning);
} else {
fileData.value = pickedFile;
isImage.value = true;
fileEkstension.value = tmpFile.files.single.extension ?? "";
DateTime now = new DateTime.now();
fileName.value =
"IMG-${now.year}${now.month}${now.day}.${tmpFile.files.single.extension ?? ''}";
}
// final Directory appDocumentsDir =
// await getApplicationDocumentsDirectory();
// print(appDocumentsDir);
// await pickedFile!.saveTo(appDocumentsDir.path);
await getBase64();
pickImage() async {
if (kIsWeb) {
// final shared = await SharedPreferences.getInstance();
// final imageBase64 = shared.getString("base64Image");
// final imagePathNew = shared.getString("imagePath");
try {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => CameraPage(
fileDataBase64Param: fileDataBase64,
filePathParam: filePath,
),
),
);
} catch (e) {
SanckbarWidget(
context, "Failed to take picture", snackbarType.warning);
}
}
}
@@ -163,9 +173,10 @@ class PresensiSelfieScreen extends HookConsumerWidget {
try {
isLoadingAddressUserLocation.value = true;
// Mendapatkan posisi pengguna
LocationPermission permission = await Geolocator.requestPermission();
// LocationPermission permission = await Geolocator.requestPermission();
final permission = await location.hasPermission();
if (permission == LocationPermission.denied) {
if (permission == PermissionStatus.denied) {
isLoadingAddressUserLocation.value = false;
SanckbarWidget(context, 'Izin lokasi ditolak', snackbarType.error);
// Handle jika pengguna menolak izin lokasi
@@ -173,30 +184,53 @@ class PresensiSelfieScreen extends HookConsumerWidget {
return;
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
// Position position = await Geolocator.getCurrentPosition(
// locationSettings: LocationSettings(
// accuracy: LocationAccuracy.high,
// ),
// );
location.changeSettings(
accuracy: LocationAccuracy.high,
interval: 1000,
distanceFilter: 5,
);
_locationData = await location.getLocation();
// Mendapatkan alamat dari posisi
List<Placemark> placemarks = await placemarkFromCoordinates(
position.latitude, position.longitude);
// final address = await positionToAddressGoogleApis(
// LatLng(_locationData.latitude!, _locationData.longitude!));
final address = await positionToAddressGoogleApis(
LatLng(currLatProvider, currLongProvider));
// final address = await positionToAddress(
// LatLng(-7.5666203, 110.8083376),
// );
// Mendapatkan alamat dari posisi
if (positionLongitude.value.isEmpty && positionLatitude.value.isEmpty) {
if (placemarks.isNotEmpty) {
if (address != "") {
isLoadingAddressUserLocation.value = false;
Placemark placemark = placemarks.first;
// ref.read(googleApisProvider.notifier).getAddressGoogleApis(
// latitude: _locationData.latitude.toString(),
// longitude: _locationData.longitude.toString(),
// );
ref.read(googleApisProvider.notifier).getAddressGoogleApis(
latitude: currLatProvider.toString(),
longitude: currLongProvider.toString(),
);
// 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 = _locationData.latitude.toString();
// positionLongitude.value = _locationData.longitude.toString();
positionLatitude.value = position.latitude.toString();
positionLongitude.value = position.longitude.toString();
if (address != "") {
currentAddressUserLocation.value = address;
}
positionLatitude.value = currLatProvider.toString();
positionLongitude.value = currLongProvider.toString();
} else {
isLoadingAddressUserLocation.value = false;
SanckbarWidget(
@@ -226,9 +260,10 @@ class PresensiSelfieScreen extends HookConsumerWidget {
}
Future<void> requestLocationPermission() async {
var status = await Permission.location.request();
// var status = await PermissionStatus.location.request();
final status = await location.serviceEnabled();
isLoadingAddressUserLocation.value = true;
if (status.isGranted) {
if (status) {
// Izin diberikan, lanjutkan dengan mendapatkan lokasi
getAddressFromLocation();
} else {
@@ -261,6 +296,19 @@ class PresensiSelfieScreen extends HookConsumerWidget {
}
});
// googleApis address
ref.listen(googleApisProvider, (prev, next) {
if (next is GoogleApisStateLoading) {
isLoadingProsesCheckDistance.value = true;
} else if (next is GoogleApisStateError) {
isLoadingProsesCheckDistance.value = false;
SanckbarWidget(context, next.message, snackbarType.warning);
} else if (next is GoogleApisStateDone) {
isLoadingProsesCheckDistance.value = false;
currentAddressUserLocation.value = next.model ?? "0";
}
});
// check jam presensi
ref.listen(checkPresensiJamProvider, (prev, next) {
if (next is CheckPresensiJamStateLoading) {
@@ -508,7 +556,7 @@ class PresensiSelfieScreen extends HookConsumerWidget {
appBar: AppBar(
title: Text(
// 'Home Screen',
'Presensi Selfie',
'Presensi Selfie ${positionLatitude.value} , ${positionLongitude.value}',
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Constant.textBlack,
@@ -626,20 +674,34 @@ class PresensiSelfieScreen extends HookConsumerWidget {
children: [
InkWell(
onTap: () async {
getAddressFromLocation();
// getAddressFromLocation();
// refresh location
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => CustomGoogleMapWidget(
positionLastLatitudeParam:
positionLatitude,
positionLastLongitudeParam:
positionLongitude,
),
),
);
// 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,
);
}
// 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(
width: Constant.getActualXPhone(
@@ -722,13 +784,53 @@ class PresensiSelfieScreen extends HookConsumerWidget {
? Center(
child: CircularProgressIndicator(),
)
: PresensiSelfieUploadAreaWidget(
isLoading: isLoadingAddressUserLocation,
isImage: isImage,
fileData: fileData,
fileDataBase64: fileDataBase64,
pickFile: pickFile,
pickImage: pickImage,
: InkWell(
onTap: () {
pickImage();
},
child: Container(
width: Constant.getActualXPhone(
context: context, x: 390),
height: Constant.getActualYPhone(
context: context,
y: isImage.value ? 200 : 83),
decoration: BoxDecoration(
color: Constant.bgUploadFile,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Icon(
// Icons.upload_outlined,
// color: Constant.textOrange,
// ),
Image.asset(
'images/camera_selfie.png', // Path gambar untuk "Check In"
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 24),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 4,
),
),
Text(
'Upload File',
style: Constant.titleH2_400_12(
context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textOrange),
)
],
),
),
),
] else ...[
// gambar icon presensi clock out
@@ -739,18 +841,75 @@ class PresensiSelfieScreen extends HookConsumerWidget {
? Center(
child: CircularProgressIndicator(),
)
: PresensiSelfieUploadAreaWidget(
isLoading: isLoadingAddressUserLocation,
isImage: isImage,
fileData: fileData,
fileDataBase64: fileDataBase64,
pickFile: pickFile,
pickImage: pickImage,
: InkWell(
onTap: () {
pickImage();
},
child: Container(
width: Constant.getActualXPhone(
context: context, x: 390),
height: Constant.getActualYPhone(
context: context,
y: isImage.value ? 200 : 83),
decoration: BoxDecoration(
color: Constant.bgUploadFile,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
// Icon(
// Icons.upload_outlined,
// color: Constant.textOrange,
// ),
Image.asset(
'images/camera_selfie.png', // Path gambar untuk "Check In"
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 24),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 4,
),
),
Text(
'Upload File',
style: Constant.titleH2_400_12(
context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textOrange),
)
],
),
),
),
]
],
],
// gambar selfie
if (fileDataBase64.value.isNotEmpty) ...[
SizedBox(
height: Constant.getActualYPhone(context: context, y: 40),
),
SizedBox(
width: double.infinity,
height:
Constant.getActualYPhone(context: context, y: 200),
child: Image.network(
filePath.value,
fit: BoxFit.cover,
),
),
],
Spacer(),
// button clock in dan clock out
@@ -830,9 +989,8 @@ class PresensiSelfieScreen extends HookConsumerWidget {
token,
"Clock In");
// print(
// fileDataBase64.value,
// );
print(
"proses imageX: ${fileDataBase64.value}");
},
style: ButtonStyle(
backgroundColor:

View File

@@ -0,0 +1,205 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../app/constant.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import '../../provider/camera_controller_provider.dart';
class PresensiSelfieUploadAreaWebWidget extends HookConsumerWidget {
const PresensiSelfieUploadAreaWebWidget({
super.key,
required this.isImage,
required this.fileData,
required this.fileDataBase64,
required this.pickFile,
required this.pickImage,
required this.isLoading,
});
final ValueNotifier<bool> isImage;
final ValueNotifier<bool> isLoading;
final ValueNotifier<XFile?> fileData;
final ValueNotifier<String> fileDataBase64;
final Function pickImage;
final Function pickFile;
@override
Widget build(BuildContext context, WidgetRef ref) {
final imgPhotoWeb = ref.read(imgPhotoWebProvider);
return InkWell(
onTap: !isLoading.value
? () {
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
height: Constant.getActualYPhone(context: context, y: 200),
padding: EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Column(
// children: [
// IconButton(
// onPressed: () {
// Navigator.pop(context);
// pickFile();
// },
// icon: Icon(
// Icons.folder_copy_rounded,
// size: 50,
// color: Constant.textOrange,
// )),
// Text(
// "Browse a file",
// style: Constant.titleH2_400_12(context: context)
// .copyWith(
// fontWeight: FontWeight.w600,
// color: Constant.textBlack,
// ),
// )
// ],
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
onPressed: () {
Navigator.pop(context);
pickImage();
},
icon: Icon(
Icons.add_a_photo_rounded,
size: 50,
color: Constant.textOrange,
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 10),
),
Text(
"Take a picture",
style: Constant.titleH2_400_12(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack,
),
)
],
),
],
),
);
},
);
}
: null,
child: Stack(
alignment: AlignmentDirectional.topEnd,
children: [
Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(
context: context, y: isImage.value ? 200 : 83),
decoration: BoxDecoration(
color: Constant.bgUploadFile,
borderRadius: BorderRadius.circular(8),
),
child: Builder(builder: (context) {
final String? mime = lookupMimeType(fileData.value?.path ?? "");
return Semantics(
label: 'image_picker_example_picked_image',
child: (mime != null
? (mime.startsWith('image/'))
? Image.network(
// image: AssetImage(photo.value!.path),
fileData.value!.path,
frameBuilder: (context, child, frame,
wasSynchronouslyLoaded) {
return (wasSynchronouslyLoaded)
? Center(
child: Text("Loadinga"),
)
: Container(
child: child,
);
},
errorBuilder: (BuildContext context, Object error,
StackTrace? stackTrace) {
return const Center(
child: Text(
'This image type is not supported'));
},
)
: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.file_present_rounded,
size: 40,
color: Constant.textOrange,
),
Text(
fileData.value?.name ?? '',
style:
Constant.titleH2_400(context: context),
)
],
),
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Icon(
// Icons.upload_outlined,
// color: Constant.textOrange,
// ),
Image.asset(
'images/camera_selfie.png', // Path gambar untuk "Check In"
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 24),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 4,
),
),
Text(
'Upload File',
style: Constant.titleH2_400_12(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textOrange),
)
],
)));
}),
),
if (fileData.value != null && !isLoading.value)
IconButton(
onPressed: () {
fileData.value = null;
fileDataBase64.value = '';
isImage.value = false;
},
icon: Icon(Icons.cancel_outlined)),
],
),
);
}
}

View File

@@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'dart:js' as js;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'provider/location_provider.dart';
class TestFlutterWebMap extends HookConsumerWidget {
const TestFlutterWebMap({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final location = ref.watch(locationProvider);
final refreshKey = useState<int>(0);
useEffect(() {
void fetchData() {
final uri = Uri.base;
final queryParams = uri.queryParameters;
if (queryParams.isNotEmpty) {
ref.read(locationProvider.notifier).state = {
'status': queryParams['status'],
'message': queryParams['message'],
'latitude': queryParams['latitude'] != null
? double.tryParse(queryParams['latitude']!)
: null,
'longitude': queryParams['longitude'] != null
? double.tryParse(queryParams['longitude']!)
: null,
};
}
js.context.callMethod('getLocationAndSend');
}
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
fetchData();
});
return () {};
}, [refreshKey.value]);
void _refreshData() {
refreshKey.value++; // Trigger useEffect to run again
}
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Web Google Maps'),
),
body: Column(
children: [
SizedBox(height: 20), // Add some spacing
Text(
'Latitude: ${location['latitude']?.toString() ?? 'Loading...'}'),
Text(
'Longitude: ${location['longitude']?.toString() ?? 'Loading...'}'),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _refreshData,
child: Icon(Icons.refresh),
tooltip: 'Refresh Location',
),
),
);
}
}

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:geocoding/geocoding.dart';
class TestMap extends StatefulWidget {
const TestMap({super.key});
@@ -104,37 +103,37 @@ class _TestMapState extends State<TestMap> {
Map<String, dynamic> posx = await determinePosition();
print('${posx}');
if (posx['error'] == false) {
Position position = posx['position'];
List<Placemark> placemarks = await placemarkFromCoordinates(
position.latitude, position.longitude);
print('placemark : ${placemarks}');
// if (posx['error'] == false) {
// Position position = posx['position'];
// List<Placemark> placemarks = await placemarkFromCoordinates(
// position.latitude, position.longitude);
// print('placemark : ${placemarks}');
double jarakx = await Geolocator.distanceBetween(-7.5350973,
110.7921524, position.latitude, position.longitude);
// double jarakx = await Geolocator.distanceBetween(-7.5350973,
// 110.7921524, position.latitude, position.longitude);
setState(() {
jarak = jarakx.toString() + " meter";
longitude = position.longitude.toString();
latitude = position.latitude.toString();
addressFromCoordinat = placemarks[0].street.toString() +
" , " +
placemarks[0].subLocality.toString() +
" , " +
placemarks[0].locality.toString() +
" , " +
placemarks[0].postalCode.toString() +
" , " +
placemarks[0].country.toString();
});
} else {
setState(() {
longitude = "";
addressFromCoordinat = "";
jarak = "";
latitude = posx['message'].toString();
});
}
// setState(() {
// jarak = jarakx.toString() + " meter";
// longitude = position.longitude.toString();
// latitude = position.latitude.toString();
// addressFromCoordinat = placemarks[0].street.toString() +
// " , " +
// placemarks[0].subLocality.toString() +
// " , " +
// placemarks[0].locality.toString() +
// " , " +
// placemarks[0].postalCode.toString() +
// " , " +
// placemarks[0].country.toString();
// });
// } else {
// setState(() {
// longitude = "";
// addressFromCoordinat = "";
// jarak = "";
// latitude = posx['message'].toString();
// });
// }
},
),
SizedBox(

101
lib/test_map_x.dart Normal file
View File

@@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class TestMapX extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final mapController = useState<GoogleMapController?>(null);
final currentPosition = useState<LatLng?>(null);
final isLocationSet = useState<bool>(false);
final customIcon = useState<BitmapDescriptor>(BitmapDescriptor.defaultMarker);
final center = const LatLng(-7.566957, 110.8080284);
useEffect(() {
Future<void> _loadCustomMarker() async {
final icon = await BitmapDescriptor.asset(
const ImageConfiguration(size: Size(100, 100)),
'images/custom_marker1.png',
);
customIcon.value = icon;
}
_loadCustomMarker();
return null;
}, []);
Future<void> _getCurrentLocation() async {
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) return;
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.deniedForever ||
permission == LocationPermission.denied) {
return;
}
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
currentPosition.value = LatLng(position.latitude, position.longitude);
isLocationSet.value = true;
if (mapController.value != null) {
mapController.value!.animateCamera(
CameraUpdate.newLatLng(currentPosition.value!),
);
}
} catch (e) {
print('Error: $e');
}
}
return Scaffold(
appBar: AppBar(
title: Text('Google Maps Flutter Web'),
backgroundColor: Colors.green[700],
),
body: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: (controller) => mapController.value = controller,
initialCameraPosition: CameraPosition(
target: center,
zoom: 11.0,
),
markers: isLocationSet.value && currentPosition.value != null
? {
Marker(
markerId: MarkerId('current_location'),
position: currentPosition.value!,
icon: customIcon.value,
infoWindow: InfoWindow(
title: 'Posisi : ${currentPosition.value!.latitude}, ${currentPosition.value!.longitude}',
snippet:
'${currentPosition.value!.latitude}, ${currentPosition.value!.longitude}',
),
),
}
: {},
),
Positioned(
bottom: 20,
left: 20,
child: ElevatedButton(
onPressed: _getCurrentLocation,
child: Text('Current Location'),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,201 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../app/constant.dart';
import '../app/route.dart';
import '../provider/current_menu_provider.dart';
import '../provider/current_user_provider.dart';
import '../provider/google_login_provider.dart';
import '../screen/presensi/check_distance_provider.dart';
import 'sankbar_widget.dart';
class CustomGoogleMapWidget extends HookConsumerWidget {
const CustomGoogleMapWidget({
super.key,
required this.positionLastLatitudeParam,
required this.positionLastLongitudeParam,
});
final ValueNotifier<String> positionLastLatitudeParam;
final ValueNotifier<String> positionLastLongitudeParam;
@override
Widget build(BuildContext context, WidgetRef ref) {
final mapController = useState<GoogleMapController?>(null);
final currentPosition = useState<LatLng?>(null);
final isLocationSet = useState<bool>(false);
final customIcon =
useState<BitmapDescriptor>(BitmapDescriptor.defaultMarker);
final center = LatLng(
Constant.positionLatitudeAwal,
Constant.positionLongitudeAwal,
);
final selectedUser = ref.read(currentUserProvider);
final isLoadingProsesCheckDistance = useState<bool>(false);
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 () {};
}, []);
useEffect(() {
Future<void> loadCustomMarker() async {
final icon = await BitmapDescriptor.asset(
const ImageConfiguration(size: Size(100, 100)),
'images/custom_marker1.png',
);
customIcon.value = icon;
}
loadCustomMarker();
return null;
}, []);
Future<void> getCurrentLocation(context) async {
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) return;
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.deniedForever ||
permission == LocationPermission.denied) {
return;
}
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
currentPosition.value = LatLng(position.latitude, position.longitude);
isLocationSet.value = true;
if (mapController.value != null) {
mapController.value!.animateCamera(
CameraUpdate.newLatLng(currentPosition.value!),
);
// change current position
final currentLat = currentPosition.value!.latitude;
final currentLong = currentPosition.value!.longitude;
// update parameter di home
positionLastLatitudeParam.value = currentLat.toString();
positionLastLongitudeParam.value = currentLong.toString();
// update ke state provider
ref.read(currentLatitudeProvider.notifier).state = currentLat;
ref.read(currentLongitudeProvider.notifier).state = currentLong;
print("LATITUDE PRESENSI ${currentLat.toString()}");
print("LONGITUDE PRESENSI ${currentLong.toString()}");
// check distance
ref.read(checkDistanceProvider.notifier).checkDistance(
selectedUser?.model.staffId ?? "",
selectedUser?.model.companyId ?? "",
currentLat.toString(),
currentLong.toString(),
);
}
} catch (e) {
print('Error: $e');
}
}
// 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 (next.model.selfie == "TRUE") {
ref.read(currentPageProvider.notifier).update((state) => 99);
// Navigator.of(context).pop();
Navigator.of(context).restorablePushNamed(presensiSelfieRoute);
} else {
if (next.model.selfie == "FALSE") {
ref.read(currentPageProvider.notifier).update((state) => 99);
Navigator.of(context).restorablePushNamed(presensiRoute);
}
}
}
});
return Scaffold(
backgroundColor: Constant.textWhite,
appBar: AppBar(
title: Text(
// 'Home Screen',
'User Location',
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Constant.textBlack,
),
),
backgroundColor: Constant.textWhite,
iconTheme: IconThemeData(
color: Constant.textBlack,
),
// elevation: 1.0,
elevation: 0.5,
),
body: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: (controller) => mapController.value = controller,
initialCameraPosition: CameraPosition(
target: center,
zoom: 11.0,
),
markers: isLocationSet.value && currentPosition.value != null
? {
Marker(
markerId: MarkerId('current_location'),
position: currentPosition.value!,
icon: customIcon.value,
infoWindow: InfoWindow(
title:
'Posisi : ${currentPosition.value!.latitude}, ${currentPosition.value!.longitude}',
snippet:
'${currentPosition.value!.latitude}, ${currentPosition.value!.longitude}',
),
),
}
: {},
),
Positioned(
bottom: 20,
left: 20,
child: ElevatedButton(
onPressed: () async {
getCurrentLocation(context);
},
child: Text('Current Location'),
),
),
],
),
);
}
}

View File

@@ -7,12 +7,16 @@ import Foundation
import file_selector_macos
import geolocator_apple
import google_sign_in_ios
import location
import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}

View File

@@ -1,22 +1,6 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
url: "https://pub.dev"
source: hosted
version: "61.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
url: "https://pub.dev"
source: hosted
version: "5.13.0"
archive:
dependency: transitive
description:
@@ -57,6 +41,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
camera:
dependency: "direct main"
description:
name: camera
sha256: "26ff41045772153f222ffffecba711a206f670f5834d40ebf5eed3811692f167"
url: "https://pub.dev"
source: hosted
version: "0.11.0+2"
camera_android_camerax:
dependency: transitive
description:
name: camera_android_camerax
sha256: "7cd93578ad201dcc6bb5810451fb00d76a86bab9b68dceb68b8cbd7038ac5846"
url: "https://pub.dev"
source: hosted
version: "0.6.8+3"
camera_avfoundation:
dependency: transitive
description:
name: camera_avfoundation
sha256: "7c28969a975a7eb2349bc2cb2dfe3ad218a33dba9968ecfb181ce08c87486655"
url: "https://pub.dev"
source: hosted
version: "0.9.17+3"
camera_platform_interface:
dependency: transitive
description:
name: camera_platform_interface
sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061
url: "https://pub.dev"
source: hosted
version: "2.8.0"
camera_web:
dependency: transitive
description:
name: camera_web
sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f"
url: "https://pub.dev"
source: hosted
version: "0.3.5"
characters:
dependency: transitive
description:
@@ -117,10 +141,10 @@ packages:
dependency: transitive
description:
name: cross_file
sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5"
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev"
source: hosted
version: "0.3.3+7"
version: "0.3.4+2"
crypto:
dependency: transitive
description:
@@ -153,14 +177,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.6"
dart_nominatim:
dependency: "direct main"
description:
name: dart_nominatim
sha256: "5f1d72733f5dd6cf4d32bae707fda4bc6ef1a61ddbd3b24e373d66b52a2ae5a9"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
dio:
dependency: "direct main"
description:
name: dio
sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8"
sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0"
url: "https://pub.dev"
source: hosted
version: "4.0.6"
version: "5.6.0"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
equatable:
dependency: "direct main"
description:
@@ -241,6 +281,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.9.3+1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
fluentui_system_icons:
dependency: "direct main"
description:
@@ -262,6 +310,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.18.6"
flutter_image_compress:
dependency: "direct main"
description:
name: flutter_image_compress
sha256: "37f1b26399098e5f97b74c1483f534855e7dff68ead6ddaccf747029fb03f29f"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
flutter_launcher_icons:
dependency: "direct main"
description:
@@ -282,10 +338,10 @@ packages:
dependency: "direct main"
description:
name: flutter_map
sha256: cda8d72135b697f519287258b5294a57ce2f2a5ebf234f0e406aad4dc14c9399
sha256: "87cc8349b8fa5dccda5af50018c7374b6645334a0d680931c1fe11bce88fa5bb"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
version: "6.2.1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -320,110 +376,118 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
geocoding:
dependency: "direct main"
description:
name: geocoding
sha256: e1dc0ac56666d9ed1d5a9ae5543ce9eb5986db6209cc7600103487d09192059c
url: "https://pub.dev"
source: hosted
version: "2.1.1"
geocoding_android:
dependency: transitive
description:
name: geocoding_android
sha256: "609db1d71bc364dd9d0616f72a41c01e0c74f3a3807efb85e0d5a67e57baf50f"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
geocoding_ios:
dependency: transitive
description:
name: geocoding_ios
sha256: "8f79e380abb640ef4d88baee8bb65390058c802601158d0813dc990b36b189d2"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
geocoding_platform_interface:
dependency: transitive
description:
name: geocoding_platform_interface
sha256: "8848605d307d844d89937cdb4b8ad7dfa880552078f310fa24d8a460f6dddab4"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
geolocator:
dependency: "direct main"
description:
name: geolocator
sha256: e946395fc608842bb2f6c914807e9183f86f3cb787f6b8f832753e5251036f02
sha256: "149876cc5207a0f5daf4fdd3bfcf0a0f27258b3fe95108fa084f527ad0568f1b"
url: "https://pub.dev"
source: hosted
version: "10.1.0"
version: "12.0.0"
geolocator_android:
dependency: transitive
description:
name: geolocator_android
sha256: "741579fa6c9e412984d2bdb2fbaa54e3c3f7587c60aeacfe6e058358a11f40f8"
sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
version: "4.6.1"
geolocator_apple:
dependency: transitive
description:
name: geolocator_apple
sha256: "16cdb6b8d3685d3e07a7e54e88e97106f4fae9e496191f33e8bb389cce14f198"
sha256: bc2aca02423ad429cb0556121f56e60360a2b7d694c8570301d06ea0c00732fd
url: "https://pub.dev"
source: hosted
version: "2.3.4"
version: "2.3.7"
geolocator_platform_interface:
dependency: transitive
description:
name: geolocator_platform_interface
sha256: "6c8d494d6948757c56720b778af742f6973f31fca1f702a7539b8917e4a2468a"
sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012"
url: "https://pub.dev"
source: hosted
version: "4.2.0"
version: "4.2.4"
geolocator_web:
dependency: transitive
description:
name: geolocator_web
sha256: "59083f7e0871b78299918d92bf930a14377f711d2d1156c558cd5ebae6c20d58"
sha256: "7a22f400d831f924a89d931ba126a10e6b8b437f31e6b9311320435f3e1571bd"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "4.0.0"
geolocator_windows:
dependency: transitive
description:
name: geolocator_windows
sha256: a92fae29779d5c6dc60e8411302f5221ade464968fe80a36d330e80a71f087af
sha256: "53da08937d07c24b0d9952eb57a3b474e29aae2abf9dd717f7e1230995f13f0e"
url: "https://pub.dev"
source: hosted
version: "0.2.2"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "0.2.3"
google_identity_services_web:
dependency: transitive
description:
name: google_identity_services_web
sha256: "000b7a31e1fa17ee04b6c0553a2b2ea18f9f9352e4dcc0c9fcc785cf10f2484e"
sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6"
url: "https://pub.dev"
source: hosted
version: "0.2.2"
version: "0.3.1+4"
google_maps:
dependency: transitive
description:
name: google_maps
sha256: "463b38e5a92a05cde41220a11fd5eef3847031fef3e8cf295ac76ec453246907"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
google_maps_flutter:
dependency: "direct main"
description:
name: google_maps_flutter
sha256: "2e302fa3aaf4e2a297f0342d83ebc5e8e9f826e9a716aef473fe7f404ec630a7"
url: "https://pub.dev"
source: hosted
version: "2.9.0"
google_maps_flutter_android:
dependency: transitive
description:
name: google_maps_flutter_android
sha256: "60a005bf1ba8d178144e442f6e2d734b0ffc2cc800a05415388472f934ad6d6a"
url: "https://pub.dev"
source: hosted
version: "2.14.4"
google_maps_flutter_ios:
dependency: transitive
description:
name: google_maps_flutter_ios
sha256: "3a484846fc56f15e47e3de1f5ea80a7ff2b31721d2faa88f390f3b3cf580c953"
url: "https://pub.dev"
source: hosted
version: "2.13.0"
google_maps_flutter_platform_interface:
dependency: transitive
description:
name: google_maps_flutter_platform_interface
sha256: "4f6930fd668bf5d40feb2695d5695dbc0c35e5542b557a34ad35be491686d2ba"
url: "https://pub.dev"
source: hosted
version: "2.9.0"
google_maps_flutter_web:
dependency: "direct main"
description:
name: google_maps_flutter_web
sha256: ff39211bd25d7fad125d19f757eba85bd154460907cd4d135e07e3d0f98a4130
url: "https://pub.dev"
source: hosted
version: "0.5.10"
google_sign_in:
dependency: "direct main"
description:
name: google_sign_in
sha256: "8f8b94880f2753ccb796744259da529674e49b9af2e372abf6978c590c0ebfef"
sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f"
url: "https://pub.dev"
source: hosted
version: "6.1.6"
version: "6.2.1"
google_sign_in_android:
dependency: transitive
description:
@@ -436,10 +500,10 @@ packages:
dependency: transitive
description:
name: google_sign_in_ios
sha256: "81495441405c138e3c638f5097bebaa0db644567b3976e08944cfb8926ff2e6d"
sha256: a058c9880be456f21e2e8571c1126eaacd570bdc5b6c6d9d15aea4bdf22ca9fe
url: "https://pub.dev"
source: hosted
version: "5.6.5"
version: "5.7.6"
google_sign_in_platform_interface:
dependency: transitive
description:
@@ -452,26 +516,26 @@ packages:
dependency: transitive
description:
name: google_sign_in_web
sha256: b48263e47f9493ba4120ccdfffe7412549ee297e82b97be9b8fa16ea8919ffbe
sha256: "042805a21127a85b0dc46bba98a37926f17d2439720e8a459d27045d8ef68055"
url: "https://pub.dev"
source: hosted
version: "0.12.0+4"
version: "0.12.4+2"
gql:
dependency: transitive
description:
name: gql
sha256: "0bdd22c3a9464970ae590559e4f0568769b219dda9e94cb10c4cf999a3e263f7"
sha256: "8ecd3585bb9e40d671aa58f52575d950670f99e5ffab18e2b34a757e071a6693"
url: "https://pub.dev"
source: hosted
version: "1.0.1-alpha+1705114622973"
version: "1.0.1-alpha+1717789143880"
gql_dedupe_link:
dependency: transitive
description:
name: gql_dedupe_link
sha256: e5359dd0c7a38f95e2b12f6ab305989a4e30028e4032825c8e9f610150999c69
sha256: "10bee0564d67c24e0c8bd08bd56e0682b64a135e58afabbeed30d85d5e9fea96"
url: "https://pub.dev"
source: hosted
version: "2.0.4-alpha+1705114623057"
version: "2.0.4-alpha+1715521079596"
gql_error_link:
dependency: transitive
description:
@@ -492,18 +556,18 @@ packages:
dependency: transitive
description:
name: gql_http_link
sha256: "1f922eed1b7078fdbfd602187663026f9f659fe9a9499e2207b5d5e01617f658"
sha256: ef6ad24d31beb5a30113e9b919eec20876903cc4b0ee0d31550047aaaba7d5dd
url: "https://pub.dev"
source: hosted
version: "1.0.1+1"
version: "1.1.0"
gql_link:
dependency: transitive
description:
name: gql_link
sha256: "63941513a688d856546f0c3218e7ad94d47fc6e04662dcdb06de92a4cde2d7db"
sha256: "70fd5b5cbcc50601679f4b9fea3bcc994e583f59cfec7e1fec11113074b1a565"
url: "https://pub.dev"
source: hosted
version: "1.0.1-alpha+1705114622987"
version: "1.0.1-alpha+1717789143896"
gql_transform_link:
dependency: transitive
description:
@@ -516,10 +580,10 @@ packages:
dependency: "direct main"
description:
name: graphql
sha256: d066e53446166c12537458386b507f7426f2b8801ebafc184576aab3cbc64d56
sha256: "62f31433ba194eda7b81a812a83c3d9560766cec5ac0210ea4a3e677c91b8df4"
url: "https://pub.dev"
source: hosted
version: "5.2.0-beta.7"
version: "5.2.0-beta.8"
hive:
dependency: transitive
description:
@@ -548,10 +612,10 @@ packages:
dependency: transitive
description:
name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.2.2"
http_parser:
dependency: transitive
description:
@@ -580,10 +644,10 @@ packages:
dependency: "direct main"
description:
name: image_picker
sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd"
sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a"
url: "https://pub.dev"
source: hosted
version: "1.0.7"
version: "1.1.2"
image_picker_android:
dependency: transitive
description:
@@ -628,10 +692,18 @@ packages:
dependency: transitive
description:
name: image_picker_platform_interface
sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b
sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80"
url: "https://pub.dev"
source: hosted
version: "2.9.3"
version: "2.10.0"
image_picker_web:
dependency: "direct main"
description:
name: image_picker_web
sha256: b5cf4faf66714f17b3e86b37a39d19743603163a08b968b28cadfc5df1dc2b75
url: "https://pub.dev"
source: hosted
version: "4.0.0"
image_picker_windows:
dependency: transitive
description:
@@ -652,10 +724,10 @@ packages:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted
version: "0.6.7"
version: "0.7.1"
json_annotation:
dependency: transitive
description:
@@ -668,10 +740,34 @@ packages:
dependency: "direct main"
description:
name: latlong2
sha256: "18712164760cee655bc790122b0fd8f3d5b3c36da2cb7bf94b68a197fbb0811b"
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
url: "https://pub.dev"
source: hosted
version: "0.9.0"
version: "0.9.1"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
url: "https://pub.dev"
source: hosted
version: "10.0.5"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
@@ -688,38 +784,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
location:
dependency: "direct main"
description:
name: location
sha256: "37ffdadcd4b1498b769824f45ebb4de8ed46663a4a67ac27b33a590ee486579f"
url: "https://pub.dev"
source: hosted
version: "6.0.2"
location_platform_interface:
dependency: transitive
description:
name: location_platform_interface
sha256: "2ecde6bb0f88032b0bbbde37e18975b4468711dd92619c2235cc0c0ee93b4b8e"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
location_web:
dependency: transitive
description:
name: location_web
sha256: "924da8436db7ded5eef92a7ef3ae6aa3715831a93965376c91738f586302350e"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
logger:
dependency: transitive
description:
name: logger
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32"
url: "https://pub.dev"
source: hosted
version: "2.0.2+1"
version: "2.4.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16"
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.15.0"
mgrs_dart:
dependency: transitive
description:
@@ -760,22 +880,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.3.2"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
dependency: "direct main"
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
version: "1.9.0"
path_drawing:
dependency: transitive
description:
@@ -844,42 +956,50 @@ packages:
dependency: "direct main"
description:
name: permission_handler
sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8"
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
url: "https://pub.dev"
source: hosted
version: "11.0.1"
version: "11.3.1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa"
url: "https://pub.dev"
source: hosted
version: "11.1.0"
version: "12.0.12"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0
url: "https://pub.dev"
source: hosted
version: "9.1.4"
version: "9.4.5"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851
url: "https://pub.dev"
source: hosted
version: "0.1.3+2"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
sha256: fe0ffe274d665be8e34f9c59705441a7d248edebbe5d9e3ec2665f88b79358ea
url: "https://pub.dev"
source: hosted
version: "3.12.0"
version: "4.2.2"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.1.3"
version: "0.2.1"
petitparser:
dependency: transitive
description:
@@ -896,14 +1016,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.14.0"
pigeon:
dependency: transitive
description:
name: pigeon
sha256: "5a79fd0b10423f6b5705525e32015597f861c31220b522a67d1e6b580da96719"
url: "https://pub.dev"
source: hosted
version: "11.0.1"
platform:
dependency: transitive
description:
@@ -924,10 +1036,10 @@ packages:
dependency: transitive
description:
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.dev"
source: hosted
version: "3.7.3"
version: "3.9.1"
polylabel:
dependency: transitive
description:
@@ -944,14 +1056,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
riverpod:
dependency: transitive
description:
@@ -968,6 +1072,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.27.7"
sanitize_html:
dependency: transitive
description:
name: sanitize_html
sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
shared_preferences:
dependency: "direct main"
description:
@@ -1069,6 +1181,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
@@ -1089,10 +1209,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.2"
top_snackbar_flutter:
dependency: "direct main"
description:
@@ -1121,10 +1241,10 @@ packages:
dependency: transitive
description:
name: uuid
sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90"
url: "https://pub.dev"
source: hosted
version: "4.2.2"
version: "4.4.2"
vector_math:
dependency: transitive
description:
@@ -1173,22 +1293,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.17"
watcher:
vm_service:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "14.2.5"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
version: "0.5.1"
web_socket_channel:
dependency: transitive
description:
@@ -1238,5 +1358,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.10.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"

View File

@@ -36,9 +36,11 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
geolocator: ^10.1.0
geocoding: ^2.1.1
dio: ^4.0.6
# geolocator: ^10.1.0
geolocator: ^12.0.0
# geocoding: ^2.1.1
# dio: ^4.0.6
dio: ^5.3.3
flutter_riverpod: ^1.0.4
flutter_hooks: ^0.18.5+1
hooks_riverpod: ^1.0.4
@@ -46,17 +48,20 @@ dependencies:
shared_preferences: ^2.0.1
flutter_map: ^6.1.0
latlong2: ^0.9.0
google_sign_in: ^6.1.6
# google_sign_in: ^6.1.6
google_sign_in: ^6.2.1
crypton: ^2.2.1
path_provider: ^2.0.2
intl: ^0.17.0
graphql: ^5.1.3
top_snackbar_flutter: ^3.1.0
permission_handler: ^11.0.0
# permission_handler: ^11.0.0
permission_handler: ^11.3.1
mobkit_dashed_border: ^0.0.5
file_picker: ^6.1.1
open_file: ^3.3.2
image_picker: ^1.0.7
# image_picker: ^1.0.7
image_picker: ^1.1.2
video_player: ^2.7.2
photo_view: ^0.14.0
mime: ^1.0.4
@@ -64,6 +69,16 @@ dependencies:
iconify_flutter: ^0.0.5
fluentui_system_icons: ^1.1.226
eva_icons_flutter: ^3.1.0
image_picker_web: ^4.0.0
dart_nominatim: ^1.0.1
location: ^6.0.2
# camera_web: ^0.3.5
camera: ^0.11.0+2
flutter_image_compress: ^1.1.0
path: ^1.8.3
# google_maps_flutter: ^2.2.6
google_maps_flutter: ^2.9.0
google_maps_flutter_web: ^0.5.10
dev_dependencies:
flutter_test:

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
@@ -24,36 +25,93 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="absensi_sas_flutter">
<!-- google sign in browser -->
<meta name="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"
content="856240587825-klh0dfjc44bovajg1rpq5vbvs4g7rh5j.apps.googleusercontent.com">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<link rel="icon" type="image/png" href="favicon.png" />
<title>absensi_sas_flutter</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
<!-- <script>
window.flutterLocationCallback = (data) => {
console.log('Received data from Flutter:', data);
window.flutterLocationData = data;
};
function getCurrentLocation(callback) {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => callback(position.coords.latitude, position.coords.longitude),
error => console.error('Geolocation error:', error)
);
} else {
console.error("Geolocation is not supported by this browser.");
}
}
function sendLocationToServer(latitude, longitude) {
fetch('https://devone.aplikasi.web.id/absensi/apiGetUserLocation.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
body: `latitude=${latitude}&longitude=${longitude}`
})
.then(response => response.json())
.then(data => {
console.log('Server response:', data);
// if (window.flutterLocationCallback) {
// window.flutterLocationCallback(data);
// } else {
// console.error('flutterLocationCallback is not defined');
// }
const queryString = new URLSearchParams(data).toString();
const newUrl = `${window.location.origin}${window.location.pathname}?${queryString}`;
// Update the URL with query parameters
window.history.replaceState(null, '', newUrl);
})
.catch(error => console.error('Request failed', error));
}
function getLocationAndSend() {
getCurrentLocation((latitude, longitude) => {
sendLocationToServer(latitude, longitude);
});
});
</script>
}
</script> -->
</head>
<body>
<script src="flutter_bootstrap.js" async></script>
<!-- <script>
// var API_KEY = "AIzaSyCiN7EeJsUpXVLQKFfrj3sE5OTKebjpzek";
// var API_KEY = "AIzaSyCztj0X7erH-uqotFfe_Yd-Nt0oBJu6FeM";
var API_KEY = "AIzaSyAVUr4Ku4O1HlSkK8n9KGnUyqvsXBL-yfs";
var script = document.createElement('script');
script.src = "https://maps.googleapis.com/maps/api/js?key=" + API_KEY;
document.head.appendChild(script);
// tambahan
// window.onload = function () {
// getLocationAndSend();
// };
// tambahan
</script> -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAVUr4Ku4O1HlSkK8n9KGnUyqvsXBL-yfs"></script>
<script src ="https://maps.googleapis.com/maps/api/js?key=AIzaSyAVUr4Ku4O1HlSkK8n9KGnUyqvsXBL-yfs&callback=initMap&v=weekly&libraries=marker" defer></script>
</body>
</html>
</html>

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="absensi_sas_flutter">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>absensi_sas_flutter</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
</body>
</html>