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.