I am trying to create a sample app like Uber with google_maps_flutter: ^0.5.10 .. In Uber app when you select a pickup/drop location, and you do double tap (to zoom in) or zoom in/zoom out scaling the screen it always happens respective to the center of screen. Whereas when I add the google maps in flutter and if I do double tap lets say on right bottom of screen, google maps zoom and takes the view to the area where it was tapped. Similarly it happens with the scaling (pinching screen). The area I scale the camera view goes to that particular location. But I want double tap or scaling to stay centered to my selected location (like in Uber).
I did achieve it using cameraMove and cameraStop callbacks. In that I determine if camera was moved due to a zoom, I get the last zoom factor and move camera back to my selected location with the new zoom factor. It works but does not looks good if I double tap on left corner the camera goes back to original position. Similarly I did for the scaling zoom as well to zoom in/zoom out the camera moves back to original location I selected.
Then I tried GestureDetector on top of the GoogleMaps. Inside that I handled the onDoubleTap. That looks neat as I just change the zoom factor on double tap and keep location same. But I am not able to achieve scaling zoom in/out using onScaleUpdate (etc) functions on GestureDetector.
Is there any better way in Google maps flutter plugin to do all this?
use onCameraMove this will listen to the change of the cameraPosition.
for instance if you zoom in using double tap or zoom in/out using gestures,
GoogleMap(
zoomGesturesEnabled: true,
tiltGesturesEnabled: false,
onCameraMove:(CameraPosition cameraPosition){
print(cameraPosition.zoom);
},
onCameraMove take a void function and pass to it the CameraPosition argument, so you can use
"cameraPosition.zoom" to return the current zoom level; "cameraPosition.tilt" return the current tilt angle ...
use zoomGesturesEnabled: true
like that :
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class ZoomInOutMaps extends StatefulWidget {
#override
_ZoomInOutMapsState createState() => _ZoomInOutMapsState();
}
class _ZoomInOutMapsState extends State<ZoomInOutMaps> {
Completer<GoogleMapController> _controller = Completer();
static const LatLng _center = const LatLng(45.521563, -122.677433);
void _onMapCreated(GoogleMapController controller) {
_controller.complete(controller);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Zoom in and Out of Google Maps'),
backgroundColor: Colors.red,
),
body: GoogleMap(
//enable zoom gestures
zoomGesturesEnabled: true,
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
),
),
);
}
}
Use below for handling min and max zoom:
minMaxZoomPreference: MinMaxZoomPreference(13,17)
Related
I am using Stream Builder that sends api request after some seconds and fetch coordinates (lat, lng).
I want to update the map location (camera position & marker) on newly fetch coordinates.
But Camera position is updating and not focusing on new coordinates.
My google map widget is inside of stream builder and in which we are passing data through snapshot
e.g latlng = LatLng(snapshot.data['lat'],snapshot.data['lng']) to markers: SetMarker( markers, markerID, latlng, ), & initialCameraPosition: CameraPosition( target: latlng, zoom: 9.0, )
The GoogleMaps widget on Flutter does have the attribute initialCameraPosition but as the name says this is just an initial value, if you want to update the map once this has loaded i'd recommend the use of the callback onMapCreated, where you can use the controller to animate to a certain position.
Doing so would look something like this:
onMapCreated: (GoogleMapController controller) {
controller.animateCamera(
CameraUpdate.newCameraPosition(CameraPosition(
target: LatLng(snapshot.data['lat'],
snapshot.data['lng']),
zoom: 12)));
setState((){});
}
Hope this helps! ;)
I have created a Flutter app that uses Google maps. It gets the user's location and centers on that point. As the user walks around the camera will follow it but ONLY if the user hasn't manually moved the camera themselves (_userMovedMap = 0). Once they have moved the map it will no longer follow them...
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
),
body: Listener(
onPointerMove: (e) {
_userMovedMap = true;
},
child: GoogleMap(
onMapCreated: _onMapCreated,
myLocationButtonEnabled: true,
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: _center,
zoom: _zoom,
),
),
),
),
);
}
Here is the location update listener, which also tells the camera to move if "_userMovedMap" is false :
Geolocator.getPositionStream().listen((Position _newLocation) {
//update position with new location data
setState(() {
_center = LatLng(_newLocation.latitude, _newLocation.longitude);
//Move camera to center of location if !_userMovedMap
if (!_userMovedMap) moveCamera();
});
});
...this all works great.
But I want to listen for the user clicking the generic "my location" button (standard google button shown in the top-right of the map). The button can do what its supposed to (animates the camera to current location), but I aso want it to reset "_userMovedMap" to false again.
From what I've seen it's something to do with "setOnMyLocationButtonClickListener", but Google Maps Flutter widget doesn't recognise it.
Can anyone help?
Thanks
I couldn't find an answer - I don't think it is currently possible. It seems to be a "severe" request in a later googlemaps plugin update, but nothing is on the roadmap.
Therefore I made my own location button.
I'm using Flutter's sliding_up_panel & my app has a glitch.
I have a Googlemap as the back panel, When you select a location from the app drawer it animates the map camera to a marker, and the sliding up panel 'peeks', revealing the top of the panel, using the panel's minHeight: property. If the peek interests, the user can then choose to pull up to reveal more detail on the marker location, contained in a ListView.
The problem comes if the users selects a new location from the app drawer. The panel closes, the Map camera animates to the new marker, then the panel provides a 200px panel peek for the new location, but the scroll position is preserved from the prior panel's details. So I can have a peek that's supposed to show the title of the new Marker position, but in fact shows a slither of picture, or text half way down the panel.
I've tried using
slideUpPanelScrollController.jumpTo(0);
To jump the panel ListView to the top, but Android Studio Logcat says
ScrollController not attached to any scroll views.
'package:flutter/src/widgets/scroll_controller.dart':
Failed assertion: line 171 pos 12: '_positions.isNotEmpty'
Clearly, I don't understand how to attach SlidingPanel's ScrollController to the panel's ListView. My understanding of controllers could be improved.
Help appreciated.
SlidingUpPanel(
key: Key("slidingUpPanelKey"),
borderRadius: new BorderRadius.vertical(top: Radius.circular(8)),
parallaxEnabled: true,
controller: slidingPanelController,
onPanelOpened: () async {
setState(() {
panelMinHeight = 0;
});
},
onPanelClosed: () {
slideUpPanelScrollController.jumpTo(0);
},
minHeight: panelMinHeight,
maxHeight: MediaQuery.of(context).size.height - AppBar().preferredSize.height,
panelBuilder: (slideUpPanelScrollController) => _scrollingList(slideUpPanelScrollController, presentLocation),
body: Animarker(
// Other properties
curve: Curves.ease,
//markers: animatedMarkerMap.values.toSet(),
isActiveTrip: false,//rippleAnimationActive,
rippleRadius: 0.1, //[0,1.0] range, how big is the circle
rippleColor: myColorSwatch.gold, // Colors.teal Color of fade ripple circle
rippleDuration: Duration(milliseconds: 2600), //Pulse ripple duration
shouldAnimateCamera: false, // TODO stops weird camera centering movement?
mapId: _mapController.future.then<int>((value) => value.mapId),
child: GoogleMap(
key: Key("myGoogleMap"),
mapType: MapType.hybrid,
initialCameraPosition: _initialCameraPosition,
//TODO markers: _markers,
markers: animatedMarkerMap.values.toSet(),
circles: mapCircles, // used for highlighting a location selected from the drawer
myLocationEnabled: true,
zoomControlsEnabled: false, // TODO would prefer them on screen, but 1/2 off!
onMapCreated: (GoogleMapController controller) {
print("GoogleMaps onMapCreated fired");
_mapController.complete(controller);
},
),
),
),
I attach the ScrollController to the ListView here:
Widget _scrollingList(ScrollController slideUpPanelScrollController, LocationDetails presentLocation) {
return ListView(controller: slideUpPanelScrollController,
key: Key("scrollingPanelListView"),
children: [...
The Sliding Panel should show the top of it's ListView, with a draggable indicator, title, subtitle like so
But if I open a new location from the app drawer, squirting a new location's data into the panel, it doesn't reset the Panel's ScrollPosition, it maintains it, showing the user info half way down:
Did you attach the slideUpPanelScrollController to the ListView:
ListView(
controller: slideUpPanelScrollController
https://pub.dev/packages/flutter_animarker
Wow, this is annoying. So Animarker is a Widget parent for my Google map. The map is busy with Markers. When the user needs to find a particular Marker from the drawer, they tap the item & it triggers a Ripple effect in the corresponding Marker.
The problem is it never stops rippling. It'll even attempt to animate several Markers if the user keeps on tapping.
It was love at first animation but I'm climbing the walls now!
Animarker(
isActiveTrip: rippleAnimationActive,
rippleRadius: 0.5, //[0,1.0] range, how big is the circle
rippleColor: Colors.teal, // Color of fade ripple circle
rippleDuration: Duration(milliseconds: 2500), //Pulse ripple duration
markers: animatedMarkerSet,
mapId: _mapController.future.then<int>((value) => value.mapId),
child: GoogleMap(
key: Key("myGoogleMap"),
mapType: MapType.hybrid,
initialCameraPosition: _edinburghCamera,
markers: _markers,
myLocationEnabled: true,
onMapCreated: (GoogleMapController controller) {
_mapController.complete(controller);
},
),
),
I trigger the animation in a Drawer item onTap():
onTap: () {
animatedMarkerSet = {
RippleMarker(
markerId: MarkerId(presentLocation.name),
position: presentLocation.latLng,
ripple: true) };
setState(() {
presentLocation = _location;
animatedMarkerSet;
rippleAnimationActive = true;
});
//setState(() => _markers.add(marker));
Navigator.of(context).pop();
},
You can see I've created a brand new Marker to animate, and added it to the animatedMarkerSet. This works fine.
When the user taps a Marker, any Marker, details about the location slide up. And I try to deactivate ANY Marker which is rippling. However no matter what I try it doesn't stop:
setState(() {
rippleAnimationActive = false;
animatedMarkerSet.clear(); // nuke from orbit
}
I've also tried changing the animatedMarkerSet to a Map, and attempting to deactivate the ripple using this:
void newLocationUpdate(LocationDetails oldLocation) {
var marker = RippleMarker(
markerId: MarkerId(oldLocation.name),
position: oldLocation.latLng,
ripple: false,
);
setState(() => animatedMarkerMap[MarkerId(oldLocation.name)] = marker);
}
But that doesn't work either.
I really really just need to switch the animation on and off. I don't see why Animarker's so resistant to setState(), telling it to stop.
In fact, the best thing might be to just have it run for 5 seconds then switch off, in all circumstances, but I fail to see how to do that either. Grrrr!
OK well I figured it out. AniMarker which takes the Set of animated Markers, is final. So once you hand it the set of say RippleMarkers, it'll animate them, but you can't then turn it off via a SetState() call.
I tried editing the AniMarker class, to make it not final, so you could mutate it's dataset, but I think because it's built on GoogleMap's Marker class, and they're final, it just will not work.
So AniMarker presently has limited utility.
My work around was just to mutate GoogleMaps marker: option, so I could highlight a Marker by changing it's color. It's also possible to change the circles: GoogleMap option to dyanically highlight a Marker according to user interaction.
I'm a novice in Flutter, and I encountered an issue with google maps plugin. I watched a couple of tutorials in order to get the current position of the camera and Most of them was using GoogleMapController.cameraPosition.target. I think they deleted this method from the controller(Since it is still on the development stage). Is there any other way of getting the current position of the camera?
If you are using the google_maps_flutter package:
The GoogleMap widget have the onCameraMove function that returns the CameraPosition while dragging the map or moving the camera.
To do this, you'll need to create a callback function called _getCameraPosition(CameraPosition cameraPosition which will be invoked when onCameraMove is called. For example:
void _getCameraPosition(CameraPosition cameraPosition) {
// You can do whatever you want with cameraPosition here
log("cameraPosition: " + cameraPosition.target.toString());
}
Then, you'll need to put the _getCameraPosition function to the onCameraMove field on GoogleMap widget, like this:
GoogleMap(
onCameraMove: _getCameraPosition, // pass it here
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: LatLng(-33.86882, 151.209296),
zoom: 12,
),
),
As a result, you will get a LatLng value in the debug console. For example:
cameraPosition: LatLng(-33.8940124943736, 151.2027569487691)