In Flutter, where does a widget's ambient TextDirection come from, and how do you force a left-to-right island inside an Arabic screen?
answer
- an inherited widget above you
- Localizations builds it
- WidgetsLocalizations.textDirection
- the built-in fallback is always ltr
- wrap the island in its own Directionality
basics
~10 sThe ambient direction comes from the nearest Directionality widget, which the app's Localizations widget inserts using WidgetsLocalizations.textDirection. For a left-to-right island, wrap that subtree in Directionality(textDirection: TextDirection.ltr).
solid answer
~30 s`Directionality` is an inherited widget; `Directionality.of(context)` returns its `TextDirection` and asserts when none exists, while `maybeOf` returns `null`. In an app, `MaterialApp` builds a `Localizations` widget that wraps its child in a `Directionality` whose value is `WidgetsLocalizations.textDirection` for the resolved locale. `GlobalWidgetsLocalizations` from `flutter_localizations` returns `rtl` for `ar`, `fa`, `he`, `ps`, `sd` and `ur`; the framework's built-in `DefaultWidgetsLocalizations` always returns `ltr`, so an app that forgets `GlobalWidgetsLocalizations.delegate` stays left-to-right in Arabic. To force a left-to-right island, such as an IBAN or an exchange-rate formula, wrap it in `Directionality(textDirection: TextDirection.ltr, child: ...)`, or pass `textDirection` to the `Text`, `TextField` or `Row` itself.
code
dart · 22 linesimport 'package:flutter/material.dart';
import 'package:intl/intl.dart' as intl;
class RateLine extends StatelessWidget {
const RateLine({super.key, required this.rate});
final double rate;
@override
Widget build(BuildContext context) {
final locale = Localizations.localeOf(context).toString();
final formatted = intl.NumberFormat.decimalPatternDigits(
locale: locale,
decimalDigits: 4,
).format(rate);
// The formula reads left to right in every UI language.
return Directionality(
textDirection: TextDirection.ltr,
child: Text('1 USD = $formatted ILS'),
);
}
}go deeper
Know that Directionality sets the direction for its subtree and that MaterialApp provides one, so you rarely add it yourself.
Explain how Localizations builds Directionality from WidgetsLocalizations.textDirection, why the global widgets delegate is needed for Arabic and Hebrew, and how of differs from maybeOf.
Diagnose an Arabic app stuck in ltr from its delegate list, keep left-to-right islands minimal, and resolve the intl TextDirection clash with an import prefix.
Decide which content classes are always left-to-right in the product and encode them in shared widgets so screens do not improvise overrides.
## `Directionality` is an inherited widget `Directionality` holds one value, a `TextDirection` (`ltr` or `rtl`), and exposes it to its subtree: - `Directionality.of(context)` returns the nearest value and registers a dependency, so the widget rebuilds if the direction changes. With no ancestor it fails with *"No Directionality widget found."* - `Directionality.maybeOf(context)` returns `null` instead, for widgets that can work without one. Text layout, `Row` and `Column` ordering, directional padding and alignment, and icons marked to mirror all read this value. ## Who inserts it in an app You rarely write a `Directionality` at the top of an app because the `Localizations` widget created by `WidgetsApp` (and therefore by `MaterialApp` and `CupertinoApp`) does it: 1. The app resolves a locale from the device's preferred locales and `supportedLocales`. 2. It loads one delegate per resource type, including a `WidgetsLocalizations`. 3. `Localizations` wraps the app in `Directionality(textDirection: widgetsLocalizations.textDirection)`, and also labels the semantics tree with that direction. Which `WidgetsLocalizations` is loaded decides the direction: | Delegate that supplies `WidgetsLocalizations` | Direction for Arabic or Hebrew | |---|---| | `GlobalWidgetsLocalizations.delegate` (from `flutter_localizations`) | `rtl` for `ar`, `fa`, `he`, `ps`, `sd`, `ur` | | `DefaultWidgetsLocalizations.delegate` (built in, appended last) | always `ltr` | The built-in delegate claims to support every locale, so it silently fills the gap when the global one is missing. A currency converter that adds `Locale('ar')` and `Locale('he')` to `supportedLocales` but forgets the global delegates shows Arabic strings in a left-to-right layout. Passing `AppLocalizations.localizationsDelegates` or `GlobalMaterialLocalizations.delegates` avoids that, because both lists include `GlobalWidgetsLocalizations.delegate`. ## Forcing a left-to-right island Some content is left-to-right whatever the UI language: an IBAN, a card number, a formula such as `1 USD = 3.71 ILS`, code snippets. Options, from broad to narrow: - **Wrap a subtree**: `Directionality(textDirection: TextDirection.ltr, child: ...)` changes direction for everything below it, including padding, alignment and `Row` order. - **Set it on one widget**: `Text`, `RichText`, `TextField`, `Row`, `Column` and `Flex` take a `textDirection` parameter that overrides the ambient value for that widget only. - **Mark a run inside a string**: `package:intl`'s `BidiFormatter` and the `Bidi.LRM` and `Bidi.RLM` marks keep a short left-to-right fragment from being reordered inside right-to-left text. Keep the island as small as possible: a whole-row override also mirrors that row's padding and icons, which is rarely what the design wants. ## Nested locales and mixed-direction text - **`Localizations.override`** creates a nested `Localizations` for one subtree, for example a Hebrew receipt preview inside an English settings screen. It reuses the parent's delegates and builds its own `Directionality` from the overriding locale's `WidgetsLocalizations`, so the preview lays out right-to-left while the rest of the screen stays left-to-right. - **Mixed text inside one string** is handled by the Unicode bidirectional algorithm during text shaping: Latin currency codes and digits inside a Hebrew sentence are laid out as left-to-right runs automatically. The widget's `TextDirection` only sets the **base** direction of the paragraph, which decides where the line starts and how neutral characters such as punctuation at the ends behave. - **Scrolling follows direction too**: horizontal `ListView` and `PageView` derive their axis direction from `Directionality`, so a horizontal list of currencies starts at the right edge in Arabic unless you pass `reverse: true` or an explicit direction. ## The `TextDirection` name clash `package:intl/intl.dart` exports its own class called `TextDirection` (used by its bidi utilities). A file that imports both `package:flutter/material.dart` and `package:intl/intl.dart` without a prefix sees two `TextDirection` types, and `TextDirection.rtl` becomes ambiguous. The usual fix is `import 'package:intl/intl.dart' as intl;`, which is also what gen-l10n's generated code does, or `hide TextDirection` on the intl import. ## Testing both directions - Pump the widget under `Directionality(textDirection: TextDirection.rtl, child: ...)` in a widget test to check the mirrored layout without a full app. - Pump `MaterialApp(locale: const Locale('ar'), ...)` with the real delegates to prove the delegate wiring as well. - Read `Directionality.of(context)` in a debug overlay while testing on a device set to Hebrew.
- Your Arabic build shows Arabic text but a left-to-right layout; what do you check first?Whether `GlobalWidgetsLocalizations.delegate` is in `localizationsDelegates`. Without it the built-in `DefaultWidgetsLocalizations` supplies `WidgetsLocalizations` for every locale and always reports `ltr`, so the app's `Directionality` never turns `rtl`.
- What is the difference between Directionality.of and Directionality.maybeOf?Both read the nearest `Directionality` and register a dependency. `of` asserts with 'No Directionality widget found' when there is none; `maybeOf` returns `null`, which suits widgets that can fall back to a sensible default.
- Why prefer a per-widget textDirection over wrapping a whole row in Directionality?Wrapping changes direction for everything below, so the row's directional padding, alignment and mirrored icons flip back to left-to-right too. Setting `textDirection` on the single `Text` or `TextField` limits the change to the content that needs it.
Directionality is like a country's driving side: every lane and roundabout inside the border follows it, and a left-to-right island is a fenced private track with its own rule that stops at the fence.
saying these in an interview costs you the question
- Flutter reads the text direction from the device on every Text widget.
- Adding Locale('ar') to supportedLocales is enough to get a right-to-left layout.
- Directionality.of returns ltr when no Directionality ancestor exists.
- The intl package's TextDirection is the same type as Flutter's.
- Wrap the whole screen in an ltr Directionality to show one IBAN.