In Flutter, what do EdgeInsetsDirectional and AlignmentDirectional do, and why use them instead of EdgeInsets.only(left:) in an app shipping Arabic?
answer
- start and end, not left and right
- resolved against the ambient TextDirection
- EdgeInsetsGeometry and AlignmentGeometry
- TextAlign.start is Text's default
- needs a Directionality ancestor
basics
~20 sEdgeInsetsDirectional and AlignmentDirectional describe spacing and position with start and end, which Flutter resolves to left or right from the ambient TextDirection at layout. EdgeInsets.only(left:) stays on the left, so it breaks in right-to-left locales.
solid answer
~30 sAbsolute geometry (`EdgeInsets`, `Alignment`, `TextAlign.left`) names physical sides. Directional geometry (`EdgeInsetsDirectional.only(start: 16)`, `AlignmentDirectional.centerStart`, `TextAlign.start`, `BorderRadiusDirectional`) names logical sides. Widgets such as `Padding`, `Align` and `Container` accept the common base types `EdgeInsetsGeometry` and `AlignmentGeometry`, and their render objects call `resolve(textDirection)` with the ambient `Directionality`, so `start` becomes left in English and right in Arabic or Hebrew. With `EdgeInsets.only(left: 16)` the gap stays on the left in both, which leaves the currency flag hugging the wrong edge. Directional values need a `Directionality` ancestor; `MaterialApp` provides one, but a bare widget test may not, and resolving then fails an assertion.
code
dart · 29 linesimport 'package:flutter/material.dart';
class CurrencyRow extends StatelessWidget {
const CurrencyRow({super.key, required this.code, required this.amount});
final String code;
final String amount;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsetsDirectional.fromSTEB(16, 8, 8, 8),
alignment: AlignmentDirectional.centerStart,
decoration: const BoxDecoration(
borderRadius: BorderRadiusDirectional.only(
topStart: Radius.circular(12),
bottomStart: Radius.circular(12),
),
),
child: Row(
children: [
Text(code),
const SizedBox(width: 12),
Expanded(child: Text(amount, textAlign: TextAlign.end)),
],
),
);
}
}go deeper
Recall that start and end replace left and right: EdgeInsetsDirectional, AlignmentDirectional, BorderRadiusDirectional and TextAlign.start mirror in right-to-left locales, absolute types do not.
Explain that Padding and Align accept the Geometry base types and resolve them against the ambient TextDirection at layout, and that Row and Text already follow direction.
Spot left and right in review, write widget tests that pump both directions, and know which absolute values are deliberate.
Make directional geometry a codebase convention early, since retrofitting a large layout for right-to-left later touches almost every screen.
## Physical sides versus logical sides Flutter has two families of geometry types for padding, alignment and corners: - **Absolute** types name physical sides: `EdgeInsets` (`left`, `right`), `Alignment` (`centerLeft`, `topRight`), `BorderRadius` (`topLeft`), `TextAlign.left` and `TextAlign.right`. - **Directional** types name logical sides: `EdgeInsetsDirectional` (`start`, `end`), `AlignmentDirectional` (`centerStart`, `topEnd`), `BorderRadiusDirectional` (`topStart`), `TextAlign.start` and `TextAlign.end`. **Start** means the side where reading begins: left in English, right in Arabic and Hebrew. **End** is the opposite side. ## How the resolution works Widgets that take padding or alignment are typed against the shared base classes, `EdgeInsetsGeometry` and `AlignmentGeometry`, so they accept either family. At layout time the render object calls `resolve(textDirection)`: 1. `Padding` passes its `EdgeInsetsGeometry` to `RenderPadding`, which resolves it with the `TextDirection` taken from the nearest `Directionality`. 2. `EdgeInsetsDirectional.resolve(TextDirection.rtl)` returns an `EdgeInsets` with `start` on the right and `end` on the left; for `ltr` it maps the other way. 3. `AlignmentDirectional(start, y).resolve(TextDirection.rtl)` returns `Alignment(-start, y)`, so `centerStart` becomes `centerRight`. Absolute values resolve to themselves, which is exactly why they do not mirror. ## Which types to reach for | Need | Breaks in RTL | Mirrors correctly | |---|---|---| | Leading gap before a currency flag | `EdgeInsets.only(left: 16)` | `EdgeInsetsDirectional.only(start: 16)` | | Asymmetric padding | `EdgeInsets.fromLTRB(16, 8, 4, 8)` | `EdgeInsetsDirectional.fromSTEB(16, 8, 4, 8)` | | Pin a badge to the leading edge | `Alignment.centerLeft` | `AlignmentDirectional.centerStart` | | Round only the leading corners | `BorderRadius.only(topLeft: ...)` | `BorderRadiusDirectional.only(topStart: ...)` | | Align a paragraph | `TextAlign.left` | `TextAlign.start` | Symmetric values (`EdgeInsets.all`, `EdgeInsets.symmetric`, `Alignment.center`) look the same in both directions, so they can stay absolute. ## What already mirrors without extra work - **`Row` and other `Flex` widgets** lay children out from the start edge, so in Arabic the first child appears on the right. `MainAxisAlignment.start` and `CrossAxisAlignment.start` follow the same rule. - **`Text`** defaults to `TextAlign.start` when neither the widget nor the surrounding `DefaultTextStyle` sets an alignment. - **Material widgets** such as `ListTile` and `AppBar` place leading and trailing parts by direction. Code that reverses a `Row`'s children manually "for Arabic" therefore flips them twice and ends up wrong. ## The `Directionality` requirement Directional geometry is meaningless without a direction. `MaterialApp`, `CupertinoApp` and `WidgetsApp` insert a `Directionality` through their `Localizations` widget, so app code rarely notices. A widget test that pumps a `Padding` with `EdgeInsetsDirectional` and no app or `Directionality` above it fails with an assertion that the directional value cannot be resolved without a text direction. Wrapping the test subject in `Directionality(textDirection: TextDirection.rtl, child: ...)` both fixes the test and lets you check the mirrored layout. ## Writing your own widgets When you build a reusable widget, type its parameters with the **Geometry base classes**, not the absolute ones: - accept `EdgeInsetsGeometry padding` rather than `EdgeInsets padding`, so callers can pass either family; - accept `AlignmentGeometry alignment` rather than `Alignment alignment`; - if your own `RenderObject` needs concrete numbers, call `padding.resolve(textDirection)` during layout with the direction you were given, and mark layout dirty when that direction changes. The base classes also combine: adding an `EdgeInsets` to an `EdgeInsetsDirectional` with `add` produces a mixed value that is resolved as a whole once the direction is known. Asking for the concrete `EdgeInsets` type in a public API forces every caller back to `left` and `right`, so the type choice itself spreads or blocks right-to-left support. ## A habit worth building Treat `left` and `right` in layout code as a smell in any app that may ship a right-to-left language. Reserve absolute values for things that are physically anchored regardless of language, for example a media player's scrubber if your design says it never mirrors (that design rule is a product decision, not a Flutter one).
- Does a Row need any change to lay out right-to-left in Arabic?No. `Row` takes its `textDirection` from the ambient `Directionality` when none is passed, and in `rtl` the first child sits at the right edge. Reversing the children list by hand for Arabic flips the order twice.
- Why does a widget test with EdgeInsetsDirectional fail when it pumps the widget without MaterialApp?Resolving directional geometry needs a `TextDirection`, and only a `Directionality` ancestor supplies one. Without an app widget above it the assertion fires. Wrap the subject in `Directionality(textDirection: TextDirection.ltr, ...)` or `rtl` to test either layout.
- When is it correct to keep an absolute EdgeInsets or Alignment?When the value is symmetric, such as `EdgeInsets.all(8)` or `Alignment.center`, or when the design deliberately anchors something to a physical side in every language. Everything leading or trailing should be directional.
saying these in an interview costs you the question
- Flutter mirrors EdgeInsets.only(left:) automatically when the locale is Arabic.
- Reverse the Row's children list when the locale is right-to-left.
- Text defaults to TextAlign.left, so set TextAlign.right for Hebrew.
- EdgeInsetsDirectional works without any Directionality ancestor.
- Directional padding is only needed for Text widgets.