Posted in

How to Move Marker Smoothly on Google Map in Flutter

How to Move Marker Smoothly on Google Map in Flutter
How to Move Marker Smoothly on Google Map in Flutter

In real-time tracking apps, like ride-sharing or delivery services, simply making a map marker jump from one location to the next can feel jarring. A smooth, animated transition provides a much better user experience, showing a clear path of movement.

While the google_maps_flutter package doesn’t offer a built-in “move” animation, you can create one yourself using Flutter’s powerful animation framework.

The Core Concept: Manual Animation

The secret to smooth movement is to rapidly update the marker’s position along a path between its start and end points. Instead of teleporting from A to B, we’ll calculate dozens of intermediate points (C, D, E…) and tell the marker to redraw itself at each one, creating the illusion of motion.

To achieve this, we’ll use a StatefulWidget along with an AnimationController to handle the timing and a Tween to manage the animation’s progress.

Step 1: Setting Up the State

First, you need a StatefulWidget to manage the marker’s changing position and the animation’s state. You must also mix in TickerProviderStateMixin to provide the “ticker” that drives the animation forward frame by frame.

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

class MapScreen extends StatefulWidget {
  const MapScreen({super.key});

  @override
  State<MapScreen> createState() => _MapScreenState();
}

// Add TickerProviderStateMixin to your State class
class _MapScreenState extends State<MapScreen> with TickerProviderStateMixin {
  // We'll add our map and animation logic here
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ...
    );
  }
}
Dart

Step 2: Initializing the Animation Controller

Inside your State class, declare your GoogleMapController, a Marker, and most importantly, an AnimationController. We will initialize the controller in the initState method.

We will also keep track of the marker’s current and target positions.

class _MapScreenState extends State<MapScreen> with TickerProviderStateMixin {
  late GoogleMapController _mapController;
  
  // Store the current marker position
  LatLng _markerPosition = const LatLng(33.6844, 73.0479); // Initial position (e.g., Islamabad)
  late Marker _marker;

  late AnimationController _animationController;
  late Animation<double> _animation;

  // A list of locations to simulate movement
  final List<LatLng> _locations = const [
    LatLng(33.6844, 73.0479), // Islamabad
    LatLng(33.7380, 73.0844), // Faisal Mosque
    LatLng(33.7295, 73.0379), // Daman-e-Koh
    LatLng(33.6938, 72.9752), // Golra Sharif
  ];
  int _locationIndex = 0;

  @override
  void initState() {
    super.initState();
    _marker = Marker(
      markerId: const MarkerId('my_marker'),
      position: _markerPosition,
    );

    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 2), // Animation duration
    );

    _animation = CurvedAnimation(
      parent: _animationController,
      curve: Curves.easeInOut,
    );
  }

  // ... rest of the class
}
Dart

Step 3: Interpolating Between Two LatLng Points

The core of the animation is calculating the intermediate LatLng values. We can create a helper function that performs linear interpolation (lerp) between a start and end location based on the animation’s progress (t, a value from 0.0 to 1.0).

LatLng _lerp(LatLng start, LatLng end, double t) {
  return LatLng(
    start.latitude + (end.latitude - start.latitude) * t,
    start.longitude + (end.longitude - start.longitude) * t,
  );
}
Dart

Step 4: Driving the Animation

Now, we’ll create a method to start the animation. This method will:

  1. Determine the start and end points.
  2. Add a listener to our animation that updates the marker’s position on every tick.
  3. Start the animation.
void _moveMarker() {
  // The starting position of the marker
  final LatLng startPosition = _marker.position;
  
  // The next position from our list
  _locationIndex = (_locationIndex + 1) % _locations.length;
  final LatLng endPosition = _locations[_locationIndex];

  // Remove any existing listeners
  _animation.removeListener(_updateMarkerPosition);

  // Reset the controller to ensure it starts from the beginning
  _animationController.reset();

  // Redefine the animation to go from 0.0 to 1.0
  _animation = Tween<double>(begin: 0.0, end: 1.0).animate(_animationController);

  // Add a listener to update the marker's position
  void _updateMarkerPosition() {
    final newPosition = _lerp(startPosition, endPosition, _animation.value);
    setState(() {
      _marker = _marker.copyWith(positionParam: newPosition);
    });
  }

  _animation.addListener(_updateMarkerPosition);

  // Start the animation
  _animationController.forward();
}
Dart

We call _marker.copyWith() to create a new Marker instance with the updated position, which is necessary for the GoogleMap widget to detect the change and redraw.

Step 5: Building the UI

Finally, let’s put it all together in the build method. We’ll have a GoogleMap to display the marker and a FloatingActionButton to trigger the movement.

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('Smooth Marker Movement'),
    ),
    body: GoogleMap(
      initialCameraPosition: CameraPosition(
        target: _markerPosition,
        zoom: 14,
      ),
      onMapCreated: (controller) {
        _mapController = controller;
      },
      markers: {_marker},
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _moveMarker,
      child: const Icon(Icons.location_on),
    ),
    floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
  );
}

@override
void dispose() {
  _animationController.dispose();
  super.dispose();
}
Dart

Now, when you run the app and tap the button, you’ll see the marker glide smoothly from one point to the next!

Conclusion

By leveraging Flutter’s AnimationController and some simple interpolation math, you can transform a jarring location jump into a fluid, professional-looking animation. This technique is fundamental for any app that involves real-time location tracking and greatly enhances the overall user experience.

Flutter Stuff Is A Team Of Passionate Flutter Developers On A Mission To Empower The Community. We Share Our Expertise And Insights Through Comprehensive Guides, Tutorials, And Resources, Making Flutter Mobile App Development Accessible And Enjoyable For Everyone.

Leave a Reply