Skip to content

Right-to-Left (RTL) Text Support in Flutter

This tutorial shows how to properly display right-to-left scripts like Arabic, Hebrew, and Persian on your MapMetrics Flutter map — ensuring labels and UI elements render correctly.

Prerequisites

Before you begin, ensure you have:

Enable RTL Text Plugin

Load the RTL text plugin so map labels in Arabic, Hebrew, etc. render correctly:

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

class RtlMapScreen extends StatefulWidget {
  @override
  _RtlMapScreenState createState() => _RtlMapScreenState();
}

class _RtlMapScreenState extends State<RtlMapScreen> {
  MapMetricsController? mapController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('RTL Support')),
      body: MapMetrics(
        styleUrl:
            'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (MapMetricsController controller) {
          mapController = controller;
          // Enable RTL text rendering
          controller.setRTLTextPlugin(
            'https://cdn.mapmetrics-atlas.net/basemaps-assets/js/mapmetrics-gl-rtl-text.min.js',
          );
        },
        initialCameraPosition: CameraPosition(
          target: LatLng(25.2048, 55.2708), // Dubai
          zoom: 10.0,
        ),
      ),
    );
  }
}

RTL Map with City Markers

Display markers in Middle Eastern cities with Arabic labels:

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

class ArabicCitiesScreen extends StatefulWidget {
  @override
  _ArabicCitiesScreenState createState() => _ArabicCitiesScreenState();
}

class _ArabicCitiesScreenState extends State<ArabicCitiesScreen> {
  MapMetricsController? mapController;

  final List<Map<String, dynamic>> cities = [
    {'name': 'Dubai', 'nameAr': 'دبي', 'lat': 25.2048, 'lng': 55.2708},
    {'name': 'Abu Dhabi', 'nameAr': 'أبوظبي', 'lat': 24.4539, 'lng': 54.3773},
    {'name': 'Riyadh', 'nameAr': 'الرياض', 'lat': 24.7136, 'lng': 46.6753},
    {'name': 'Cairo', 'nameAr': 'القاهرة', 'lat': 30.0444, 'lng': 31.2357},
    {'name': 'Beirut', 'nameAr': 'بيروت', 'lat': 33.8938, 'lng': 35.5018},
    {'name': 'Amman', 'nameAr': 'عمّان', 'lat': 31.9454, 'lng': 35.9284},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Arabic Cities')),
      body: MapMetrics(
        styleUrl:
            'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (MapMetricsController controller) {
          mapController = controller;
          controller.setRTLTextPlugin(
            'https://cdn.mapmetrics-atlas.net/basemaps-assets/js/mapmetrics-gl-rtl-text.min.js',
          );
        },
        initialCameraPosition: CameraPosition(
          target: LatLng(27.0, 45.0),
          zoom: 4.0,
        ),
        markers: cities.map((city) {
          return Marker(
            markerId: MarkerId(city['name']),
            position: LatLng(city['lat'], city['lng']),
            infoWindow: InfoWindow(
              title: city['nameAr'],
              snippet: city['name'],
            ),
          );
        }).toSet(),
      ),
    );
  }
}

Full RTL App Layout

Build a complete RTL-aware map app with Arabic UI:

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

class RtlAppScreen extends StatefulWidget {
  @override
  _RtlAppScreenState createState() => _RtlAppScreenState();
}

class _RtlAppScreenState extends State<RtlAppScreen> {
  MapMetricsController? mapController;
  bool isRtl = true;

  final List<Map<String, dynamic>> locations = [
    {'nameAr': 'برج خليفة', 'nameEn': 'Burj Khalifa', 'lat': 25.1972, 'lng': 55.2744},
    {'nameAr': 'نخلة جميرا', 'nameEn': 'Palm Jumeirah', 'lat': 25.1124, 'lng': 55.1390},
    {'nameAr': 'دبي مول', 'nameEn': 'Dubai Mall', 'lat': 25.1985, 'lng': 55.2796},
  ];

  @override
  Widget build(BuildContext context) {
    return Directionality(
      textDirection: isRtl ? TextDirection.rtl : TextDirection.ltr,
      child: Scaffold(
        appBar: AppBar(
          title: Text(isRtl ? 'خريطة دبي' : 'Dubai Map'),
          actions: [
            TextButton(
              onPressed: () => setState(() => isRtl = !isRtl),
              child: Text(
                isRtl ? 'EN' : 'عربي',
                style: TextStyle(color: Colors.white, fontSize: 16),
              ),
            ),
          ],
        ),
        body: Column(
          children: [
            // Location list
            Container(
              height: 60,
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                reverse: isRtl, // RTL scroll direction
                padding: EdgeInsets.all(8),
                itemCount: locations.length,
                itemBuilder: (context, i) {
                  final loc = locations[i];
                  return Padding(
                    padding: EdgeInsets.symmetric(horizontal: 4),
                    child: ActionChip(
                      label: Text(isRtl ? loc['nameAr'] : loc['nameEn']),
                      onPressed: () {
                        mapController?.animateCamera(
                          CameraUpdate.newLatLngZoom(
                            LatLng(loc['lat'], loc['lng']),
                            15.0,
                          ),
                        );
                      },
                    ),
                  );
                },
              ),
            ),
            // Map
            Expanded(
              child: MapMetrics(
                styleUrl:
                    'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
                onMapCreated: (MapMetricsController controller) {
                  mapController = controller;
                  controller.setRTLTextPlugin(
                    'https://cdn.mapmetrics-atlas.net/basemaps-assets/js/mapmetrics-gl-rtl-text.min.js',
                  );
                },
                initialCameraPosition: CameraPosition(
                  target: LatLng(25.2048, 55.2708),
                  zoom: 11.0,
                ),
                markers: locations.map((loc) {
                  return Marker(
                    markerId: MarkerId(loc['nameEn']),
                    position: LatLng(loc['lat'], loc['lng']),
                    infoWindow: InfoWindow(
                      title: isRtl ? loc['nameAr'] : loc['nameEn'],
                    ),
                  );
                }).toSet(),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

RTL-Supported Scripts

ScriptLanguage ExamplesDirection
ArabicArabic, Urdu, PashtoRight-to-Left
HebrewHebrew, YiddishRight-to-Left
PersianFarsi, DariRight-to-Left
ThaanaDhivehi (Maldives)Right-to-Left

Key Steps for RTL

  1. Call setRTLTextPlugin() early in onMapCreated
  2. Wrap your app/screen with Directionality for UI widgets
  3. Use TextDirection.rtl for Arabic/Hebrew text in Flutter widgets
  4. Map labels are handled automatically by the RTL plugin

Next Steps


Tip: The RTL text plugin only needs to be loaded once — subsequent map instances in the same app session will use the cached plugin. Call it in onMapCreated on your first map screen.