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: