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:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
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
| Script | Language Examples | Direction |
|---|---|---|
| Arabic | Arabic, Urdu, Pashto | Right-to-Left |
| Hebrew | Hebrew, Yiddish | Right-to-Left |
| Persian | Farsi, Dari | Right-to-Left |
| Thaana | Dhivehi (Maldives) | Right-to-Left |
Key Steps for RTL
- Call
setRTLTextPlugin()early inonMapCreated - Wrap your app/screen with
Directionalityfor UI widgets - Use
TextDirection.rtlfor Arabic/Hebrew text in Flutter widgets - Map labels are handled automatically by the RTL plugin
Next Steps
- Custom Map Styling — Style your map for any region
- Display the Whole World — Global map view
- Add Custom Icons with Markers — Custom markers
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.