Disable Scroll Zoom in Flutter
This tutorial shows how to disable or control zoom gestures on your MapMetrics Flutter map. This is useful for maps embedded in scrollable pages where pinch-to-zoom conflicts with page scrolling.
Prerequisites
Before you begin, ensure you have:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Disable Zoom Gestures
Gestures are configured via a MapGestures value on MapOptions.gestures, not individual booleans on the map widget. Pass MapGestures.all(zoom: false) to disable pinch-to-zoom and double-tap-to-zoom while leaving everything else on:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class DisableZoomScreen extends StatefulWidget {
@override
_DisableZoomScreenState createState() => _DisableZoomScreenState();
}
class _DisableZoomScreenState extends State<DisableZoomScreen> {
MapController? mapController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Zoom Disabled')),
body: MapMetricsView(
options: MapOptions(
initCenter: Position(2.3522, 48.8566),
initZoom: 13.0,
initStyle: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
gestures: const MapGestures.all(zoom: false), // Disables pinch-to-zoom
),
onMapCreated: (controller) => mapController = controller,
),
);
}
}The map displays normally but users cannot zoom with gestures. You can still zoom programmatically with mapController?.animateCamera(zoom: ...).
Disable All Gestures
Lock the map completely — no pan, zoom, rotation, or tilt — with MapGestures.none():
MapMetricsView(
options: MapOptions(
initCenter: Position(2.3522, 48.8566),
initZoom: 13.0,
initStyle: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
gestures: const MapGestures.none(),
),
onMapCreated: (controller) => mapController = controller,
)Toggle Zoom On/Off
Let users enable or disable zoom with a switch. Since gestures live on MapOptions, rebuild it in setState:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class ToggleZoomScreen extends StatefulWidget {
@override
_ToggleZoomScreenState createState() => _ToggleZoomScreenState();
}
class _ToggleZoomScreenState extends State<ToggleZoomScreen> {
MapController? mapController;
bool zoomEnabled = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Toggle Zoom'),
actions: [
Row(
children: [
Text(
zoomEnabled ? 'Zoom ON' : 'Zoom OFF',
style: TextStyle(color: Colors.white),
),
Switch(
value: zoomEnabled,
onChanged: (value) {
setState(() {
zoomEnabled = value;
});
},
activeColor: Colors.white,
),
],
),
],
),
body: MapMetricsView(
options: MapOptions(
initCenter: Position(2.3522, 48.8566),
initZoom: 13.0,
initStyle: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
gestures: MapGestures.all(zoom: zoomEnabled),
),
onMapCreated: (controller) => mapController = controller,
),
);
}
}Embedded Map in Scrollable Page
A common pattern: disable gestures on an embedded map, then provide a "tap to interact" overlay:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class EmbeddedMapScreen extends StatefulWidget {
@override
_EmbeddedMapScreenState createState() => _EmbeddedMapScreenState();
}
class _EmbeddedMapScreenState extends State<EmbeddedMapScreen> {
MapController? mapController;
bool isMapActive = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Embedded Map')),
body: SingleChildScrollView(
child: Column(
children: [
// Some content above the map
Container(
padding: EdgeInsets.all(20),
child: Text(
'Scroll down to see the map. Tap the map to interact with it.',
style: TextStyle(fontSize: 16),
),
),
// Embedded map with tap-to-activate
Container(
height: 300,
margin: EdgeInsets.all(16),
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[300]!),
),
child: Stack(
children: [
MapMetricsView(
options: MapOptions(
initCenter: Position(2.3522, 48.8566),
initZoom: 13.0,
initStyle: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
gestures: MapGestures.all(
zoom: isMapActive,
pan: isMapActive,
rotate: isMapActive,
),
),
onMapCreated: (controller) => mapController = controller,
),
// Overlay: tap to activate
if (!isMapActive)
GestureDetector(
onTap: () {
setState(() {
isMapActive = true;
});
},
child: Container(
color: Colors.transparent,
alignment: Alignment.center,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 16,
vertical: 10,
),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Tap to interact with map',
style: TextStyle(color: Colors.white),
),
),
),
),
],
),
),
// More content below
Container(
padding: EdgeInsets.all(20),
child: Text(
'More content below the map...',
style: TextStyle(fontSize: 16),
),
),
],
),
),
);
}
}Gesture Properties
MapGestures (passed as MapOptions.gestures) has four boolean fields, all true by default via MapGestures.all(...):
| Property | Type | Default | Description |
|---|---|---|---|
zoom | bool | true | Pinch-to-zoom and double-tap zoom |
pan | bool | true | Pan/drag to move the map |
rotate | bool | true | Two-finger rotation |
pitch | bool | true | Two-finger tilt for 3D perspective |
Next Steps
- Toggle Interactions — Fine-grained control of all gestures
- Navigation Controls — Add zoom buttons when gestures are disabled
- Map Interactions — Full interaction handling guide
Tip: When embedding a map in a scrollable page, always disable pan gestures so the page scroll works normally. Provide a "tap to interact" overlay to let users opt in.