I just started learning flutter and I am trying to build a mobile app using google maps.
I am following a tutorial which is bulding an app that track my position all the time:
It is working pretty good, the problem is that when ever I try to zoom in/out it take me back to my position with the default zoom even if I am not mooving.
I am trying to be able to zoom in/out even if i am moving and tak me back my position only when i click on button.
here is the source code:
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Maps',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Map Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
StreamSubscription _locationSubscription;
Location _locationTracker = Location();
Marker marker;
Circle circle;
GoogleMapController _controller;
static final CameraPosition initialLocation = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
Future<Uint8List> getMarker() async {
ByteData byteData = await DefaultAssetBundle.of(context).load("assets/car_icon.png");
return byteData.buffer.asUint8List();
}
void updateMarkerAndCircle(LocationData newLocalData, Uint8List imageData) {
LatLng latlng = LatLng(newLocalData.latitude, newLocalData.longitude);
this.setState(() {
marker = Marker(
markerId: MarkerId("home"),
position: latlng,
rotation: newLocalData.heading,
draggable: false,
zIndex: 2,
flat: true,
anchor: Offset(0.5, 0.5),
icon: BitmapDescriptor.fromBytes(imageData));
circle = Circle(
circleId: CircleId("car"),
radius: newLocalData.accuracy,
zIndex: 1,
strokeColor: Colors.blue,
center: latlng,
fillColor: Colors.blue.withAlpha(70));
});
}
void getCurrentLocation() async {
try {
Uint8List imageData = await getMarker();
var location = await _locationTracker.getLocation();
updateMarkerAndCircle(location, imageData);
if (_locationSubscription != null) {
_locationSubscription.cancel();
}
_locationSubscription = _locationTracker.onLocationChanged().listen((newLocalData) {
if (_controller != null) {
_controller.animateCamera(CameraUpdate.newCameraPosition(new CameraPosition(
bearing: 192.8334901395799,
target: LatLng(newLocalData.latitude, newLocalData.longitude),
tilt: 0,
zoom: 18.00)));
updateMarkerAndCircle(newLocalData, imageData);
}
});
} on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') {
debugPrint("Permission Denied");
}
}
}
#override
void dispose() {
if (_locationSubscription != null) {
_locationSubscription.cancel();
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: GoogleMap(
mapType: MapType.hybrid,
initialCameraPosition: initialLocation,
markers: Set.of((marker != null) ? [marker] : []),
circles: Set.of((circle != null) ? [circle] : []),
onMapCreated: (GoogleMapController controller) {
_controller = controller;
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.location_searching),
onPressed: () {
getCurrentLocation();
}),
);
}
}
Please help me guys!
Thanks.
You can try this
Declare a variable that hold you position as your location changes
Map _position;
The in your location change listener
_locationSubscription = _locationTracker.onLocationChanged().listen((newLocalData) {
setState(() {
_position = {
"lat": newLocalData.latitude,
"lng": newLocalData.longitude,
"heading": newLocalData.heading,
};
});
if (_controller != null) updateMarkerAndCircle(newLocalData, imageData);
});
Finally, call this method each time you want to center the map to your current position
void _gotoCurrentPosition() {
if (null != _controller && null != _position) {
_controller.animateCamera(CameraUpdate.newCameraPosition(new CameraPosition(
bearing: _position["heading"],
target: LatLng(_position["lat"], _position["lng"]),
tilt: 0,
zoom: 18.00),
),);
}
}
The idea is your application is focusing on your location and when you try to override an action it will continue to focus on the first taste which focusing your location. you have to separate your methods so when your application focusing on your location, you can still cancel the focus by override the first method onTap or onCameraMove.
_locationSubscription = _locationTracker.onLocationChanged().listen((newLocalData) {
if (_controller != null) {
_controller.animateCamera(CameraUpdate.newCameraPosition(new CameraPosition(
bearing: 192.8334901395799,
target: LatLng(newLocalData.latitude, newLocalData.longitude),
tilt: 0,
zoom: 18.00)));
updateMarkerAndCircle(newLocalData, imageData);
}
});
This is the reason why the maps take your back to your location when you are trying to move it, because the camera always animated to your location position
Related
I am working on flutter app where its all start with the user login to app,then in the home screen app check if if location is enabled,and if is not enabled the app ask for permission, i granted it but it does not show the widget as i expected it only stack on widget where location is null but the second time when i hot restart it enter as i expect so what is going on when location is off and when i granted for the first time,Also as user move i expect the location to be updated on the map to current location but both of these scenario they are not occuring,I really really need help.
Here are my code in homepage for listening location change
final initialPosition = LatLng(-6.77146, 39.23958);
_getCurrentLocation() async {
try {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text("Location Service Disabled"),
content: Text("Please enable location service"),
actions: <Widget>[
TextButton(
child: CustomText(text: "OK", color: black, size: 20),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied ||
permission == LocationPermission.deniedForever) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return;
}
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best,
);
setState(() {
currentPosition = position;
});
_updateCameraPosition(position);
_locationSubscription = await Geolocator.getPositionStream(
locationSettings: LocationSettings(
accuracy: LocationAccuracy.best,
distanceFilter: 5,
),
).listen((position) {
setState(() {
currentPosition = position;
print("###################################:${currentPosition}");
});
_updateCameraPosition(position);
});
} catch (e) {
print("**********************************:${e}");
}
}
Here is my code in build method
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: currentPosition != null
? Stack(children: [
GoogleMap(
padding: EdgeInsets.only(bottom: bottomPaddingOfMap),
mapType: MapType.normal,
myLocationButtonEnabled: true,
zoomGesturesEnabled: false,
zoomControlsEnabled: true,
myLocationEnabled: true,
markers: {
Marker(
markerId: const MarkerId('currentLocation'),
position: LatLng(currentPosition!.latitude,
currentPosition!.longitude))
},
// polygons: _polygons,
initialCameraPosition: CameraPosition(
target: LatLng(currentPosition!.latitude,
currentPosition!.longitude),
zoom: 18,
),
onMapCreated: (controller) {
_GoogleMapcontroller = controller;
},
),
//othe widgets here
])
: Stack(children: [
GoogleMap(
padding: EdgeInsets.only(bottom: bottomPaddingOfMap),
mapType: MapType.normal,
zoomGesturesEnabled: false,
zoomControlsEnabled: true,
initialCameraPosition: CameraPosition(
target: LatLng(initialPosition.latitude,
initialPosition.longitude),
zoom: 18,
),
),
Here is update camera position
void _updateCameraPosition(Position position) {
if (_GoogleMapcontroller != null) {
_GoogleMapcontroller!.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(position.latitude, position.longitude),
zoom: 18,
),
),
);
}
}
Here is init state method
#override
void initState() {
super.initState();
_getCurrentLocation();
}
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'dart:async';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MapSample(),
);
}
}
class MapSample extends StatefulWidget {
#override
State<MapSample> createState() => MapSampleState();
}
class MapSampleState extends State<MapSample> {
Future<Position> _determinePosition() async {
Position position = await Geolocator.getCurrentPosition();
return position;
}
late GoogleMapController googleMapController;
static final CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Medical Location"),
centerTitle: true,
),
body: GoogleMap(
mapType: MapType.hybrid,
// markers: {_kGooglePlexMarker},
initialCameraPosition: _kGooglePlex,
onMapCreated: (GoogleMapController controller) {
googleMapController = controller;
},
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () async {
Position position = await _determinePosition();
googleMapController.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(position.latitude, position.longitude),
zoom: 14)));
setState(() {});
},
label: Text('Current Location'),
icon: Icon(Icons.location_history),
),
);
}
}
Hello, this is the code I've been working on and it shows users' location on the map when they click the 'current location' button.
What I want to implement is to make the map shows nearby hospitals based on the user's current location. I've searched for documents about this problem but couldn't find one that really can help me. I'm getting the current location by 'LatLng(position.latitude, position.longitude)' which is not specific numbers fixed because location can vary.
I think I need to use 'places API' but except for that, I couldn't figure solutions out yet.
Could anyone help me to solve this problem by explaining codes I need to add?
Thank you so much
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 have to write an app that displays markers from firebase for uni.
I managed to get markers created localy to show up but i cant seem to get markers from Firebase working. im guessing it has to do something to do with how im retreiving the data from firebase but i cant find why on my own. I am loading images and creating Cards from firebase but this is different.
I am trying to retreive data from Firebase like this:
getMarkerData() async{
FirebaseFirestore.instance.collection('buildings').get().then((data){
if(data.docs.isNotEmpty){
for(int i = 0; i < data.docs.length; i++){
initMarker(data.docs[i].data, data.docs[i].id);
}
}
});
}
Then trying to create the markers from the data like this:
GoogleMapController? mapController;
Location location = new Location();
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
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['name'], snippet: specify['adresse'])
);
setState(() {
markers[markerId] = marker;
});
}
I am Calling the getMarkerData() function in initState():
#override
void initState() {
getMarkerData();
super.initState();
}
and then using the markers in the google maps widget:
GoogleMap(
markers: Set<Marker>.of(markers.values),
myLocationEnabled: true,
myLocationButtonEnabled: true,
mapType: MapType.normal,
initialCameraPosition: _initialCameraPosition,
onMapCreated: _onMapCreated,
),
here is my _onMapCreated function
void _onMapCreated(GoogleMapController mapController) async
{
setState(() {
mapController = mapController;
location.onLocationChanged.listen((l) {
});
});
}
here is the whole code (with collapsed drawer):
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'package:mobs/routes/all_allert.dart';
//home seite
class Home extends StatefulWidget {
final User user;
const Home({Key? key, required this.user}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
User? _currentUser = FirebaseAuth.instance.currentUser;
static const _initialCameraPosition = CameraPosition(
target: LatLng(54.332678239573355, 10.180124654249582),
zoom: 16,
);
final list = FirebaseFirestore.instance.collection("buildings");
GoogleMapController? mapController;
Location location = new Location();
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
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['name'], snippet: specify['adresse']));
setState(() {
markers[markerId] = marker;
});
}
getMarkerData() async {
FirebaseFirestore.instance.collection('buildings').get().then((data) {
if (data.docs.isNotEmpty) {
for (int i = 0; i < data.docs.length; i++) {
initMarker(data.docs[i].data, data.docs[i].id);
}
}
});
}
#override
void initState() {
getMarkerData();
super.initState();
}
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(
//titel zeile der home seite
centerTitle: true,
title: Visibility(
child: Text('Hallo ${_currentUser!.displayName}!'),
maintainSize: false,
maintainAnimation: true,
maintainState: true,
visible: !_currentUser!.isAnonymous,
),
backgroundColor: const Color(0xff08345c),
),
drawer: Drawer(...),
body: GoogleMap(
markers: Set<Marker>.of(markers.values),
myLocationEnabled: true,
myLocationButtonEnabled: true,
mapType: MapType.normal,
initialCameraPosition: _initialCameraPosition,
onMapCreated: _onMapCreated,
),
),
);
}
void _onMapCreated(GoogleMapController mapController) async {
setState(() {
mapController = mapController;
location.onLocationChanged.listen((l) {});
});
}
}
and a picture of the app for good measure.
Your problem is that getMarkerData() is an async function, but you can't use await to wait for its result in initState, because initState can't be an async function. As a result, the GoogleMap widget will be built before the data of your markers are received.
One common solution is to use a FutureBuilder, see here: get the data using a future and build GoogleMap with fetched marker data only after the data has been received, and meanwhile display a progress indicator.
Note: one could argue that using setState when receiving marker data should work in this case, because the GoogleMap widget will be rebuilt. I'm not 100% sure but according to my memories this approach works only if you change the entire marker set, and does not reflect changes like markers[markerId] = marker;. But you can give it a try if you like, make sure that you first collect all the markers, and in setState you update the entires marker set passed to GoogleMap widget in markers.
my flutter app is crashing after I press the back arrow on the phone.
how to make the error happen:
1 -
this button sends to a route that uses google maps.
ElevatedButton(
child: Text('Profesional'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyApp2()),
);
},
),
2-
This shows a live map that tracks the position of your phone.
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
class MyApp2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
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),
),
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
GoogleMap(
initialCameraPosition:
CameraPosition(target: _initialcameraposition),
mapType: MapType.normal,
onMapCreated: _onMapCreated,
myLocationEnabled: true,
),
],
),
),
);
}
}
3 -
When I press the back button of the phone, the app crashes and shows me this. which is inside a platform_channel.dart file that I believe is created automatically.
BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance!.defaultBinaryMessenger;
final BinaryMessenger? _binaryMessenger;
#optionalTypeArgs
Future<T?> _invokeMethod<T>(String method, { required bool missingOk, dynamic arguments }) async {
assert(method != null);
final ByteData? result = await binaryMessenger.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null) {
if (missingOk) {
return null;
}
*throw MissingPluginException('No implementation found for method $method on channel $name');*
}
return codec.decodeEnvelope(result) as T?;
}
the program (Visual studio ) highlights the line that I put between * *.