Skip to content
Blog

RTL (Right-to-Left) Layout Support in Flutter: Complete Guide

Master RTL layout support in Flutter for Arabic, Hebrew, Persian, and other RTL languages. Learn text direction, mirrored layouts, and bidirectional text.

Published on September 19, 2026

AI Assistant

RTL (Right-to-Left) Layout Support in Flutter: Complete Guide

Flutter provides built-in support for right-to-left (RTL) languages like Arabic, Hebrew, Persian, and Urdu. With proper configuration, your app automatically mirrors its layout when an RTL locale is selected, ensuring a native experience for billions of RTL users.

Why RTL Support Matters

Over 1.5 billion people speak RTL languages. Without proper RTL support:

  • Text alignment is wrong
  • Navigation direction is reversed
  • Icons and controls appear in wrong positions
  • Layout breaks for RTL users

Flutter’s RTL support is automatic when properly configured.

How RTL Works in Flutter

Flutter uses the Directionality widget to determine text direction:

Directionality(
  textDirection: TextDirection.rtl, // or TextDirection.ltr
  child: YourWidget(),
)

When you use MaterialApp with proper localization, Flutter automatically sets the correct direction based on the selected locale.

Automatic RTL Configuration

Basic Setup

import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate, // Handles RTL
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en', ''),  // English (LTR)
        Locale('ar', ''),  // Arabic (RTL)
        Locale('he', ''),  // Hebrew (RTL)
        Locale('fa', ''),  // Persian (RTL)
      ],
      home: const HomeScreen(),
    );
  }
}

Layout Mirroring

Automatic Mirroring with Directionality

Most Flutter widgets automatically mirror in RTL mode:

// This row automatically reverses in RTL mode
Row(
  children: [
    Icon(Icons.arrow_back),      // Becomes arrow_forward in RTL
    Text('Back'),
    const Spacer(),
    Icon(Icons.arrow_forward),   // Becomes arrow_back in RTL
  ],
)

// This list tile automatically mirrors
ListTile(
  leading: Icon(Icons.chevron_right), // Becomes chevron_left in RTL
  title: Text('Settings'),
)

Manual Directionality Control

For custom layouts that need RTL support:

class CustomCard extends StatelessWidget {
  const CustomCard({super.key});

  @override
  Widget build(BuildContext context) {
    final textDirection = Directionality.of(context);

    return Container(
      padding: const EdgeInsets.all(16),
      child: Row(
        children: [
          // Leading element (reverses in RTL)
          const Icon(Icons.star, color: Colors.amber),
          const SizedBox(width: 8),

          // Content
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text('Rating'),
                const Text('4.5 out of 5'),
              ],
            ),
          ),

          // Trailing element (reverses in RTL)
          Icon(
            textDirection == TextDirection.rtl
                ? Icons.chevron_left
                : Icons.chevron_right,
          ),
        ],
      ),
    );
  }
}

RTL-Aware Widgets

Padding and Margin

// RTL-aware padding
Padding(
  padding: EdgeInsetsDirectional.only(
    start: 16, // Left in LTR, Right in RTL
    end: 8,    // Right in LTR, Left in RTL
  ),
  child: Text('RTL-aware padding'),
)

// RTL-aware margin
Container(
  margin: const EdgeInsetsDirectional.only(
    top: 8,
    bottom: 8,
    start: 16,
    end: 8,
  ),
  child: Text('RTL-aware margin'),
)

Positioning

PositionedDirectional(
  start: 16, // Left in LTR, Right in RTL
  top: 16,
  child: Container(
    width: 100,
    height: 100,
    color: Colors.blue,
  ),
)

Alignment

Align(
  alignment: AlignmentDirectional.centerStart,
  child: Text('Aligned to start'),
)

// Or use the convenience constants
Align(
  alignment: AlignmentDirectional.centerStart, // Start edge
  child: Text('Start-aligned'),
)

Align(
  alignment: AlignmentDirectional.centerEnd, // End edge
  child: Text('End-aligned'),
)

Bidirectional Text

Mixing LTR and RTL Text

Flutter handles bidirectional text automatically:

Text(
  'This is English text mixed with نص عربي and more English',
  textDirection: TextDirection.ltr,
)
// Flutter correctly renders the mixed text

Explicit Text Direction

// Force text direction for specific text
Text(
  'مرحبا بالعالم',
  textDirection: TextDirection.rtl,
)

// Or use BidiFormatter for automatic detection
import 'package:intl/bidi.dart';

Text(
  Bidi.stripHtmlIfNeeded('مرحبا Hello World'),
)

Testing RTL Support

Visual Testing

testWidgets('renders correctly in RTL', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      locale: const Locale('ar', ''),
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      home: MyWidget(),
    ),
  );

  await expectLater(
    find.byType(MyWidget),
    matchesGoldenFile('golden/rtl_layout.png'),
  );
});

Directionality Testing

testWidgets('correct text direction', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      locale: const Locale('ar', ''),
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      home: MyWidget(),
    ),
  );

  final direction = Directionality.of(
    tester.element(find.byType(MyWidget)),
  );

  expect(direction, TextDirection.rtl);
});

Common Patterns

class NavigationIcon extends StatelessWidget {
  const NavigationIcon({super.key});

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

    return Icon(
      isRTL ? Icons.arrow_back : Icons.arrow_forward,
    );
  }
}

Drawer and Navigation

// Drawer automatically appears on correct side
Scaffold(
  drawer: const Drawer(
    child: MenuItems(),
  ),
  // In RTL: drawer opens from right
  // In LTR: drawer opens from left
)

Custom Scroll Views

// Scrollbar position adjusts automatically
ListView(
  children: items.map((item) => ListTile(
    title: Text(item.name),
    trailing: const Icon(Icons.chevron_right),
  )).toList(),
)

Best Practices

  1. Always use EdgeInsetsDirectional instead of EdgeInsets for start/end spacing
  2. Use PositionedDirectional for positioned elements
  3. Let Flutter mirror icons automatically — don’t manually flip them
  4. Test with both LTR and RTL locales
  5. Use TextDirection only when you need explicit control
  6. Handle bidirectional text — let Flutter’s text engine handle it

Resources

RTL support is essential for building truly global Flutter apps. With Flutter’s built-in directionality support, you can create experiences that feel native for RTL users without significant code changes.