Skip to content

Draw a Gradient Line in Flutter

This tutorial shows how to draw a line with a color gradient along its length — useful for showing elevation, speed, or progress along a route.

Prerequisites

Before you begin, ensure you have:

Gradient Line Using Multiple Segments

Since Flutter map polylines use a single color, we simulate a gradient by breaking the route into short segments with progressively changing colors:

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

class GradientLineScreen extends StatefulWidget {
  @override
  _GradientLineScreenState createState() => _GradientLineScreenState();
}

class _GradientLineScreenState extends State<GradientLineScreen> {
  MapMetricsController? mapController;

  // Route through European capitals
  final List<LatLng> routePoints = [
    LatLng(40.4168, -3.7038),  // Madrid
    LatLng(48.8566, 2.3522),   // Paris
    LatLng(52.52, 13.405),     // Berlin
    LatLng(48.2082, 16.3738),  // Vienna
    LatLng(41.0082, 28.9784),  // Istanbul
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Gradient Line')),
      body: MapMetrics(
        styleUrl:
            'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (MapMetricsController controller) {
          mapController = controller;
        },
        initialCameraPosition: CameraPosition(
          target: LatLng(47.0, 12.0),
          zoom: 4.0,
        ),
        polylines: _buildGradientPolylines(),
      ),
    );
  }

  /// Build a set of polyline segments that form a gradient
  Set<Polyline> _buildGradientPolylines() {
    final polylines = <Polyline>{};
    final segmentCount = routePoints.length - 1;

    for (int i = 0; i < segmentCount; i++) {
      // Calculate color at this position (blue -> purple -> red)
      final t = i / segmentCount;
      final color = Color.lerp(Colors.blue, Colors.red, t)!;

      polylines.add(
        Polyline(
          polylineId: PolylineId('gradient_$i'),
          points: [routePoints[i], routePoints[i + 1]],
          color: color,
          width: 5,
        ),
      );
    }

    return polylines;
  }
}

Smooth Gradient with Interpolated Points

For a smoother gradient, interpolate extra points between waypoints:

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

class SmoothGradientLineScreen extends StatefulWidget {
  @override
  _SmoothGradientLineScreenState createState() =>
      _SmoothGradientLineScreenState();
}

class _SmoothGradientLineScreenState extends State<SmoothGradientLineScreen> {
  MapMetricsController? mapController;

  final List<LatLng> waypoints = [
    LatLng(40.4168, -3.7038),  // Madrid
    LatLng(48.8566, 2.3522),   // Paris
    LatLng(52.52, 13.405),     // Berlin
    LatLng(48.2082, 16.3738),  // Vienna
    LatLng(41.0082, 28.9784),  // Istanbul
  ];

  /// Interpolate extra points between waypoints for a smoother gradient
  List<LatLng> _interpolate(List<LatLng> points, int stepsPerSegment) {
    final result = <LatLng>[];
    for (int i = 0; i < points.length - 1; i++) {
      final from = points[i];
      final to = points[i + 1];
      for (int s = 0; s < stepsPerSegment; s++) {
        final t = s / stepsPerSegment;
        result.add(LatLng(
          from.latitude + (to.latitude - from.latitude) * t,
          from.longitude + (to.longitude - from.longitude) * t,
        ));
      }
    }
    result.add(points.last);
    return result;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Smooth Gradient Line')),
      body: MapMetrics(
        styleUrl:
            'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (MapMetricsController controller) {
          mapController = controller;
        },
        initialCameraPosition: CameraPosition(
          target: LatLng(47.0, 12.0),
          zoom: 4.0,
        ),
        polylines: _buildSmoothGradient(),
      ),
    );
  }

  Set<Polyline> _buildSmoothGradient() {
    final smoothPoints = _interpolate(waypoints, 20);
    final polylines = <Polyline>{};

    for (int i = 0; i < smoothPoints.length - 1; i++) {
      final t = i / (smoothPoints.length - 1);
      final color = Color.lerp(Colors.green, Colors.red, t)!;

      polylines.add(
        Polyline(
          polylineId: PolylineId('smooth_$i'),
          points: [smoothPoints[i], smoothPoints[i + 1]],
          color: color,
          width: 5,
        ),
      );
    }

    return polylines;
  }
}

