← Back to Blog

Complete Guide to RTL Language Support in Flutter Apps

flutterlocalizationrtlarabichebrewi18n

Complete Guide to RTL Language Support in Flutter Apps

Building a truly global Flutter app means supporting languages that read right-to-left (RTL). Arabic, Hebrew, Persian, and Urdu represent over 400 million native speakers worldwide. Getting RTL support right isn't just about flipping layouts — it's about creating an authentic experience for these users.

Understanding RTL Languages

Right-to-left languages require more than simple text direction changes. Your entire UI needs to adapt:

Language Native Speakers Script Direction
Arabic 310 million Right-to-left
Hebrew 9 million Right-to-left
Persian (Farsi) 70 million Right-to-left
Urdu 70 million Right-to-left

Key insight: These markets are often underserved in mobile apps, creating a competitive advantage for apps that support them properly.

Setting Up RTL Support in Flutter

Step 1: Configure Your l10n.yaml

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations
synthetic-package: false

Step 2: Add RTL Locales to MaterialApp

import 'package:flutter_localizations/flutter_localizations.dart';

MaterialApp(
  localizationsDelegates: [
    AppLocalizations.delegate,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: [
    Locale('en'),
    Locale('ar'), // Arabic
    Locale('he'), // Hebrew
    Locale('fa'), // Persian
    Locale('ur'), // Urdu
  ],
)

Step 3: Create Your Arabic .arb File

app_ar.arb:

{
  "@@locale": "ar",
  "welcomeMessage": "مرحباً بك في تطبيقنا",
  "@welcomeMessage": {
    "description": "Welcome message shown on the home screen"
  },
  "loginButton": "تسجيل الدخول",
  "@loginButton": {
    "description": "Button to log into the account"
  },
  "itemCount": "{count, plural, =0{لا توجد عناصر} =1{عنصر واحد} =2{عنصران} few{{count} عناصر} many{{count} عنصراً} other{{count} عنصر}}",
  "@itemCount": {
    "description": "Shows the number of items",
    "placeholders": {
      "count": {"type": "int"}
    }
  }
}

Important: Arabic has 6 plural forms (zero, one, two, few, many, other). Your translations must handle all of them.

Automatic Layout Mirroring

Flutter automatically mirrors layouts for RTL locales. Here's what happens:

What Flutter Mirrors Automatically

  • Row children order reverses
  • ListView.builder horizontal scroll direction flips
  • Padding with EdgeInsets directional values swap
  • Alignment values like start and end swap
  • Icons in ListTile position correctly

What You Must Handle Manually

// Use directional edge insets
Padding(
  padding: EdgeInsetsDirectional.only(start: 16, end: 8),
  child: Text('Properly padded content'),
)

// Use TextDirection-aware alignment
Align(
  alignment: AlignmentDirectional.centerStart,
  child: Text('Aligned to start'),
)

Common RTL Mistakes and Fixes

Mistake 1: Hardcoded Left/Right Values

Wrong:

Padding(
  padding: EdgeInsets.only(left: 16),
  child: Text('This breaks in RTL'),
)

Correct:

Padding(
  padding: EdgeInsetsDirectional.only(start: 16),
  child: Text('This works in both directions'),
)

Mistake 2: Non-Directional Icons

Wrong:

Icon(Icons.arrow_back)

Correct:

Icon(Icons.arrow_back_ios) // Automatically mirrors in RTL

// Or use directional icons explicitly
Icon(
  Directionality.of(context) == TextDirection.rtl
    ? Icons.arrow_forward_ios
    : Icons.arrow_back_ios,
)

Mistake 3: Ignoring Text Alignment

Wrong:

Text(
  'Some text',
  textAlign: TextAlign.left,
)

Correct:

Text(
  'Some text',
  textAlign: TextAlign.start, // Respects text direction
)

Mistake 4: Hardcoded Layout Direction

Wrong:

Row(
  textDirection: TextDirection.ltr, // Forces LTR always
  children: [...],
)

Correct:

Row(
  // Let Flutter handle direction based on locale
  children: [...],
)

Testing RTL Layouts

Force RTL for Testing

MaterialApp(
  builder: (context, child) {
    return Directionality(
      textDirection: TextDirection.rtl,
      child: child!,
    );
  },
)

Check Current Direction in Widgets

Widget build(BuildContext context) {
  final isRTL = Directionality.of(context) == TextDirection.rtl;

  return Container(
    child: Text(isRTL ? 'RTL Mode Active' : 'LTR Mode Active'),
  );
}

Debug Layout Issues

Add this to quickly visualize directional problems:

Directionality(
  textDirection: TextDirection.rtl,
  child: Container(
    decoration: BoxDecoration(
      border: Border.all(color: Colors.red),
    ),
    child: YourWidget(),
  ),
)

Bidirectional Text (BiDi) Handling

Sometimes you need to mix RTL and LTR content:

Embedding LTR in RTL Context

Text(
  'السعر: \$99.99', // Price in Arabic with English currency
  textDirection: TextDirection.rtl,
)

Using Unicode Directional Characters

{
  "priceLabel": "السعر: \u200F{price}\u200F دولار",
  "@priceLabel": {
    "description": "Price label with RTL marks around the number",
    "placeholders": {
      "price": {"type": "String"}
    }
  }
}

Unicode control characters:

  • \u200F (RLM) - Right-to-Left Mark
  • \u200E (LRM) - Left-to-Right Mark

Font Considerations for RTL

Arabic Font Requirements

Arabic text requires fonts that support:

  • Character shaping (letters change form based on position)
  • Ligatures (connected letters)
  • Diacritics (vowel marks)

Recommended Arabic fonts:

  • Noto Sans Arabic (Google Fonts)
  • Amiri
  • Cairo
  • Tajawal

Loading Arabic Fonts

// pubspec.yaml
fonts:
  - family: NotoSansArabic
    fonts:
      - asset: fonts/NotoSansArabic-Regular.ttf
      - asset: fonts/NotoSansArabic-Bold.ttf
        weight: 700

// Usage
Text(
  'مرحباً',
  style: TextStyle(
    fontFamily: 'NotoSansArabic',
  ),
)

Automatic Font Selection

TextStyle(
  fontFamilyFallback: ['NotoSansArabic', 'Roboto'],
)

RTL-Specific UI Patterns

Navigation Drawer

Scaffold(
  // Drawer automatically appears on the correct side
  drawer: Drawer(...),  // Left in LTR, Right in RTL
  endDrawer: Drawer(...), // Right in LTR, Left in RTL
)

Swipe Gestures

Dismissible(
  // Swap directions based on locale
  direction: Directionality.of(context) == TextDirection.rtl
    ? DismissDirection.startToEnd
    : DismissDirection.endToStart,
  child: ListTile(...),
)

Progress Indicators

LinearProgressIndicator(
  // Automatically respects text direction
  value: 0.5,
)

Performance Tips for RTL

Avoid Runtime Direction Checks

Less efficient:

Widget build(BuildContext context) {
  final isRTL = Directionality.of(context) == TextDirection.rtl;
  return Padding(
    padding: EdgeInsets.only(
      left: isRTL ? 0 : 16,
      right: isRTL ? 16 : 0,
    ),
  );
}

More efficient:

Widget build(BuildContext context) {
  return Padding(
    padding: EdgeInsetsDirectional.only(start: 16),
  );
}

Cache Directional Values

class _MyWidgetState extends State<MyWidget> {
  late TextDirection _direction;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _direction = Directionality.of(context);
  }
}

RTL Localization Checklist

Before launching your app with RTL support:

  • All EdgeInsets converted to EdgeInsetsDirectional
  • All Alignment values use directional variants
  • Icons that indicate direction are mirrored correctly
  • Text alignment uses start/end instead of left/right
  • Arabic plural forms (all 6) are properly handled
  • Fonts support Arabic/Hebrew character shaping
  • Bidirectional text displays correctly
  • Swipe gestures work intuitively in RTL
  • Navigation drawer appears on correct side
  • Numbers and prices display properly with mixed directions

Real-World Success Story

"Adding Arabic support to our e-commerce app increased downloads in MENA region by 340% within two months. The key was proper RTL implementation — users immediately noticed the attention to detail."

— Ahmed Hassan, Mobile Lead at RetailTech

Automating RTL Translation Management

Managing translations for RTL languages adds complexity:

  • Multiple plural forms to track
  • Character encoding issues
  • Bidirectional text validation

FlutterLocalisation handles these challenges with:

  • Visual editors that display RTL text correctly
  • Automatic plural form validation for Arabic
  • AI translation with RTL language support
  • Real-time preview of translations in context

Related Resources:

Ready to expand into Arabic, Hebrew, and other RTL markets? Start with proper localization infrastructure — your users will notice the difference.