skip to content

In Flutter, what do EdgeInsetsDirectional and AlignmentDirectional do, and why use them instead of EdgeInsets.only(left:) in an app shipping Arabic?

level: juniorimportance: should knowfreq 41%

answer

  1. start and end, not left and right
  2. resolved against the ambient TextDirection
  3. EdgeInsetsGeometry and AlignmentGeometry
  4. TextAlign.start is Text's default
  5. needs a Directionality ancestor

basics

~20 s

EdgeInsetsDirectional 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 s

Absolute 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 lines
dart
import '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

for a junior

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.

for a middle

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.

for a senior

Spot left and right in review, write widget tests that pump both directions, and know which absolute values are deliberate.

for a principal

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.