Add location permission request

This commit is contained in:
anhtunz
2024-12-18 22:38:26 +07:00
parent d0ba34381a
commit 9046b21831
11 changed files with 168 additions and 30 deletions

View File

@@ -11,6 +11,7 @@ import 'package:sfm_app/feature/home/home_bloc.dart';
import 'package:sfm_app/product/constant/app/app_constants.dart'; import 'package:sfm_app/product/constant/app/app_constants.dart';
import 'package:sfm_app/product/constant/enums/app_route_enums.dart'; import 'package:sfm_app/product/constant/enums/app_route_enums.dart';
import 'package:sfm_app/product/constant/enums/role_enums.dart'; import 'package:sfm_app/product/constant/enums/role_enums.dart';
import 'package:sfm_app/product/permission/location_permission.dart';
import '../devices/devices_manager_bloc.dart'; import '../devices/devices_manager_bloc.dart';
import '../devices/devices_manager_screen.dart'; import '../devices/devices_manager_screen.dart';
import '../home/home_screen.dart'; import '../home/home_screen.dart';
@@ -39,7 +40,7 @@ class MainScreen extends StatefulWidget {
State<MainScreen> createState() => _MainScreenState(); State<MainScreen> createState() => _MainScreenState();
} }
class _MainScreenState extends State<MainScreen> { class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
late MainBloc mainBloc; late MainBloc mainBloc;
bool isVN = true; bool isVN = true;
@@ -68,16 +69,39 @@ class _MainScreenState extends State<MainScreen> {
mainBloc.sinkIsVNIcon.add(isVN); mainBloc.sinkIsVNIcon.add(isVN);
mainBloc.sinkThemeMode.add(isLight); mainBloc.sinkThemeMode.add(isLight);
log("role: $role"); log("role: $role");
LocationPermissionRequest.instance.checkLocationPermission(context);
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
mainBloc = BlocProvider.of(context); mainBloc = BlocProvider.of(context);
WidgetsBinding.instance.addObserver(this);
initialCheck(); initialCheck();
getBellNotification(); getBellNotification();
} }
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.inactive) {
log("App Inactive");
} else if (state == AppLifecycleState.resumed) {
log("App Resumed");
LocationPermissionRequest.instance.checkLocationPermission(context);
} else if (state == AppLifecycleState.paused) {
log("App paused");
} else if (state == AppLifecycleState.detached) {
log("App detached");
}
}
@override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ThemeNotifier themeNotifier = context.watch<ThemeNotifier>(); ThemeNotifier themeNotifier = context.watch<ThemeNotifier>();

View File

@@ -17,8 +17,8 @@ class MapBloc extends BlocBase {
void dispose() {} void dispose() {}
final mapType = StreamController<MapType>.broadcast(); final mapType = StreamController<MapType>.broadcast();
StreamSink<MapType> get sinkmapType => mapType.sink; StreamSink<MapType> get sinkMapType => mapType.sink;
Stream<MapType> get streammapType => mapType.stream; Stream<MapType> get streamMapType => mapType.stream;
final mapController = StreamController<GoogleMapController>.broadcast(); final mapController = StreamController<GoogleMapController>.broadcast();
StreamSink<GoogleMapController> get sinkmapController => mapController.sink; StreamSink<GoogleMapController> get sinkmapController => mapController.sink;
@@ -71,4 +71,6 @@ class MapBloc extends BlocBase {
context, "Không tìm thấy đường", Colors.orange, Colors.white); context, "Không tìm thấy đường", Colors.orange, Colors.white);
} }
} }
} }

View File

