I'm using mapbox_gl, which is a Flutter plugin for access to Mapbox services. Application needs to display the initial camera position fed from user's current location acquired via geolocator plugin. On the Android emulator, setting the current location to coordinates for Ljubljana, Slovenia (around 46N, 13E) displays map of Republic of the Congo, which is obviously incorrect.
The MapboxMap widget is built the following way:
class HomeScreen extends StatelessWidget {
final String mapboxToken = 'XXX-TOKEN-XXX';
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _acquireCurrentPosition(),
builder: (BuildContext context, AsyncSnapshot snapshot) =>
snapshot.hasData
? MapboxMap(
accessToken: mapboxToken,
minMaxZoomPreference: MinMaxZoomPreference(6.0, 15.0),
compassEnabled: false,
initialCameraPosition: CameraPosition(
target: snapshot.data,
),
)
: Center(
child: CircularProgressIndicator(),
),
),
);
}
Future<LatLng> _acquireCurrentPosition() async {
Position position = await getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
return LatLng(position.latitude, position.longitude);
}
}
Method _acquireCurrentPosition() correctly acquires latitude/longitude combination, and was tested on Android emulator, iOS Simulator and even physical Android device (Xiaomi Redmi Note 8 Pro). Error with the initial camera position persists even when using a different location.
Any sort of help is greatly appreciated.
After some workarounds, I managed to identify and fix the problem using the following steps:
First, drop the geolocator package and replace it with location package, and explicitly declare location permissions on Android and iOS, via mentioning them in AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
and Info.plist respectively
<key>NSLocationWhenInUseUsageDescription</key>
<string>Shows your location on the map and helps improve the map</string>
Next, instead of setting the initial camera location via initialCameraPosition parameter, you can use the onMapCreated callback:
// This is how you'd build the MapboxMap widget
MapboxMap(
accessToken: mapboxToken,
onMapCreated: (controller) {
_acquireCurrentLocation().then((LatLong location) {
if (location != null) {
controller.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: location,
),
),
);
}
}).catchError((error) => print(error));
},
minMaxZoomPreference: MinMaxZoomPreference(6.0, 15.0),
initialCameraPosition: CameraPosition(
target: LatLng(45.45, 45.45),
),
);
// Method that uses location plugin
Future<LatLng> _acquireCurrentLocation() async {
Location location = new Location();
bool serviceEnabled;
PermissionStatus permissionGranted;
LocationData locationData;
serviceEnabled = await location.serviceEnabled();
if (!serviceEnabled) {
serviceEnabled = await location.requestService();
if (!serviceEnabled) {
return null;
}
}
permissionGranted = await location.hasPermission();
if (permissionGranted == PermissionStatus.denied) {
permissionGranted = await location.requestPermission();
if (permissionGranted != PermissionStatus.granted) {
return null;
}
}
locationData = await location.getLocation();
return LatLng(locationData.latitude, locationData.longitude);
}
My personal recommendation is to change the minimum SDK version for Android to 23.
Related
I am currently using the latest geolocator that is 'geolocator: ^9.0.1'and for some reason am not able to get any data from the position function when i run
bool isLoc = await Geolocator.isLocationServiceEnabled();
print(isLoc);
The result is true which i think means the location services are all enabled but when i run
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.best);
print('position');
nothing is been returned i have tried all the accuracy values low, high, best,lowest, medium am really confused at what could be wrong i have also try it in the initState and also using a *button
Here is the full dart code
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
class GeoLoc extends StatefulWidget {
const GeoLoc({Key? key}) : super(key: key);
#override
State<GeoLoc> createState() => _GeoLocState();
}
class _GeoLocState extends State<GeoLoc> {
#override
void initState() {
super.initState();
getLoc();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(onPressed: () {
getLoc();
},
child: const Text('Get Location'),
),
],
);
}
getLoc() async{
bool isLoc = await Geolocator.isLocationServiceEnabled();
print(isLoc);
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
print(position);
}
}
Below is your code modified, pls try it. you forget to ask for geolocation permission
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
class GeoLoc extends StatefulWidget {
const GeoLoc({Key? key}) : super(key: key);
#override
State<GeoLoc> createState() => _GeoLocState();
}
class _GeoLocState extends State<GeoLoc> {
#override
void initState() {
super.initState();
getLoc()
.then((value) => print("Location: " + value.toJson().toString()));
}
#override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(onPressed: () {
getLoc()
.then((value) => print("Location: " + value.toJson().toString()));
},
child: const Text('Get Location'),
),
],
);
}
getLoc() async{
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
Geolocator.openLocationSettings();
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
Geolocator.openAppSettings();
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
// When we reach here, permissions are granted and we can
// continue accessing the position of the device.
return await Geolocator.getCurrentPosition();
}
}
Also make sure you have below permission in your AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
I have working maps with GoogleMaps in my app but now I need to upload my app to AppGallery and I am trying to do my functionality with Huawei Maps.
However Huawei Maps not acting same as Google Maps and I don't know why.
This is my app, map should be hidden right now by Offstage class, I also tried it with Opacity, same effect and it's not showing properly in Stack class, what is wrong with these maps?
Here is my code for that:
return Builder(
builder: (context) {
bContext = context;
widget.firstLoad = false;
googleMapKey = GlobalKey<GoogleMapDrawerState>(
debugLabel: 'googleMapWidget');
Provider.of<ControllerProvider>(context, listen: false)
.loadGoogleMapKey(googleMapKey);
var googleMap = GoogleMapDrawer(
key: googleMapKey,
mapCompleted: (controller) {
Provider.of<ControllerProvider>(context, listen: false)
.loadController(controller);
if (Theme.of(context).brightness == Brightness.dark) {
Provider.of<ControllerProvider>(context, listen: false)
.changeTheme(Brightness.dark);
}
},
markers:
Provider.of<ControllerProvider>(context, listen: false)
.marker !=
null
? [
Provider.of<ControllerProvider>(context,
listen: false)
.marker
]
: [],
cameraPosition: hm.CameraPosition(
target: hm.LatLng(53.00825 + 0.004, 18.59762), zoom: 15));
return Scaffold(
body: Stack(children: [
Offstage(
offstage: true,
child: googleMap,
),
and here is my class GoogleMapDrawer:
return SizedBox(
height: size.height * 0.51,
width: size.width,
child: /*GoogleMap(
initialCameraPosition: widget.cameraPosition!,
markers: markers != null ? Set.from(markers) : <Marker>{},
onMapCreated: (controller) {
Provider.of<ControllerProvider>(context, listen: false)
.currentMapBrightness = Theme.of(context).brightness;
widget.mapCompleted(controller);
})*/
HuaweiMap(
initialCameraPosition: widget.cameraPosition!,
markers: markers != null ? Set.from(markers) : <Marker>{},
onMapCreated: (controller) {
Provider.of<ControllerProvider>(context, listen: false)
.currentMapBrightness = Theme.of(context).brightness;
widget.mapCompleted(controller);
},
)
);
I left commented code to show that it's the same code for HuaweiMaps as for GoogleMaps.
With Google Maps it was working fine, now its not.
Also in another view maps should be under other widget but with HuaweiMaps its above other widget. Why is working like this and how to fix it?
https://github.com/HMS-Core/hms-flutter-plugin/issues/191
I found this issue, so it's probably problem with Huawei Map package
I am learning Fllutter to build a map based App. I am using the location pub.dev plugin for managing location permissions.
I have created a Location() object Location location = Location()
and when I call
await location.hasPermission()
and I don't have granted permission , it actually requests the permission , without me calling
await location.requestPermission()
This causes many problems , such as asking two permissions at once , something not permitted by Android , so the app crashes , or by removing await location.requestPermission(), the app ask for the user's permission but it does not wait for the result.
I tested it on my Pixel 5 via adb , running Android 12
What is going on? I have not found another reference of this issue.
Here is the full Code Sample:
class Gmap extends StatefulWidget {
const Gmap({ Key? key }) : super(key: key);
#override
State<Gmap> createState() => _GmapState();
}
class _GmapState extends State<Gmap> {
String _mapStyle = "";
late GoogleMapController mapController;
late Future<LatLng> ull ;
Location location = Location();
final LatLng _center = const LatLng(37.983810, 23.727539);
LatLng _userLocation = const LatLng(37.983810, 23.727539);
Future<LatLng> userLocation() async{
PermissionStatus _permissionGranted;
bool _serviceEnabled;
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) {
return _center;
}
}
_permissionGranted = await location.hasPermission(); //it requests permission here
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission()//it requests here again;
if (_permissionGranted != PermissionStatus.granted) {
return _center;
}
}
LocationData l = await location.getLocation();
setState((){
_userLocation = LatLng(l.latitude!, l.longitude!);
});
return _userLocation;
}
void centerLocation(){
CameraPosition userCamera = CameraPosition(
target: _userLocation,
zoom: 14.0,
);
CameraUpdate moveTo = CameraUpdate.newCameraPosition(userCamera);
mapController.animateCamera(moveTo);
}
void _onMapCreated(GoogleMapController controller) async{
mapController = controller;
mapController.setMapStyle(_mapStyle);
centerLocation();
}
#override
void initState() {
super.initState();
rootBundle.loadString('assets/style.txt').then((string) {
_mapStyle = string;
});
ull = userLocation();
}
Widget googleMap(userLocation){
return GoogleMap(
onMapCreated: _onMapCreated,
myLocationEnabled:true,
initialCameraPosition: CameraPosition(
target: userLocation,
zoom: 6.0,
),
zoomControlsEnabled: false, //dont show zoom buttons
compassEnabled: false,
myLocationButtonEnabled: false,
);
}
#override
Widget build(BuildContext context){
return Scaffold(
body: FutureBuilder<LatLng>(
future: ull,
builder: ( context , AsyncSnapshot<LatLng> snapshot){
Widget g;
if(snapshot.hasData){
g = googleMap(_userLocation);
}else if(snapshot.hasError){
g = googleMap(_center);
}else{
g = googleMap(_center);
}
return g;
}
),
floatingActionButton: Column(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
primary: ourGreen,
shape: const CircleBorder(),
padding: const EdgeInsets.all(15),),
child: const Icon(Icons.filter_alt_rounded, size: 27, color: ourDark),),
const SizedBox(height: 9),
ElevatedButton(
onPressed: () {centerLocation(); },
style: ElevatedButton.styleFrom(
primary: const Color(0xFF1A202C),
shape: const CircleBorder(),
padding: const EdgeInsets.all(15)),
child: const Icon(Icons.location_on,
size: 27, color: Colors.greenAccent)),
]),
);
}
}
You can use the permission_handler package and request the permission status like this:
var status = await Permission.camera.status;
if (status.isDenied) {
// We didn't ask for permission yet or the permission has been denied before but not permanently.
}
// You can can also directly ask the permission about its status.
if (await Permission.location.isRestricted) {
// The OS restricts access, for example because of parental controls.
}
See the documentation: https://pub.dev/packages/permission_handler
Update: I never got this to work for some reason following the documentation or the other comments. Here is what worked for me:
Future<LatLng> askPermissionAndGetLocation() async {
if (await Permission.locationWhenInUse.serviceStatus.isEnabled) { //this here checks
//if the permission is granted and if not , it requests it.
if (await Permission.location.request().isGranted) {
LocationData l = await Location().getLocation();
setState(() {
_userLocation = LatLng(l.latitude!, l.longitude!);
});
return _userLocation;
}
}
return Future.error("Location services disabled or restricted");
}
This is the only way I got it to work and I do not see a reason why. If anyone has any insight please leave a comment
I need my app to read step count from Google Fit. I'm using health 3.05 package. For now I copied the example code to see if it works and unfortunately it's not. Of course I did every step from this packge readme. I set up OAuth2 Client ID, I changed gradle.properties as they shown and in AndroidManifest.xml I put <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/> . However after running app I don't get any permission window and when I click the button to get data I got an error "Authorization not granted" in console. What should I do? Thanks
Here is my code that I copied form package example:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:health/health.dart';
class DailyStepsScreen extends StatefulWidget {
#override
_DailyStepsScreenState createState() => _DailyStepsScreenState();
}
enum AppState {
DATA_NOT_FETCHED,
FETCHING_DATA,
DATA_READY,
NO_DATA,
AUTH_NOT_GRANTED
}
class _DailyStepsScreenState extends State<DailyStepsScreen> {
List<HealthDataPoint> _healthDataList = [];
AppState _state = AppState.DATA_NOT_FETCHED;
#override
void initState() {
super.initState();
}
Future<void> fetchData() async {
/// Get everything from midnight until now
DateTime startDate = DateTime(2020, 11, 07, 0, 0, 0);
DateTime endDate = DateTime(2025, 11, 07, 23, 59, 59);
HealthFactory health = HealthFactory();
/// Define the types to get.
List<HealthDataType> types = [
HealthDataType.STEPS,
HealthDataType.WEIGHT,
HealthDataType.HEIGHT,
HealthDataType.BLOOD_GLUCOSE,
HealthDataType.DISTANCE_WALKING_RUNNING,
];
setState(() => _state = AppState.FETCHING_DATA);
/// You MUST request access to the data types before reading them
bool accessWasGranted = await health.requestAuthorization(types);
int steps = 0;
if (accessWasGranted) {
try {
/// Fetch new data
List<HealthDataPoint> healthData =
await health.getHealthDataFromTypes(startDate, endDate, types);
/// Save all the new data points
_healthDataList.addAll(healthData);
} catch (e) {
print("Caught exception in getHealthDataFromTypes: $e");
}
/// Filter out duplicates
_healthDataList = HealthFactory.removeDuplicates(_healthDataList);
/// Print the results
_healthDataList.forEach((x) {
print("Data point: $x");
steps += x.value.round();
});
print("Steps: $steps");
/// Update the UI to display the results
setState(() {
_state =
_healthDataList.isEmpty ? AppState.NO_DATA : AppState.DATA_READY;
});
} else {
print("Authorization not granted");
setState(() => _state = AppState.DATA_NOT_FETCHED);
}
}
Widget _contentFetchingData() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: CircularProgressIndicator(
strokeWidth: 10,
)),
Text('Fetching data...')
],
);
}
Widget _contentDataReady() {
return ListView.builder(
itemCount: _healthDataList.length,
itemBuilder: (_, index) {
HealthDataPoint p = _healthDataList[index];
return ListTile(
title: Text("${p.typeString}: ${p.value}"),
trailing: Text('${p.unitString}'),
subtitle: Text('${p.dateFrom} - ${p.dateTo}'),
);
});
}
Widget _contentNoData() {
return Text('No Data to show');
}
Widget _contentNotFetched() {
return Text('Press the download button to fetch data');
}
Widget _authorizationNotGranted() {
return Text('''Authorization not given.
For Android please check your OAUTH2 client ID is correct in Google Developer Console.
For iOS check your permissions in Apple Health.''');
}
Widget _content() {
if (_state == AppState.DATA_READY)
return _contentDataReady();
else if (_state == AppState.NO_DATA)
return _contentNoData();
else if (_state == AppState.FETCHING_DATA)
return _contentFetchingData();
else if (_state == AppState.AUTH_NOT_GRANTED)
return _authorizationNotGranted();
return _contentNotFetched();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.file_download),
onPressed: () {
fetchData();
},
)
],
),
body: Center(
child: _content(),
)
);
}
}
Step 1 use health: 3.0.4
Step 2 do proper set up for OAuth2 Client ID, download new google-service.json
Step 3 From Android 10. you have to add ACTIVITY_RECOGNITION for getting STEP Count permission in AndroidManifest.xml.
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
Step 4 And then using permission_handler ask for permission.
if (Platform.isAndroid) {
final permissionStatus = Permission.activityRecognition.request();
if (await permissionStatus.isDenied ||
await permissionStatus.isPermanentlyDenied) {
showToast(
'activityRecognition permission required to fetch your steps count');
return;
}
}
FINALLY GOT THIS ISSUE SOLVED!!
So the problem doesn't lie in the version I am using 3.4.0 but still got the problem solved
Authorization not granted. And stuck in loading screen
Stuck in authorization request screen
When you create your OAuth 2.0 consent screen try to add at least 2 email addresses to the TEST USER section and make sure to login from that emails.
Add 2 email addresses in Test User
After that make sure to verify your application from Google, it will work until you test your app once you release the application, it will not work
Verify Your Application from Google
Final Result
Step 1 use health: 3.0.4
Step 2 Add permission function
Step 3 Start your function inside of initstate
I am new to flutter and I am trying something to achieve in this example, I want to update user location after user turns location on, say for suppose user didn't turn on his location first after we give user a pop up saying this application need location on then it should update data but in the below example its not working, please help me out.
Here is the example what I am working on
PS:
Just subscribe to "onLocationChanged" Stream like in the example.
_location.onLocationChanged().listen((Map<String,double> result) {
var latitude = result["latitude"]; //This is called always when the location updates
var longitude = result["longitude"];
});
For showing a popup when the user has no location enabled use this:
try {
currentLocation = await location.getLocation;
} on PlatformException {
await showDialog<dynamic>(
context: context,
builder: (context) {
return AlertDialog(
title: Text("No Location"),
content: Text(
"Please allow this App to use Location or turn on your GPS."),
actions: <Widget>[
FlatButton(
child: Text(
"Ok"
),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}