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:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
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 Type | Visual Property | Mapping Function |
|---|---|---|
| Speed | Color | Red (slow) -> Green (fast) |
| Elevation | Color | Green (low) -> Red (high) |
| Volume | Width | Thin (few) -> Thick (many) |
| Type | Color + Pattern | Category -> fixed style |
| Priority | Opacity | Low -> 0.3, High -> 1.0 |
Next Steps
- Gradient Line — Color gradient along a line
- Add a Polyline — Basic polyline drawing
- Add a GeoJSON Line — GeoJSON-based lines
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.