@@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart'; import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:sfm_app/feature/devices/device_model.dart'; import 'package:sfm_app/feature/devices/device_model.dart';
@@ -10,6 +11,7 @@ import 'package:sfm_app/feature/map/map_bloc.dart';
import 'package:sfm_app/feature/map/widget/on_tap_marker_widget.dart'; import 'package:sfm_app/feature/map/widget/on_tap_marker_widget.dart';
import 'package:sfm_app/product/base/bloc/base_bloc.dart'; import 'package:sfm_app/product/base/bloc/base_bloc.dart';
import 'package:sfm_app/product/constant/icon/icon_constants.dart'; import 'package:sfm_app/product/constant/icon/icon_constants.dart';
import 'package:sfm_app/product/permission/location_permission.dart';
import 'package:sfm_app/product/services/api_services.dart'; import 'package:sfm_app/product/services/api_services.dart';
class MapScreen extends StatefulWidget { class MapScreen extends StatefulWidget {
@@ -19,7 +21,7 @@ class MapScreen extends StatefulWidget {
State<MapScreen> createState() => _MapScreenState(); State<MapScreen> createState() => _MapScreenState();
} }
class _MapScreenState extends State<MapScreen> { class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
late BitmapDescriptor normalIcon; late BitmapDescriptor normalIcon;
late BitmapDescriptor offlineIcon; late BitmapDescriptor offlineIcon;
late BitmapDescriptor abnormalIcon; late BitmapDescriptor abnormalIcon;
@@ -47,7 +49,8 @@ class _MapScreenState extends State<MapScreen> {
Set<Marker> markersAll = {}; Set<Marker> markersAll = {};
List<Marker> markers = []; List<Marker> markers = [];
LatLng myLocation = const LatLng(213761, 123123); LatLng myLocation = const LatLng(213761, 123123);
Position? position;
bool isAllowLocationPermission = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -143,17 +146,27 @@ class _MapScreenState extends State<MapScreen> {
cluster.getId(), cluster.getId(),
), ),
position: cluster.location, position: cluster.location,
onTap: () { onTap: () async {
bool check = await checkLocationPermission(context);
if (check == true) {
Position position = await Geolocator.getCurrentPosition();
// ignore: use_build_context_synchronously
onTapMarker( onTapMarker(
context, context,
_controller, _controller,
mapBloc, mapBloc,
myLocation, LatLng(position.latitude, position.longitude),
cluster.items, cluster.items,
imageAssets, imageAssets,
markers, markers,
hospitalIcon, hospitalIcon,
fireStationIcon); fireStationIcon,
);
} else {
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Cannot get your Location")));
}
}, },
icon: getMarkerIcon(cluster), icon: getMarkerIcon(cluster),
); );
@@ -234,4 +247,10 @@ class _MapScreenState extends State<MapScreen> {
} }
} }
} }
Future<bool> checkLocationPermission(context) async {
bool check = await LocationPermissionRequest.instance
.checkLocationPermission(context);
return check;
}
} }

View File

@@ -1,7 +1,6 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'show_direction_widget.dart'; import 'show_direction_widget.dart';
@@ -26,7 +25,6 @@ onTapMarker(
BitmapDescriptor hospitalIcon, BitmapDescriptor hospitalIcon,
BitmapDescriptor fireStationIcon, BitmapDescriptor fireStationIcon,
) { ) {
LatLng testLocation = const LatLng(20.985453, 105.738381);
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (BuildContext modalBottomSheetContext) { builder: (BuildContext modalBottomSheetContext) {
@@ -77,7 +75,7 @@ onTapMarker(
mapBloc.findTheWay( mapBloc.findTheWay(
context, context,
controller, controller,
testLocation, myLocation,
destination, destination,
); );
String deviceLocations = await DeviceUtils.instance String deviceLocations = await DeviceUtils.instance

View File

@@ -1,7 +1,7 @@
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:sfm_app/product/services/language_services.dart'; import 'product/services/language_services.dart';
import 'feature/main/main_bloc.dart'; import 'feature/main/main_bloc.dart';
import 'product/base/bloc/base_bloc.dart'; import 'product/base/bloc/base_bloc.dart';
import 'product/constant/navigation/navigation_router.dart'; import 'product/constant/navigation/navigation_router.dart';

View File

@@ -1,11 +1,17 @@
import 'package:app_settings/app_settings.dart'; import 'package:app_settings/app_settings.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../extention/context_extention.dart'; import '../../../extention/context_extention.dart';
import '../../../services/language_services.dart'; import '../../../services/language_services.dart';
class RequestPermissionDialog { class RequestPermissionDialog {
showRequestPermissionDialog(BuildContext context, IconData icon, RequestPermissionDialog._init();
String dialogContent, AppSettingsType type) { static RequestPermissionDialog? _instance;
static RequestPermissionDialog get instance =>
_instance ??= RequestPermissionDialog._init();
requestPermissionDialogAndroid(BuildContext context, IconData icon,
String content, AppSettingsType type) {
showDialog( showDialog(
useRootNavigator: false, useRootNavigator: false,
context: context, context: context,
@@ -20,36 +26,39 @@ class RequestPermissionDialog {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Padding( Padding(
padding: context.paddingNormalVertical, padding: dialogContext.paddingNormalVertical,
child: Text( child: Text(
dialogContent, "Alow app to use $content permission",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 18), fontWeight: FontWeight.bold, fontSize: 18),
), ),
), ),
Divider(height: context.lowValue), Divider(height: dialogContext.lowValue),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
AppSettings.openAppSettings(type: type); AppSettings.openAppSettings(type: type);
Navigator.of(dialogContext).pop();
}, },
child: Padding( child: Padding(
padding: padding: dialogContext.paddingNormalVertical,
context.paddingNormalVertical, // Cách giữa các phần tử
child: Text(appLocalization(context).allow_message, child: Text(appLocalization(context).allow_message,
style: const TextStyle(fontWeight: FontWeight.bold)), style: const TextStyle(fontWeight: FontWeight.bold)),
), ),
), ),
Divider(height: context.lowValue), Divider(height: dialogContext.lowValue),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
Navigator.of(dialogContext).pop(); // Đóng dialog Navigator.of(dialogContext).pop();
}, },
child: Padding( child: Padding(
padding: padding: dialogContext.paddingNormalVertical,
context.paddingNormalVertical, // Cách giữa các phần tử child: Text(
child: Text(appLocalization(context).decline_message, appLocalization(dialogContext).decline_message,
style: const TextStyle(fontWeight: FontWeight.bold)), style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
), ),
), ),
], ],
@@ -58,4 +67,31 @@ class RequestPermissionDialog {
}, },
); );
} }
requestPermissionDialogIOS(context, AppSettingsType appSettingsType, content,
AppSettingsType type) =>
showCupertinoDialog<void>(
context: context,
builder: (showCupertinoDialogContext) => CupertinoAlertDialog(
title: Text(appLocalization(showCupertinoDialogContext)
.permission_deny_message),
content: Text("Alow app to use $content permission"),
actions: <CupertinoDialogAction>[
CupertinoDialogAction(
child: Text(appLocalization(showCupertinoDialogContext)
.cancel_button_content),
onPressed: () => Navigator.pop(showCupertinoDialogContext),
),
CupertinoDialogAction(
isDefaultAction: true,
child: Text(
appLocalization(showCupertinoDialogContext).allow_message),
onPressed: () {
Navigator.pop(showCupertinoDialogContext);
AppSettings.openAppSettings(type: appSettingsType);
},
),
],
),
);
} }