Elevation-Based Gradient

Color the line based on simulated elevation data:

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

class ElevationGradientScreen extends StatefulWidget {
  @override
  _ElevationGradientScreenState createState() =>
      _ElevationGradientScreenState();
}

class _ElevationGradientScreenState extends State<ElevationGradientScreen> {
  MapMetricsController? mapController;

  // Points with simulated elevation data (meters)
  final List<Map<String, dynamic>> routeWithElevation = [
    {'lat': 48.8584, 'lng': 2.2945, 'elevation': 30},   // Eiffel Tower
    {'lat': 48.8620, 'lng': 2.3100, 'elevation': 45},
    {'lat': 48.8650, 'lng': 2.3200, 'elevation': 80},
    {'lat': 48.8700, 'lng': 2.3300, 'elevation': 60},
    {'lat': 48.8750, 'lng': 2.3350, 'elevation': 100},
    {'lat': 48.8800, 'lng': 2.3400, 'elevation': 130},  // Montmartre hill
    {'lat': 48.8867, 'lng': 2.3431, 'elevation': 130},  // Sacre-Coeur
  ];

  /// Map elevation to a color (green = low, yellow = medium, red = high)
  Color _elevationColor(int elevation) {
    final minElev = 30.0;
    final maxElev = 130.0;
    final t = ((elevation - minElev) / (maxElev - minElev)).clamp(0.0, 1.0);

    if (t < 0.5) {
      return Color.lerp(Colors.green, Colors.yellow, t * 2)!;
    } else {
      return Color.lerp(Colors.yellow, Colors.red, (t - 0.5) * 2)!;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Elevation Gradient')),
      body: Stack(
        children: [
          MapMetrics(
            styleUrl:
                'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
            onMapCreated: (MapMetricsController controller) {
              mapController = controller;
            },
            initialCameraPosition: CameraPosition(
              target: LatLng(48.8700, 2.3200),
              zoom: 14.0,
            ),
            polylines: _buildElevationPolylines(),
          ),
          // Legend
          Positioned(
            bottom: 16,
            left: 16,
            child: Container(
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(8),
                boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)],
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text('Elevation',
                      style: TextStyle(fontWeight: FontWeight.bold)),
                  SizedBox(height: 4),
                  _legendRow(Colors.green, 'Low (30m)'),
                  _legendRow(Colors.yellow, 'Medium (80m)'),
                  _legendRow(Colors.red, 'High (130m)'),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _legendRow(Color color, String label) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 2),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Container(width: 20, height: 4, color: color),
          SizedBox(width: 6),
          Text(label, style: TextStyle(fontSize: 12)),
        ],
      ),
    );
  }

  Set<Polyline> _buildElevationPolylines() {
    final polylines = <Polyline>{};

    for (int i = 0; i < routeWithElevation.length - 1; i++) {
      final from = routeWithElevation[i];
      final to = routeWithElevation[i + 1];
      final avgElevation = ((from['elevation'] + to['elevation']) / 2).round();

      polylines.add(
        Polyline(
          polylineId: PolylineId('elev_$i'),
          points: [
            LatLng(from['lat'], from['lng']),
            LatLng(to['lat'], to['lng']),
          ],
          color: _elevationColor(avgElevation),
          width: 6,
        ),
      );
    }

    return polylines;
  }
}

Gradient Techniques Comparison

TechniqueSegmentsSmoothnessPerformance
Waypoint segmentsFew (4-10)Visible stepsBest
Interpolated (20/seg)Many (80-200)SmoothGood
Interpolated (50/seg)Very many (200+)Very smoothModerate

Next Steps


Tip: Use Color.lerp() to blend between any two colors. For multi-stop gradients (e.g., green -> yellow -> red), split the t value into ranges and lerp between adjacent colors.