Skip to content

Change the Case of Labels in Flutter

This tutorial shows how to change the text case of map labels — converting to uppercase, lowercase, or title case at runtime.

Prerequisites

Before you begin, ensure you have:

Change Label Text Transform

Use the text-transform layout property to change label casing:

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

class LabelCaseScreen extends StatefulWidget {
  @override
  _LabelCaseScreenState createState() => _LabelCaseScreenState();
}

class _LabelCaseScreenState extends State<LabelCaseScreen> {
  MapMetricsController? mapController;
  String currentCase = 'none';

  final List<Map<String, String>> caseOptions = [
    {'value': 'none', 'label': 'Default'},
    {'value': 'uppercase', 'label': 'UPPERCASE'},
    {'value': 'lowercase', 'label': 'lowercase'},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Label Case')),
      body: Column(
        children: [
          // Case selector
          Container(
            padding: EdgeInsets.all(12),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: caseOptions.map((option) {
                return ChoiceChip(
                  label: Text(option['label']!),
                  selected: currentCase == option['value'],
                  onSelected: (selected) {
                    if (selected) {
                      setState(() => currentCase = option['value']!);
                      _applyTextTransform(option['value']!);
                    }
                  },
                );
              }).toList(),
            ),
          ),
          // Map
          Expanded(
            child: MapMetrics(
              styleUrl:
                  'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
              onMapCreated: (MapMetricsController controller) {
                mapController = controller;
              },
              initialCameraPosition: CameraPosition(
                target: LatLng(48.8566, 2.3522),
                zoom: 12.0,
              ),
            ),
          ),
        ],
      ),
    );
  }

  void _applyTextTransform(String textCase) {
    // Apply to common label layers in the style
    final labelLayers = [
      'place-city-label',
      'place-town-label',
      'road-label',
      'poi-label',
    ];

    for (final layer in labelLayers) {
      mapController?.setLayoutProperty(layer, 'text-transform', textCase);
    }
  }
}

Custom Label Styling

Combine text case changes with font size and color adjustments:

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

class LabelStyleScreen extends StatefulWidget {
  @override
  _LabelStyleScreenState createState() => _LabelStyleScreenState();
}

class _LabelStyleScreenState extends State<LabelStyleScreen> {
  MapMetricsController? mapController;
  String activePreset = 'default';

  final Map<String, Map<String, dynamic>> presets = {
    'default': {
      'name': 'Default',
      'textTransform': 'none',
      'textSize': 1.0,
      'textColor': '#333333',
    },
    'bold_caps': {
      'name': 'Bold Caps',
      'textTransform': 'uppercase',
      'textSize': 1.2,
      'textColor': '#1a1a1a',
    },
    'subtle': {
      'name': 'Subtle',
      'textTransform': 'lowercase',
      'textSize': 0.8,
      'textColor': '#999999',
    },
    'highlight': {
      'name': 'Highlight',
      'textTransform': 'uppercase',
      'textSize': 1.1,
      'textColor': '#1565c0',
    },
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Label Styling')),
      body: Column(
        children: [
          Container(
            padding: EdgeInsets.all(8),
            child: Wrap(
              spacing: 8,
              children: presets.entries.map((entry) {
                return ChoiceChip(
                  label: Text(entry.value['name']),
                  selected: activePreset == entry.key,
                  onSelected: (selected) {
                    if (selected) {
                      setState(() => activePreset = entry.key);
                      _applyPreset(entry.value);
                    }
                  },
                );
              }).toList(),
            ),
          ),
          Expanded(
            child: MapMetrics(
              styleUrl:
                  'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
              onMapCreated: (MapMetricsController controller) {
                mapController = controller;
              },
              initialCameraPosition: CameraPosition(
                target: LatLng(48.8566, 2.3522),
                zoom: 13.0,
              ),
            ),
          ),
        ],
      ),
    );
  }

  void _applyPreset(Map<String, dynamic> preset) {
    final layers = ['place-city-label', 'place-town-label', 'road-label'];

    for (final layer in layers) {
      mapController?.setLayoutProperty(
        layer, 'text-transform', preset['textTransform'],
      );
      mapController?.setPaintProperty(
        layer, 'text-color', preset['textColor'],
      );
    }
  }
}

Text Transform Options

ValueInputOutput
noneParisParis
uppercaseParisPARIS
lowercaseParisparis

Next Steps


Tip: Uppercase labels look great on maps at low zoom levels (country/state names), while default casing is better at street level where readability matters more.