Skip to content

Data-Driven Line Styling in Flutter

This tutorial shows how to style polylines based on data properties — useful for showing traffic speed, elevation, route type, or any varying attribute along a path.

Prerequisites

Before you begin, ensure you have:

Lines Colored by Category

Style multiple route lines based on their transport type:

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

class DataDrivenLinesScreen extends StatefulWidget {
  @override
  _DataDrivenLinesScreenState createState() => _DataDrivenLinesScreenState();
}

class _DataDrivenLinesScreenState extends State<DataDrivenLinesScreen> {
  MapMetricsController? mapController;

  final List<Map<String, dynamic>> routes = [
    {
      'name': 'Highway A1',
      'type': 'highway',
      'color': Colors.red,
      'width': 5,
      'points': [
        LatLng(48.8566, 2.3522),
        LatLng(49.2583, 2.0833),
        LatLng(49.8941, 2.2958),
        LatLng(50.6292, 3.0573),
      ],
    },
    {
      'name': 'Railway TGV',
      'type': 'rail',
      'color': Colors.blue,
      'width': 3,
      'points': [
        LatLng(48.8766, 2.3822),
        LatLng(49.2100, 2.1300),
        LatLng(49.8500, 2.3500),
        LatLng(50.6300, 3.0700),
      ],
    },
    {
      'name': 'Cycling Path',
      'type': 'bike',
      'color': Colors.green,
      'width': 2,
      'points': [
        LatLng(48.8400, 2.3200),
        LatLng(49.1800, 2.0500),
        LatLng(49.7500, 2.2000),
        LatLng(50.6000, 3.0300),
      ],
    },
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Data-Driven Lines')),
      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(49.5, 2.5),
              zoom: 7.0,
            ),
            polylines: routes.map((route) {
              return Polyline(
                polylineId: PolylineId(route['name']),
                points: route['points'] as List<LatLng>,
                color: route['color'] as Color,
                width: route['width'] as int,
              );
            }).toSet(),
          ),
          // Legend
          Positioned(
            top: 16,
            right: 16,
            child: Card(
              child: Padding(
                padding: EdgeInsets.all(12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text('Transport',
                        style: TextStyle(fontWeight: FontWeight.bold)),
                    SizedBox(height: 6),
                    _legendLine(Colors.red, 5, 'Highway'),
                    _legendLine(Colors.blue, 3, 'Railway'),
                    _legendLine(Colors.green, 2, 'Cycling'),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _legendLine(Color color, int width, String label) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 3),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Container(
            width: 24,
            height: width.toDouble(),
            color: color,
          ),
          SizedBox(width: 8),
          Text(label, style: TextStyle(fontSize: 13)),
        ],
      ),
    );
  }
}

Traffic Speed Lines

Color route segments based on traffic speed:

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

class TrafficSpeedScreen extends StatefulWidget {
  @override
  _TrafficSpeedScreenState createState() => _TrafficSpeedScreenState();
}

class _TrafficSpeedScreenState extends State<TrafficSpeedScreen> {
  MapMetricsController? mapController;

  // Route segments with speed data (km/h)
  final List<Map<String, dynamic>> segments = [
    {
      'from': LatLng(48.8566, 2.3522),
      'to': LatLng(48.8700, 2.3300),
      'speed': 15, // slow — traffic jam
    },
    {
      'from': LatLng(48.8700, 2.3300),
      'to': LatLng(48.8800, 2.3100),
      'speed': 35, // moderate
    },
    {
      'from': LatLng(48.8800, 2.3100),
      'to': LatLng(48.8950, 2.2800),
      'speed': 60, // fast
    },
    {
      'from': LatLng(48.8950, 2.2800),
      'to': LatLng(48.9100, 2.2500),
      'speed': 80, // very fast
    },
    {
      'from': LatLng(48.9100, 2.2500),
      'to': LatLng(48.9200, 2.2200),
      'speed': 25, // slow
    },
    {
      'from': LatLng(48.9200, 2.2200),
      'to': LatLng(48.9350, 2.1900),
      'speed': 55, // moderate-fast
    },
  ];

  /// Map speed to color (red = slow, yellow = moderate, green = fast)
  Color _speedColor(int speed) {
    if (speed < 20) return Colors.red[700]!;
    if (speed < 40) return Colors.orange;
    if (speed < 60) return Colors.yellow[700]!;
    return Colors.green;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Traffic Speed')),
      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.8900, 2.2700),
              zoom: 12.0,
            ),
            polylines: segments.asMap().entries.map((entry) {
              final i = entry.key;
              final seg = entry.value;
              return Polyline(
                polylineId: PolylineId('seg_$i'),
                points: [seg['from'] as LatLng, seg['to'] as LatLng],
                color: _speedColor(seg['speed'] as int),
                width: 6,
              );
            }).toSet(),
          ),
          // Speed legend
          Positioned(
            bottom: 16,
            left: 16,
            child: Card(
              child: Padding(
                padding: EdgeInsets.all(12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text('Speed',
                        style: TextStyle(fontWeight: FontWeight.bold)),
                    SizedBox(height: 4),
                    _speedRow(Colors.red[700]!, '< 20 km/h'),
                    _speedRow(Colors.orange, '20-40 km/h'),
                    _speedRow(Colors.yellow[700]!, '40-60 km/h'),
                    _speedRow(Colors.green, '> 60 km/h'),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _speedRow(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)),
        ],
      ),
    );
  }
}

Line Width by Data

Vary line width based on a data property like passenger volume:

dart
Set<Polyline> _buildVolumeLines() {
  final routes = [
    {
      'name': 'Main Line',
      'volume': 50000,  // daily passengers
      'points': [LatLng(48.85, 2.35), LatLng(49.00, 2.20)],
    },
    {
      'name': 'Branch A',
      'volume': 15000,
      'points': [LatLng(49.00, 2.20), LatLng(49.10, 2.00)],
    },
    {
      'name': 'Branch B',
      'volume': 5000,
      'points': [LatLng(49.00, 2.20), LatLng(49.05, 2.40)],
    },
  ];

  return routes.map((route) {
    // Map volume to width (2-10 pixels)
    final volume = route['volume'] as int;
    final width = ((volume / 50000) * 8 + 2).clamp(2, 10).toInt();

    return Polyline(
      polylineId: PolylineId(route['name'] as String),
      points: route['points'] as List<LatLng>,
      color: Colors.blue,
      width: width,
    );
  }).toSet();
}

Data Mapping Helpers

Data TypeVisual PropertyMapping Function
SpeedColorRed (slow) -> Green (fast)
ElevationColorGreen (low) -> Red (high)
VolumeWidthThin (few) -> Thick (many)
TypeColor + PatternCategory -> fixed style
PriorityOpacityLow -> 0.3, High -> 1.0

Next Steps


Tip: For real-time data like traffic, update the polylines in setState() when new speed data arrives. Split long routes into short segments so each segment can have its own color based on current conditions.