Toggle Map Interactions in Flutter
This tutorial shows how to enable and disable individual map interactions like zoom, pan, rotate, and tilt at runtime.
Prerequisites
Before you begin, ensure you have:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style ID from the MapMetrics Portal
A Note on Gesture Control
There are no individual zoomGesturesEnabled/scrollGesturesEnabled/rotateGesturesEnabled/tiltGesturesEnabled parameters on MapMetricsView. Instead, MapOptions.gestures takes a single MapGestures object with rotate, pan, zoom, and pitch booleans. Rebuild it (via setState) whenever you want to flip a gesture — the map picks up the new MapGestures on the next MapOptions it receives, same as any other declarative widget property.
Complete Example: Interaction Control Panel
A full control panel that lets users toggle each gesture type independently:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class ToggleInteractionsScreen extends StatefulWidget {
@override
_ToggleInteractionsScreenState createState() => _ToggleInteractionsScreenState();
}
class _ToggleInteractionsScreenState extends State<ToggleInteractionsScreen> {
MapController? mapController;
bool zoomGestures = true;
bool scrollGestures = true;
bool rotateGestures = true;
bool tiltGestures = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Toggle Interactions'),
actions: [
TextButton(
onPressed: _enableAll,
child: Text('All ON', style: TextStyle(color: Colors.white)),
),
TextButton(
onPressed: _disableAll,
child: Text('All OFF', style: TextStyle(color: Colors.white70)),
),
],
),
body: Column(
children: [
// Interaction toggles
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
color: Colors.grey[100],
child: Column(
children: [
_interactionToggle(
'Zoom (pinch)',
Icons.zoom_in,
zoomGestures,
(v) => setState(() => zoomGestures = v),
),
_interactionToggle(
'Scroll (pan)',
Icons.pan_tool,
scrollGestures,
(v) => setState(() => scrollGestures = v),
),
_interactionToggle(
'Rotate (two-finger)',
Icons.rotate_right,
rotateGestures,
(v) => setState(() => rotateGestures = v),
),
_interactionToggle(
'Tilt (two-finger vertical)',
Icons.view_in_ar,
tiltGestures,
(v) => setState(() => tiltGestures = v),
),
],
),
),
// Map
Expanded(
child: MapMetricsView(
options: MapOptions(
initStyle:
'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
initCenter: Position(2.3522, 48.8566), // lng, lat — Paris
initZoom: 13.0,
gestures: MapGestures(
zoom: zoomGestures,
pan: scrollGestures,
rotate: rotateGestures,
pitch: tiltGestures,
),
),
onMapCreated: (controller) => mapController = controller,
),
),
],
),
);
}
Widget _interactionToggle(
String label,
IconData icon,
bool value,
ValueChanged<bool> onChanged,
) {
return SwitchListTile(
title: Row(
children: [
Icon(icon, size: 20, color: value ? Colors.blue : Colors.grey),
SizedBox(width: 8),
Text(label, style: TextStyle(fontSize: 14)),
],
),
value: value,
onChanged: onChanged,
dense: true,
contentPadding: EdgeInsets.symmetric(horizontal: 8),
);
}
void _enableAll() {
setState(() {
zoomGestures = true;
scrollGestures = true;
rotateGestures = true;
tiltGestures = true;
});
}
void _disableAll() {
setState(() {
zoomGestures = false;
scrollGestures = false;
rotateGestures = false;
tiltGestures = false;
});
}
}Presentation Mode
A common use case: lock all interactions for a presentation or kiosk display, then unlock with a button:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class PresentationModeScreen extends StatefulWidget {
@override
_PresentationModeScreenState createState() => _PresentationModeScreenState();
}
class _PresentationModeScreenState extends State<PresentationModeScreen> {
MapController? mapController;
bool isLocked = true;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
MapMetricsView(
options: MapOptions(
initStyle:
'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
initCenter: Position(2.3522, 48.8566),
initZoom: 13.0,
gestures: isLocked ? const MapGestures.none() : const MapGestures.all(),
),
onMapCreated: (controller) => mapController = controller,
),
// Lock indicator and toggle
Positioned(
top: 50,
right: 16,
child: GestureDetector(
onTap: () => setState(() => isLocked = !isLocked),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: isLocked ? Colors.red : Colors.green,
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isLocked ? Icons.lock : Icons.lock_open,
color: Colors.white,
size: 16,
),
SizedBox(width: 6),
Text(
isLocked ? 'Locked' : 'Unlocked',
style: TextStyle(color: Colors.white, fontSize: 13),
),
],
),
),
),
),
],
),
);
}
}Interaction Properties Reference
MapGestures field | Gesture | Default (MapGestures.all()) |
|---|---|---|
zoom | Pinch-to-zoom, double-tap zoom | true |
pan | Drag/pan to move the map | true |
rotate | Two-finger rotation | true |
pitch | Two-finger vertical swipe (3D tilt) | true |
Next Steps
- Disable Scroll Zoom — Focus on zoom control for embedded maps
- Navigation Controls — Add UI buttons when gestures are off
- Map Interactions — Full interaction handling with events
Tip: When disabling gestures, always provide alternative controls (buttons, sliders) so users can still navigate the map.