Fly to a Location in Flutter
Smoothly animate the map camera to fly to any location. This is great for navigation UIs where you want the map to glide to a destination.
Prerequisites
Before you begin, ensure you have:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Basic Fly To
Use animateCamera with CameraUpdate.newLatLngZoom to fly to a location:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class FlyToLocationScreen extends StatefulWidget {
@override
_FlyToLocationScreenState createState() => _FlyToLocationScreenState();
}
class _FlyToLocationScreenState extends State<FlyToLocationScreen> {
MapMetricsController? mapController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Fly to a Location')),
body: Column(
children: [
// Buttons row
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.all(8),
child: Row(
children: [
_buildLocationButton('New York', LatLng(40.7128, -74.0060)),
SizedBox(width: 8),
_buildLocationButton('London', LatLng(51.5074, -0.1276)),
SizedBox(width: 8),
_buildLocationButton('Tokyo', LatLng(35.6895, 139.6917)),
SizedBox(width: 8),
_buildLocationButton('Paris', LatLng(48.8566, 2.3522)),
],
),
),
// Map
Expanded(
child: MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
mapController = controller;
},
initialCameraPosition: CameraPosition(
target: LatLng(48.8566, 2.3522),
zoom: 3.0,
),
),
),
],
),
);
}
Widget _buildLocationButton(String label, LatLng target) {
return ElevatedButton(
onPressed: () => _flyTo(target),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(label),
);
}
void _flyTo(LatLng target) {
mapController?.animateCamera(
CameraUpdate.newLatLngZoom(target, 12.0),
);
}
}Tap any button and the map will smoothly fly to that city.
Fly To with Bearing and Tilt
For a more dramatic fly-to effect, change the bearing (rotation) and tilt at the same time:
dart
void _flyToWithPerspective(LatLng target) {
mapController?.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: target,
zoom: 15.0,
bearing: 45.0, // Rotate 45 degrees
tilt: 50.0, // Tilt for a 3D perspective
),
),
);
}Fly To with Custom Duration
Control the animation speed by providing a duration:
dart
void _slowFlyTo(LatLng target) {
mapController?.animateCamera(
CameraUpdate.newLatLngZoom(target, 14.0),
duration: Duration(seconds: 3), // Slow, cinematic flight
);
}
void _fastFlyTo(LatLng target) {
mapController?.animateCamera(
CameraUpdate.newLatLngZoom(target, 14.0),
duration: Duration(milliseconds: 500), // Quick snap
);
}Complete Example: City Tour
Build a city tour that automatically cycles through locations:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
import 'dart:async';
class CityTourScreen extends StatefulWidget {
@override
_CityTourScreenState createState() => _CityTourScreenState();
}
class _CityTourScreenState extends State<CityTourScreen> {
MapMetricsController? mapController;
Timer? tourTimer;
int currentIndex = 0;
bool isTourRunning = false;
final List<Map<String, dynamic>> cities = [
{'name': 'Paris', 'position': LatLng(48.8566, 2.3522)},
{'name': 'New York', 'position': LatLng(40.7128, -74.0060)},
{'name': 'Tokyo', 'position': LatLng(35.6895, 139.6917)},
{'name': 'Sydney', 'position': LatLng(-33.8688, 151.2093)},
{'name': 'Dubai', 'position': LatLng(25.2048, 55.2708)},
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('City Tour'),
actions: [
TextButton.icon(
onPressed: _toggleTour,
icon: Icon(
isTourRunning ? Icons.stop : Icons.play_arrow,
color: Colors.white,
),
label: Text(
isTourRunning ? 'Stop Tour' : 'Start Tour',
style: TextStyle(color: Colors.white),
),
),
],
),
body: Column(
children: [
// City buttons
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.all(8),
child: Row(
children: cities.map((city) {
final isActive = cities[currentIndex]['name'] == city['name'];
return Padding(
padding: EdgeInsets.only(right: 8),
child: ElevatedButton(
onPressed: () {
setState(() {
currentIndex = cities.indexOf(city);
});
_flyTo(city['position']);
},
style: ElevatedButton.styleFrom(
backgroundColor: isActive ? Colors.blue : Colors.grey[300],
foregroundColor: isActive ? Colors.white : Colors.black87,
),
child: Text(city['name']),
),
);
}).toList(),
),
),
// Map
Expanded(
child: MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
mapController = controller;
},
initialCameraPosition: CameraPosition(
target: LatLng(48.8566, 2.3522),
zoom: 3.0,
),
),
),
],
),
);
}
void _flyTo(LatLng target) {
mapController?.animateCamera(
CameraUpdate.newLatLngZoom(target, 12.0),
);
}
void _toggleTour() {
if (isTourRunning) {
tourTimer?.cancel();
setState(() {
isTourRunning = false;
});
} else {
setState(() {
isTourRunning = true;
});
_flyTo(cities[currentIndex]['position']);
tourTimer = Timer.periodic(Duration(seconds: 4), (timer) {
setState(() {
currentIndex = (currentIndex + 1) % cities.length;
});
_flyTo(cities[currentIndex]['position']);
});
}
}
@override
void dispose() {
tourTimer?.cancel();
mapController?.dispose();
super.dispose();
}
}Camera Update Methods
| Method | Description |
|---|---|
CameraUpdate.newLatLng(latlng) | Move to a location, keep current zoom |
CameraUpdate.newLatLngZoom(latlng, zoom) | Move to a location with a specific zoom |
CameraUpdate.newCameraPosition(position) | Move with full control (target, zoom, bearing, tilt) |
CameraUpdate.zoomIn() | Zoom in by 1 level |
CameraUpdate.zoomOut() | Zoom out by 1 level |
CameraUpdate.zoomTo(zoom) | Zoom to a specific level |
Next Steps
- Jump to Locations — Navigate through a series of locations
- Set Pitch and Bearing — Control 3D perspective
- Locate the User — Fly to the user's current GPS position
Tip: Use animateCamera for smooth transitions and moveCamera for instant jumps without animation.