View File

@@ -193,8 +193,9 @@
"warning_message": "Warning: ", "warning_message": "Warning: ",
"loading_message": "Loading...", "loading_message": "Loading...",
"detail_message": "Detail", "detail_message": "Detail",
"decline_message": "DECLINE", "permission_deny_message": "Permission Denied",
"allow_message": "ALLOW", "decline_message": "Decline",
"allow_message": "Allow",
"add_button_content": "Add", "add_button_content": "Add",
"update_button_content": "Update", "update_button_content": "Update",
"change_button_content": "Change", "change_button_content": "Change",

View File

@@ -193,6 +193,7 @@
"warning_message": "Cảnh báo:", "warning_message": "Cảnh báo:",
"loading_message": "Đang tải...", "loading_message": "Đang tải...",
"detail_message": "Chi tiết", "detail_message": "Chi tiết",
"permission_deny_message":"Quyền bị từ chối",
"decline_message": "TỪ CHỐI", "decline_message": "TỪ CHỐI",
"allow_message": "CHO PHÉP", "allow_message": "CHO PHÉP",
"add_button_content": "Thêm", "add_button_content": "Thêm",

View File

@@ -0,0 +1,57 @@
import 'dart:developer';
import 'dart:io';
import 'package:app_settings/app_settings.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sfm_app/product/base/widget/dialog/request_permission_dialog.dart';
class LocationPermissionRequest {
LocationPermissionRequest._init();
static LocationPermissionRequest? _instance;
static LocationPermissionRequest get instance =>
_instance ??= LocationPermissionRequest._init();
Future<bool> checkLocationPermission(context) async {
var status = await Permission.location.status;
log("Status: $status");
if (status.isDenied || status.isPermanentlyDenied) {
requestLocationPermisson(context, Icons.location_on_outlined,
Permission.location, AppSettingsType.location, "Location");
return false;
} else {
return true;
}
}
void requestLocationPermisson(
context,
IconData icon,
Permission service,
AppSettingsType appSettingsType,
String serviceName,
) async {
var status = await service.status;
if (status.isDenied) {
await service.request();
} else if (status.isPermanentlyDenied) {
if (Platform.isAndroid) {
RequestPermissionDialog.instance.requestPermissionDialogAndroid(
context,
icon,
serviceName,
appSettingsType,
);
} else if (Platform.isIOS) {
RequestPermissionDialog.instance.requestPermissionDialogIOS(
context,
appSettingsType,
serviceName,
appSettingsType,
);
} else {
log("Location Permission: Undefined Platform");
}
} else {}
}
}

View File

@@ -24,7 +24,7 @@ class NotificationPermission {
} else { } else {
log("NotificationsPermission: User denied permission"); log("NotificationsPermission: User denied permission");
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
RequestPermissionDialog().showRequestPermissionDialog(context, RequestPermissionDialog.instance.requestPermissionDialogAndroid(context,
Icons.location_on_outlined, "ABCDE", AppSettingsType.notification); Icons.location_on_outlined, "ABCDE", AppSettingsType.notification);
} }
} }

View File

@@ -5,7 +5,7 @@ InputDecoration borderRadiusTopLeftAndBottomRight(
BuildContext context, String hintText) => BuildContext context, String hintText) =>
InputDecoration( InputDecoration(
hintText: hintText, hintText: hintText,
border: OutlineInputBorder( border: const OutlineInputBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(12), bottomRight: Radius.circular(12)), topLeft: Radius.circular(12), bottomRight: Radius.circular(12)),
)); ));