Skip to content

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:

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

MethodDescription
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


Tip: Use animateCamera for smooth transitions and moveCamera for instant jumps without animation.