Zooming and Panning in Flutter Maps (SfMaps)
19 Oct 202224 minutes to read
It is possible to zoom in and out for any layer to take a closer look at a specific region by pinching the map, scrolling the mouse wheel or track pad, or using the toolbar on the web. Pan the map to navigate across the regions. You can also customize the zoom level and the center point of the initial rendering.
The procedure for zooming and panning for both layers is very similar.
Shape layer
late MapZoomPanBehavior _zoomPanBehavior;
late MapShapeSource _dataSource;
@override
void initState() {
super.initState();
_dataSource = MapShapeSource.asset(
'assets/world_map.json',
shapeDataField: 'continent',
);
_zoomPanBehavior = MapZoomPanBehavior();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SfMaps(
layers: [
MapShapeLayer(
source: _dataSource,
zoomPanBehavior: _zoomPanBehavior,
),
],
),
);
}
Tile layer
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = MapZoomPanBehavior();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
}
);
}
Customizing the center latitude and longitude
The MapZoomPanBehavior.focalLatLng
is the focal point of the map layer based on which zooming happens. It represents the focal latitude and longitude position of the map layer. You can also get the current focalLatLng after interaction using the MapZoomPanBehavior.focalLatLng property.
To enable panning, set the instance of MapZoomPanBehavior
to MapTileLayer.zoomPanBehavior
. By default, it will be true
.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = MapZoomPanBehavior(
focalLatLng: MapLatLng(27.1751, 50.0421),
);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
}
);
}
Update the center latitude and longitude programmatically
You can change the center latitude and longitude of the shape layer programmatically using the MapZoomPanBehavior.focalLatLng
property.
NOTE
It is applicable for both shape layer and tile layer.
late MapZoomPanBehavior _zoomPanBehavior;
late MapShapeSource _dataSource;
@override
void initState() {
super.initState();
_dataSource = MapShapeSource.asset(
'assets/world_map.json',
shapeDataField: 'continent',
);
_zoomPanBehavior = MapZoomPanBehavior(
focalLatLng: MapLatLng(27.1751, 78.0421),
zoomLevel: 4,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
height: 600,
child: SfMaps(
layers: [
MapShapeLayer(
source: _dataSource,
zoomPanBehavior: _zoomPanBehavior,
),
],
),
),
SizedBox(height: 20),
RaisedButton(
child: Text('Change focalLatLng'),
onPressed: () {
_zoomPanBehavior.focalLatLng = MapLatLng(56.1304, -106.3468);
},
),
],
),
);
}
Customizing the zoom level
You can set the current zoom level of the map layer by using MapZoomPanBehavior.zoomLevel
property.
The default MapZoomPanBehavior.zoomLevel value is 1 which will show the whole map in the viewport for MapShapeLayer and the possible bounds for the MapShapeLayer based on the MapZoomPanBehavior.focalLatLng. You can also get the current zoom level after interaction using the MapZoomPanBehavior.zoomLevel property.
To enable zooming, set the instance of MapZoomPanBehavior
to MapTileLayer.zoomPanBehavior
. By default, it will be true
.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = MapZoomPanBehavior(
focalLatLng: MapLatLng(27.1751, 78.0421),
zoomLevel: 5,
);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
}
);
}
Update the zoom level programmatically
You can change the zoom level of the shape layer programmatically using the MapZoomPanBehavior.zoomLevel
property.
NOTE
It is applicable for both shape layer and tile layer.
late MapZoomPanBehavior _zoomPanBehavior;
late MapShapeSource _dataSource;
@override
void initState() {
_dataSource = MapShapeSource.asset(
'assets/world_map.json',
shapeDataField: 'continent',
);
_zoomPanBehavior = MapZoomPanBehavior(
focalLatLng: MapLatLng(27.1751, 78.0421),
zoomLevel: 2,
);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
height: 600,
child: SfMaps(
layers: [
MapShapeLayer(
source: _dataSource,
zoomPanBehavior: _zoomPanBehavior,
),
],
),
),
SizedBox(height: 20),
RaisedButton(
child: Text('Change zoomLevel'),
onPressed: () {
_zoomPanBehavior.zoomLevel = 7;
},
),
],
),
);
}
Customizing min and max zoom level
You can set the min and max zoom level of the map layer by setting the value to MapZoomPanBehavior.minZoomLevel
and MapZoomPanBehavior.maxZoomLevel
properties. The minimum and maximum zooming levels can be restricted using these properties respectively. The default values of MapZoomPanBehavior.minZoomLevel
and MapZoomPanBehavior.maxZoomLevel
are 1 and 15 respectively.
However, for MapTileLayer, MapZoomPanBehavior.maxZoomLevel may slightly vary depends on the providers. Kindly check the respective official website of the map tile providers to know about the maximum zoom level it supports.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = MapZoomPanBehavior(
focalLatLng: MapLatLng(27.1751, 78.0421),
zoomLevel: 5,
minZoomLevel: 3,
maxZoomLevel: 10,
);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
}
);
}
Double tap zooming
Double tap zooming can be enabled using enableDoubleTapZooming
property. The default value of the enableDoubleTapZooming
is false
.
NOTE
It is applicable for both tile layer and shape layer.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
_zoomPanBehavior = MapZoomPanBehavior(enableDoubleTapZooming: true);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SfMaps(
layers: [
MapTileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
zoomPanBehavior: _zoomPanBehavior,
),
],
),
);
}
Toolbar
The toolbar can be used to perform zoom in, zoom out, and reset operations in the web and desktop platforms. By default, the MapZoomPanBehavior.showToolbar property is true
.
Toolbar customization
You can customize the toolbar items using the following properties:
-
Position - Used to position the toolbar item at the four corners of the map visual bounds using the
position
property. The default value of theposition
property isMapToolbarPosition.topRight
. The possible values aretopRight
,topLeft
,bottomLeft
, andbottomRight
. -
Icon color - used to set color to the three icons using the
iconColor
property. -
Item background color - Used to set background color of the toolbar icons using the
itemBackgroundColor
property. -
Item hover color - Used to set color while hover over the icon using the
itemHoverColor
property.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
_zoomPanBehavior = MapZoomPanBehavior(
focalLatLng: MapLatLng(27.1751, 78.0421),
zoomLevel: 3,
showToolbar: true,
toolbarSettings: MapToolbarSettings(
position: MapToolbarPosition.topLeft,
iconColor: Colors.red,
itemBackgroundColor: Colors.green,
itemHoverColor: Colors.blue,
),
);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: 350,
width: 350,
child: SfMaps(
layers: [
MapTileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
zoomPanBehavior: _zoomPanBehavior,
),
],
),
),
);
}
Zooming callback
Whenever zooming happens, this callback is called. If it returns false, zooming will not happen.
MapZoomDetails contains following properties.
- MapZoomDetails.previousVisibleBounds - provides the visible bounds before the current zooming operation completes i.e. current visible bounds.
- MapZoomDetails.newVisibleBounds - provides the new visible bounds when the current zoom completes. Hence, if it returns false, there will be no changes in the UI.
- MapZoomDetails.previousZoomLevel - provides the zoom level before the current zooming operation completes i.e. current zoom level.
- MapZoomDetails.newZoomLevel - provides the new zoom level when the current zoom completes. Hence, if it returns false, there will be no changes in the UI.
- MapZoomDetails.globalFocalPoint - The global focal point of the pointers in contact with the screen.
- MapZoomDetails.localFocalPoint - The local focal point of the pointers in contact with the screen.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = MapZoomPanBehavior()
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
onWillZoom: (MapZoomDetails detail) {
return true;
},
),
],
);
}
return CircularProgressIndicator();
}
);
}
Panning callback
Whenever panning happens, this callback is called. If it returns false, panning will not happen.
MapPanDetails contains following properties.
- MapPanDetails.previousVisibleBounds - provides the visible bounds before the current panning operation completes i.e. current visible bounds.
- MapPanDetails.newVisibleBounds - provides the new visible bounds when the current pan completes. Hence, if it returns false, there will be no changes in the UI.
- MapPanDetails.zoomLevel - provides the current zoom level.
- MapPanDetails.delta - The difference in pixels between touch start and current touch position.
- MapPanDetails.globalFocalPoint - The global focal point of the pointers in contact with the screen.
- MapPanDetails.localFocalPoint - The local focal point of the pointers in contact with the screen.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = MapZoomPanBehavior();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
onWillPan: (MapPanDetails detail) {
return true;
},
),
],
);
}
return CircularProgressIndicator();
}
);
}
Overriding the zoom pan behavior
Zooming
Whenever zooming happens, this method is called. Subclasses can override this method to do any custom operations based on the details provided in the MapZoomDetails.
MapZoomDetails contains following properties,
- MapZoomDetails.previousVisibleBounds - provides the visible bounds before the current zooming operation completes i.e. current visible bounds.
-
MapZoomDetails.newVisibleBounds - provides the new visible bounds when the current zoom completes. Hence, if the
super.onZooming(details)
is not called, there will be no changes in the UI. - MapZoomDetails.previousZoomLevel - provides the zoom level before the current zooming operation completes i.e. current zoom level.
-
MapZoomDetails.newZoomLevel - provides the new zoom level when the current zoom completes. Hence, if the
super.onZooming(details)
is not called, there will be no changes in the UI. - MapZoomDetails.globalFocalPoint - The global focal point of the pointers in contact with the screen.
- MapZoomDetails.localFocalPoint - The local focal point of the pointers in contact with the screen.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = _CustomZoomPanBehavior();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
}
);
}
class _CustomZoomPanBehavior extends MapZoomPanBehavior {
@override
void onZooming(MapZoomDetails details) {
super.onZooming(details);
// Add the code here.
}
}
NOTE
- When
super.onZooming(details)
is not called, zooming will not happen.
Panning
Whenever panning happens, this method is called. Subclasses can override this method to do any custom operations based on the details provided in the MapPanDetails.
MapPanDetails contains following properties,
- MapPanDetails.previousVisibleBounds - provides the visible bounds before the current panning operation completes i.e. current visible bounds.
-
MapPanDetails.newVisibleBounds - provides the new visible bounds when the current pan completes. Hence, if the
super.onPanning(details)
is not called, there will be no changes in the UI. - MapPanDetails.zoomLevel - provides the current zoom level.
- MapPanDetails.delta - The difference in pixels between touch start and current touch position.
- MapPanDetails.globalFocalPoint - The global focal point of the pointers in contact with the screen.
- MapPanDetails.localFocalPoint - The local focal point of the pointers in contact with the screen.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = _CustomZoomPanBehavior();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
}
);
}
class _CustomZoomPanBehavior extends MapZoomPanBehavior {
@override
void onPanning(MapPanDetails details) {
super.onPanning(details);
// Add the code here
}
}
NOTE
- When
super.onPanning(details)
is not called, panning will not happen.
Reset
You can reset
the map to the MapZoomPanBehavior.minZoomLevel by calling this method.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = MapZoomPanBehavior();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
},
),
FlatButton(
onPressed: () {
_zoomPanBehavior.reset();
},
child: Text('Reset Zoom Level'),
),
],
);
}
HandleEvent
You can override this method to handle pointer events that hit this render object.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = _CustomZoomPanBehavior();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
}
);
}
class _CustomZoomPanBehavior extends MapZoomPanBehavior {
@override
void handleEvent(PointerEvent event, HitTestEntry entry) {
super.handleEvent(event, entry);
// Add the code here
}
}
Paint
You can paint this render object into the given context at the given offset.
late MapZoomPanBehavior _zoomPanBehavior;
@override
void initState() {
super.initState();
_zoomPanBehavior = _CustomZoomPanBehavior();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getBingUrlTemplate(
'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabels?output=json&uriScheme=https&include=ImageryProviders&key=YOUR_KEY'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SfMaps(
layers: [
MapTileLayer(
urlTemplate: snapshot.data as String,
zoomPanBehavior: _zoomPanBehavior,
),
],
);
}
return CircularProgressIndicator();
}
);
}
class _CustomZoomPanBehavior extends MapZoomPanBehavior {
@override
void paint(PaintingContext context, Offset offset) {
super.paint(context, offset);
// Add the code here
}
}
NOTE
You can refer to our Flutter Maps feature tour page for its groundbreaking feature representations. You can also explore our Flutter Maps Zoom & Pan example that shows how to configure a Maps in Flutter.