How to add Animation for Google Maps Marker In Flutter?

4 min read 06-10-2024
How to add Animation for Google Maps Marker In Flutter?


Adding Smooth Animations to Your Google Maps Markers in Flutter

Ever felt your Flutter Google Maps application lacked a touch of dynamism? Adding animations to your map markers can significantly improve user experience and enhance visual appeal. In this article, we'll explore how to incorporate smooth animations into your map markers using Flutter's powerful animation capabilities.

The Problem and Our Solution

Imagine you're building a ride-sharing app where you want to display the movement of nearby drivers. Static markers simply won't cut it. We need a way to animate the markers along their routes, giving users a sense of real-time action.

Getting Started: The Basics

Let's start with a basic Flutter Google Maps setup and a simple marker animation.

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class AnimatedMarkerScreen extends StatefulWidget {
  @override
  _AnimatedMarkerScreenState createState() => _AnimatedMarkerScreenState();
}

class _AnimatedMarkerScreenState extends State<AnimatedMarkerScreen>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  final LatLng _initialPosition = LatLng(37.7749, -122.4194); // San Francisco

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 5),
      vsync: this,
    );
    _animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Animated Marker')),
      body: GoogleMap(
        initialCameraPosition: CameraPosition(
          target: _initialPosition,
          zoom: 12,
        ),
        markers: {
          Marker(
            markerId: MarkerId('animated_marker'),
            position: _initialPosition,
            icon: BitmapDescriptor.defaultMarker,
            anchor: Offset(0.5, 0.5),
            infoWindow: InfoWindow(title: 'Animated Marker'),
          ),
        },
        onMapCreated: (GoogleMapController controller) {
          setState(() {
            controller.animateCamera(
              CameraUpdate.newLatLngBounds(
                LatLngBounds(
                  southwest: LatLng(_initialPosition.latitude - 0.1,
                      _initialPosition.longitude - 0.1),
                  northeast: LatLng(_initialPosition.latitude + 0.1,
                      _initialPosition.longitude + 0.1),
                ),
                10.0,
              ),
            );
          });
        },
      ),
    );
  }
}

This code creates a simple map with a marker placed at San Francisco. The marker doesn't move yet, but it's the foundation we'll build upon.

Animating the Marker: Bringing it to Life

To make the marker move, we'll leverage an AnimationController and an Animation. The Animation will determine the marker's position based on the controller's progress.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Animated Marker')),
      body: GoogleMap(
        initialCameraPosition: CameraPosition(
          target: _initialPosition,
          zoom: 12,
        ),
        markers: {
          Marker(
            markerId: MarkerId('animated_marker'),
            position: LatLng(
              _initialPosition.latitude + (_animation.value * 0.1), 
              _initialPosition.longitude + (_animation.value * 0.1)
            ),
            icon: BitmapDescriptor.defaultMarker,
            anchor: Offset(0.5, 0.5),
            infoWindow: InfoWindow(title: 'Animated Marker'),
          ),
        },
        onMapCreated: (GoogleMapController controller) {
          setState(() {
            controller.animateCamera(
              CameraUpdate.newLatLngBounds(
                LatLngBounds(
                  southwest: LatLng(_initialPosition.latitude - 0.1,
                      _initialPosition.longitude - 0.1),
                  northeast: LatLng(_initialPosition.latitude + 0.1,
                      _initialPosition.longitude + 0.1),
                ),
                10.0,
              ),
            );
          });
        },
      ),
    );
  }

In the build method, we've modified the marker's position to dynamically change based on the value of _animation. This will cause the marker to move diagonally from its initial position.

Enhancing the Animation: Adding Smoothness and Control

We can make the marker movement smoother and more realistic by using an animation curve and controlling the duration of the animation.

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 5),
      vsync: this,
    );
    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    )
        .drive(Tween<double>(begin: 0.0, end: 1.0)); // Smooth animation
    _controller.forward();
  }

Now, instead of moving linearly, the marker will ease in and out smoothly thanks to the Curves.easeInOut curve. You can experiment with different animation curves like Curves.bounceIn or Curves.fastOutSlowIn for different effects.

Building Realistic Animations

Let's take the animation a step further by defining a route for the marker to follow. We'll use a list of LatLng objects to simulate the route.

  final List<LatLng> _route = [
    LatLng(37.7749, -122.4194),
    LatLng(37.7849, -122.4294),
    LatLng(37.7949, -122.4394),
    LatLng(37.8049, -122.4494),
  ];

We'll need to adjust the _animation to handle this route.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Animated Marker')),
      body: GoogleMap(
        initialCameraPosition: CameraPosition(
          target: _initialPosition,
          zoom: 12,
        ),
        markers: {
          Marker(
            markerId: MarkerId('animated_marker'),
            position: _getMarkerPosition(_animation.value),
            icon: BitmapDescriptor.defaultMarker,
            anchor: Offset(0.5, 0.5),
            infoWindow: InfoWindow(title: 'Animated Marker'),
          ),
        },
        onMapCreated: (GoogleMapController controller) {
          setState(() {
            controller.animateCamera(
              CameraUpdate.newLatLngBounds(
                LatLngBounds(
                  southwest: LatLng(_route.first.latitude - 0.1,
                      _route.first.longitude - 0.1),
                  northeast: LatLng(_route.last.latitude + 0.1,
                      _route.last.longitude + 0.1),
                ),
                10.0,
              ),
            );
          });
        },
      ),
    );
  }

  LatLng _getMarkerPosition(double value) {
    // Interpolate between points on the route based on animation value
    final index = (value * _route.length).floor();
    final nextIndex = (value * _route.length).ceil();
    final progress = value * _route.length - index;
    if (nextIndex >= _route.length) {
      return _route.last;
    } else {
      return LatLng(
        _route[index].latitude +
            (progress * (_route[nextIndex].latitude - _route[index].latitude)),
        _route[index].longitude +
            (progress * (_route[nextIndex].longitude - _route[index].longitude)),
      );
    }
  }

We've introduced a new function _getMarkerPosition that interpolates between consecutive points on the route based on the animation value, effectively creating a smooth movement along the defined path.

Additional Tips and Resources

  • Customizing Marker Appearance: You can use custom icons for your markers to make them more visually appealing.
  • Animation Duration and Timing: Play with the animation duration and curves to achieve different visual effects.
  • Multiple Animations: You can create multiple animations for different markers, simulating complex movement patterns.
  • Real-time Data: Integrate real-time data from APIs or databases to update marker positions dynamically.

This article has provided you with a starting point for adding animation to your Google Maps markers in Flutter. Remember to adapt the code and animation techniques to match the specific needs of your application. The possibilities are endless!

Resources: