I am trying to get the user's current location from Google Maps but I am getting an exception in return which I can't seem to understand why it's happening.
The app is simple - get the user's current location, and then zoom the camera onto that location. That's it.
I am going to attach my code and the exception message. The strange thing is that I am getting my LatLng correctly as you can see in the exception message, so why is the camera not pointing to that location?
class Map extends StatefulWidget {
#override
_MapState createState() => _MapState();
}
class _MapState extends State<Map> {
LatLng currentLatLng;
Completer<GoogleMapController> _controller = Completer();
#override
void initState(){
super.initState();
Geolocator.getCurrentPosition().then((currLocation){
setState((){
currentLatLng = new LatLng(currLocation.latitude, currLocation.longitude);
});
});
}
#override
Widget build(BuildContext context) {
print("Current Location --------> " + currentLatLng.latitude.toString() + " " + currentLatLng.longitude.toString());
return MaterialApp(
home: new Scaffold(
body: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(target: currentLatLng),
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
),
);
}
}
note - I am using 'geolocator' and 'google_maps_flutter' from pub.dev - all latest versions.
This is async programming. currentLatLng is null until getCurrentPosition calls it's callback, so you can't just do this:
initialCameraPosition: CameraPosition(target: currentLatLng)
because, as you error shows, currentLatLng is null.
Your two options are:
Set the map to a default position that you define and then update the position using the mapcontroller when getCurrentPosition completes.
Show a loader while currentLatLng is null, and when it's no longer null, show your map.
Here's an example for 2
return MaterialApp(
home: new Scaffold(
body: currentLatLng == null ? Center(child:CircularProgressIndicator()) : GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(target: currentLatLng),
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
),
);
Related
Below are the errors i keep getting when running my code. I researched online and tried changing and adding information in my pupspec.yaml which i saw online, but i still keep getting these errors. I also added some information in my androidmanifest.xml from examples i saw but still kept getting the same errors.
I/PlatformViewsController(32458): Using hybrid composition for platform view: 3
E/BufferItemConsumer(32458): [ImageReader-1080x1812f1m3-32458-0] Failed to release buffer: Unknown error -1 (1)
E/PlatformViewsController(32458): Disposing platform view threw an exception
E/PlatformViewsController(32458): java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.util.AbstractCollection.isEmpty()' on a null object reference
E/PlatformViewsController(32458): at com.google.android.gms.dynamic.DeferredLifecycleHelper.zae(com.google.android.gms:play-services-base##18.0.1:1)
E/PlatformViewsController(32458): at com.google.android.gms.dynamic.DeferredLifecycleHelper.onDestroy(com.google.android.gms:play-services-base##18.0.1:2)
E/PlatformViewsController(32458): at com.google.android.gms.maps.MapView.onDestroy(com.google.android.gms:play-services-maps##18.1.0:1)
E/PlatformViewsController(32458): at io.flutter.plugins.googlemaps.GoogleMapController.destroyMapViewIfNecessary(GoogleMapController.java:891)
E/PlatformViewsController(32458): at io.flutter.plugins.googlemaps.GoogleMapController.dispose(GoogleMapController.java:578)
E/PlatformViewsController(32458): at io.flutter.plugin.platform.PlatformViewsController$1.dispose(PlatformViewsController.java:376)
E/PlatformViewsController(32458): at io.flutter.embedding.en
Below is the code which i am running for the google maps to display in the app.
class CurrentLocationView extends StatefulWidget {
const CurrentLocationView({super.key});
#override
State<CurrentLocationView> createState() => _CurrentLocationViewState();
}
class _CurrentLocationViewState extends State<CurrentLocationView> {
late GoogleMapController googleMapController;
static const CameraPosition initialCameraPosition = CameraPosition(
target: LatLng(
52.586971,
-2.128820,
),
zoom: 14,
);
Set<Marker> markers = {};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: const Text('User current location')),
backgroundColor: Colors.deepPurple[200],
elevation: 0,
),
body: GoogleMap(
initialCameraPosition: initialCameraPosition,
markers: markers,
zoomControlsEnabled: false,
mapType: MapType.normal,
onMapCreated: (GoogleMapController controller) {
googleMapController = controller;
},
),
);
}
}
i start with this code but all what i get white screen
how i can use google map to get current location or choose location.I am using GoogleMap package in flutter to get the required current location.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget { #override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Google Maps Demo', home: MapSample(), ); } }
class MapSample extends StatefulWidget { #override
State createState() => MapSampleState();
}
class MapSampleState extends State {
Completer _controller = Completer();
static final CameraPosition _kGooglePlex = CameraPosition( target:
LatLng(37.42796133580664, -122.085749655962), zoom: 14.4746, );
static final CameraPosition _kLake = CameraPosition( bearing:
192.8334901395799, target: LatLng(37.43296265331129,
-122.08832357078792), tilt: 59.440717697143555, zoom:
19.151926040649414);
#override Widget build(BuildContext context) { return new Scaffold(
body: GoogleMap(
mapType: MapType.hybrid, initialCameraPosition: _kGooglePlex,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
floatingActionButton:
FloatingActionButton.extended(
onPressed: _goToTheLake,
label:
Text('To the lake!'),
icon: Icon(Icons.directions_boat), ),
);
}
Future _goToTheLake() async {
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));
}
}
If you're using Url properly(no spelling/parameter mistakes) with API key and there's no error from the server which happens a lot due to restrictions. Then there are other steps you need to, ask Location permission on app loading using https://pub.dev/packages/permission_handler package like this.
Code can be something like this.
Create an app_persmission_provider file.
class AppPermissionProvider with ChangeNotifier {
PermissionStatus _locationStatus = PermissionStatus.denied;
LatLng? _locationCenter;
final location_package.Location _location =
location_package.Location();
location_package.LocationData? _locationData;
// Getter
get locationStatus => _locationStatus;
get locationCenter => _locationCenter;
get location => _location;
void getLocationStatus() async {
final status = await Permission.location.request();
_locationStatus = status;
notifyListeners();
print(_locationStatus);
}
void getLocation() async {
_locationData = await _location.getLocation();
_locationCenter = LatLng(
_locationData!.latitude as double, _locationData!.longitude as double);
notifyListeners();
}
}
Then declare the provider on the root maybe, and In my case, I'm initializing some functions onInit. By doing so, android will ask for permission for location when your page loads.
#override
void initState() {
super.initState();
appPermission = Provider.of<AppPermissionProvider>(context, listen: false);
appPermission.getLocationStatus();
appPermission.getLocation();
}
And then using consumer.
SafeArea(
child: Consumer<AppPermissionProvider>(
builder: (context, appPermissionProvider, _) => appPermission
.locationCenter ==
null
? const Center(child: CircularProgressIndicator())
:
GoogleMap( myLocationButtonEnabled:
appPermissionProvider.locationStatus ==
PermissionStatus.granted
? true
: false,
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: appPermission.locationCenter,
zoom: 14,
),
onMapCreated: onMapCreated,
mapType: MapType.normal,
compassEnabled: true,
),
),
)
Here, I am checking if permission is granted, also I am only enabling myLocationButton if location permission is granted.
You can use google_maps_flutter package ,
A short example how you can use it.
For more clearity you can visit
Here or you can use geolocator.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
LatLng _initialcameraposition = LatLng(20.5937, 78.9629);
GoogleMapController _controller;
Location _location = Location();
void _onMapCreated(GoogleMapController _cntlr)
{
_controller = _cntlr;
_location.onLocationChanged.listen((l) {
_controller.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(target: LatLng(l.latitude, l.longitude),zoom: 15),
),
);
});
}
I am new to Flutter and I have created a google map using the flutter_google_maps package.
I have the following code in my parent widget,
SizedBox(
child: _showFindHouseModal
? FutureBuilder<Address?>(
future: _locationDataFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Map(
initialLatitude: _userLocation.latitude!.toDouble(),
initialLongitude: _userLocation.longitude!.toDouble(),
markers: const [],
);
}
},
)
: FutureBuilder<Address?>(
future: _showFindHouseModal,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Map( // <---------------------------------------- This one is the problem
initialLongitude: _userLocation.latitude!.toDouble(),
initialLatitude: _userLocation.latitude!.toDouble(),
markers: [
Marker(
markerId: MarkerId('${_housesList.first.id}'),
position: LatLng(_housesList.first.houseLatitude, _housesList.first.houseLongitude),
),
],
);
}
}),
),
In the above code, you can see that I am using a ternary operator. if _showFindHouseModal is true a Map widget is built. If it is not true, the same Map widget will be built but with additional markers. The problem is, those additional markers I am forwarding are not rendered on the screen.
However, I think I figured out the problem. It is in the child widget. (It is that I can not find a solution to the problem)
Let me show the code for the child widget.
class Map extends StatefulWidget {
final List<Marker> markers;
final double initialLatitude;
final double initialLongitude;
const Map({
Key? key,
required this.initialLatitude,
required this.initialLongitude,
required this.markers, // Todo: Make the default to an empty value
}) : super(key: key);
#override
State<Map> createState() => MapState();
}
class MapState extends State<Map> {
late final CameraPosition _initialCameraPosition;
late final Set<Marker> _markers = {};
final Completer<GoogleMapController> _controller = Completer();
#override
void initState() {
super.initState();
_initialCameraPosition = CameraPosition(
target: LatLng(widget.initialLatitude, widget.initialLongitude),
zoom: 12,
);
}
#override
Widget build(BuildContext context) {
return GoogleMap(
mapType: MapType.normal,
initialCameraPosition: _initialCameraPosition,
markers: _markers,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
setState(
() {
_markers.addAll(widget.markers); <--------- This is the problem I think
_markers.add(
Marker(
markerId: const MarkerId('user-marker'),
position: LatLng(widget.initialLatitude, widget.initialLongitude),
),
);
},
);
},
);
}
}
As I have pointed out in the code, I think the problem is, inside the child widget, those markers are added under the onMapCreated property. Since the map is already created in the first FutureBuilder, those markers are not added to the map for some reason. I can not figure out how to add new markers from the second FutureBuilder. The markers I am adding are not passed through.
Can someone please help. I have been trying to find a way for 6 or so hours and could not make it.
try this, change the line you assign the markers
markers: _markers
along this line
markers: Set<Marker>.of(_markers.values),
this may help you
bool mapToggle = false;
Position currentLocation;
GoogleMapController mapController;
GoogleMap googleMap;
var ads = [];
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
MarkerId selectedMarker;
LatLng markerPosition;
bool clientToggle = false;
#override
void initState() {
super.initState();
// GeolocationStatus geolocationStatus = await Geolocator.checkGeolocationPermissionStatus();
// Geolocator.checkPermission();
// Geolocator.getServiceStatusStream();
Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
.then((currloc) {
setState(() {
currentLocation = currloc;
mapToggle = true;
populateClient();
});
});
}
#override
void dispose() {
super.dispose();
}
populateClient() {
kfirestore.collection('marks').get().then((value) {
if (value.docs.isNotEmpty) {
setState(() {
clientToggle = true;
});
for (int i = 0; i < value.docs.length; i++) {
ads.add(value.docs[i].data());
initMarker(value.docs[i].data(), value.docs[i].id);
var _distanceBetweenLastTwoLocations = Geolocator.distanceBetween(
value.docs[i].data()['location'].latitude,
value.docs[i].data()['location'].longitude,
currentLocation.latitude,
currentLocation.longitude,
);
print("bairshiluud:" + _distanceBetweenLastTwoLocations.toString());
if (_distanceBetweenLastTwoLocations < 100) {
SuccessDialog(
title: "Таны байршилтай ойр сурталчилгаа",
titleColor: Colors.green,
description: value.docs[i].data()['adName'],
);
} else {
SuccessDialog(
title: "Таны байршилтай ойр сурталчилгаа",
titleColor: Colors.green,
description: "Таны байршилд ойр сурталчилгаа олдсонгүй.",
);
}
}
}
});
}
void initMarker(specify, specifyId) async {
var markerIdVal = specifyId;
final MarkerId markerId = MarkerId(markerIdVal);
final Marker marker = Marker(
markerId: markerId,
position: LatLng(
specify['location'].latitude,
specify['location'].longitude,
),
infoWindow: InfoWindow(title: specify['adName'], snippet: "Сурталчилгаа"),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRose),
);
setState(() {
markers[markerId] = marker;
});
}
Container(
height: MediaQuery.of(context).size.height - 80,
width: double.infinity,
child: mapToggle
? GoogleMap(
mapType: MapType.hybrid,
compassEnabled: true,
onMapCreated: onMapCreated,
buildingsEnabled: true,
myLocationButtonEnabled: true,
myLocationEnabled: true,
rotateGesturesEnabled: true,
zoomControlsEnabled: true,
zoomGesturesEnabled: true,
indoorViewEnabled: true,
mapToolbarEnabled: true,
tiltGesturesEnabled: true,
scrollGesturesEnabled: true,
initialCameraPosition: CameraPosition(
target: LatLng(currentLocation.latitude,
currentLocation.longitude),
zoom: 15,
),
// circles: circles,
markers: Set<Marker>.of(markers.values),
)
: Center(
child: Text("Loading"),
),
),
1- It is not high possibile but maybe you can change markers: const [] line to just [] without cons keyword.
2- This one more possible than first one, try to cover with one of them future builder, with different widget like SizedBox or give one of them unique key. (but i suggest first approach like; condition? FutureBuilder:SizedBox(child: FutureBuilder)) because, your problem can be releated widget tree rendering. İf this solve your problem I can add a youtube link about that and you can understand the meaning what I am trying to point out.
3- for camera position, after on camera create function call, with help initialed googleMapsController, you can change camera position, camera zoom and some other stuffs with googleMapsController.animateCamera() function,
example; googleMapsController.animateCamera(CameraUpdate.newLatLng(latLng)) will change the google maps view to your new lat long point. So I suggest, dont't use future builder for this, just animate your camera after initialing and you can cover your map widget with IgnorePointer before getting locations, in this way you can ensure the animation can't block from user interaction.
I display in my application a google map and a marker on a predefined position. I also changed the icon of the marker but it is too large and above all does not adapt to the size of the screen and its definition.
I would therefore like to adapt the dimensions of cd nouveau marker to the size of my screen. First of all, how to change its size?
My code is the following:
class _MyAppState extends State<MyApp>{
late GoogleMapController mapController;
....
late BitmapDescriptor iconMareaEstLa;
final List<Marker> _listMk =[];
....
void _onMapCreated(GoogleMapController controller){
mapController = controller;
setState(() {
_listMk.add(
Marker(
markerId: const MarkerId('MKIDPOSMAREA'),
draggable: false,
icon: iconMareaEstLa,
position: (LatLng(posMareaLat,posMareaLng)),
)
);
});
}
....
#override
void initState(){
super.initState();
BitmapDescriptor
.fromAssetImage(
const ImageConfiguration(size:Size(50,50)),
'images/MareaBlancFondBlanc.png')
.then((value) => iconMareaEstLa = value);
}
...
Widget build(BuildContext context) {
MediaQueryData queryData;
queryData = MediaQuery.of(context);
screenW =queryData.size.width;
screenH =queryData.size.height;
return Scaffold(
body: Stack(
children: [
GoogleMap(
markers: Set.of(_listMk),
onMapCreated: _onMapCreated,
mapType: _currentMapType,
myLocationButtonEnabled: false,
zoomControlsEnabled: false,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 14.0
),
),
....
Can someone tell me where and how to specify the dimensions of my new marker.
Merci
YC
I am newbie in flutter, I am trying to set my location as the center point of the map that the app draw on the screen
I am using statefulwidget as the root of my app and add this code :
class MyMap extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyMapState();
}
}
class MyMapState extends State<MyMap> {
GoogleMapController googleMapController;
LocationData currentLocation;
LocationData distinationLocation;
Location location;
#override
void initState() {
location = Location();
setInitSourceAndDestination();
super.initState();
}
setInitSourceAndDestination() async {
currentLocation = await location.getLocation();
}
#override
Widget build(BuildContext context) {
CameraPosition initialCameraPosition = CameraPosition(
target: LatLng(currentLocation.latitude, currentLocation.longitude),);
return GoogleMap(
initialCameraPosition: initialCameraPosition,
onMapCreated: (GoogleMapController controller) => googleMapController = controller,
mapType: MapType.normal,
tiltGesturesEnabled: false,
compassEnabled: true,
myLocationEnabled: true,
);
}
}
But there are a problem I can not solve:
the map is drawn in the screen before currentLocation is set
I tried setState and the problem has not been solved.
How can I make the app draw the map after setInitSourceAndDestination method finish excuting?
What make me confused is that the code at this form the build method will be excuted before setInitSourceAndDestination method finished, but if I add setState and change the currentLocation value inside it to rebuild the screen I noticed that setState executed before build function but it still dont show my location
Add a condition to check the state. If the state is null, then draw a loading indicator. If not null, draw the maps
it can be look like this :
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
class MapScreen extends StatefulWidget {
#override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
double lat;
double long;
LatLng _pickedLoc;
Future _getLocation() async {
LocationData location = await Location().getLocation();
setState(() {
lat = location.latitude;
long = location.longitude;
});
}
_selectLocation(LatLng location) {
_pickedLoc = location;
}
#override
void initState() {
super.initState();
_getLocation();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: lat == null || long == null
? Center(
child: CircularProgressIndicator(),
)
: GoogleMap(
initialCameraPosition: CameraPosition(
zoom: 16,
target: LatLng(lat, long),
),
onTap: _selectLocation,
onCameraMove: (object) => {debugPrint(object.target.toString())},
markers: {
Marker(
markerId: MarkerId("m1"),
position: _pickedLoc,
)
},
),
);
}
}