Rating: 4.83 / 5 Based on 30 reviews
When the app supports right to left (RTL) languages, it gets available for way more users worldwide. Plus, layout and text in apps, especially in Flutter, can be changed into RTL easier than you think.
In this article, you will find the most important things to keep in mind when introducing RTL design in your application and how you can use Flutter to support various text directions with little effort. It is based on my experience developing a well-being mobile app in Flutter - Welliba. Here you can see Welliba Case Study.
Right to left (RTL) is simply used to indicate that in a certain locale text is read from right to left direction. Its opposite LTR, in which this article is written, stands for the left-to-right direction of writing, preferred by most of the world. However, Arabic, Hebrew, and Persian are just a few RTL language examples using such a text flow.
There are 400 million Arabic speakers, and the total of all people who read from right to left is estimated to be around 1 billion. There is a huge market opportunity to take advantage of by simply supporting these languages and implementing all the side effects of RTL layout.
Also, it may be interesting to know that in some countries, it may be a legal requirement to provide an RTL version of your app. For example, in Israel, all public websites and applications are required by law to provide an RTL version.
There are several key differences between right-to-left (RTL) and left-to-right (LTR) design in user interfaces. Here are some of the main differences:
Overall, RTL design requires careful consideration and attention to detail to ensure that the UI is effective and accessible for users who read and write in RTL languages. By understanding the differences between RTL and LTR design, designers and developers can create user interfaces that are optimized for all users.
To notice how different the same app looks in RTL design, you can go to any popular service which allows you to change the language to, for instance, Arabic and see for yourself. On Booking.comBooking.com, the whole screen is almost perfectly reflected, including the picture at the bottom. Icons, on the other hand, are kept the same even though they use RTL layout.
Graphics source: https://www.booking.com/
Apple's home page has more subtle differences; the images are not mirrored there, probably because they show the actual devices which are not sold in mirrored versions. As you can see, sometimes it’s not so simple to decide what to change to the RTL arrangement of elements and how usually you will be flipping the design horizontally.
Graphics source: https://www.apple.com/
You can learn about other rules to follow in right-to-left design, in Material’s guide on bidirectional design.
In Flutter development, the localization widget is used to handle the localization of the app's user interface (UI) elements. The localization widget is called "Localizations" and is part of the Flutter internationalization (i18n) and localization (l10n) framework.
On the other hand, directionality widgets are used to specify the directionality of text and UI elements in a widget tree. Directionality widgets are important for ensuring that your app is displayed correctly in languages that use right-to-left (RTL) writing systems, such as Arabic, Hebrew, and Persian.
Let’s now find out how an application realizes what locale it should be using and what text direction it has. We won’t get into details of localizing your app from scratch, since there already are multiple great articles covering this topic like How to internationalize your Flutter app with ARB files today? — full-blown tutorial.
I recommend using MaterialApp since it is a pre-built widget that implements the Material Design guidelines for creating Android-style mobile applications and provides built-in support for RTL languages. When you create a new Flutter app using MaterialApp, you can easily add support for RTL languages by setting the textDirection property to TextDirection.rtl. This tells Flutter to use the RTL layout direction for text and other elements in your app.
For the purpose of your app displaying in RTL, you should pass supportedLocales and localizationsDelegates as MaterialApp parameters, and specify an RTL locale:
MaterialApp(
locale: const Locale('ar'),
supportedLocales: const [
Locale('ar'),
Locale('en'),
],
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
Once this is covered, let me explain where the magic of setting text direction actually happens. MaterialApp wraps your app in Localizations widget, which itself adds Directionality widget with proper textDirection based on the current locale and GlobalWidgetsLocalizations, which specifies supported RTL languages:
static const List<String> _rtlLanguages = <String>[
'ar', // Arabic
'fa', // Farsi
'he', // Hebrew
'ps', // Pashto
'ur', // Urdu
];
To sum up the most important widget in the context of directionality is called Directionality. It requires only two arguments: child and textDirection enum, which has two members, rtl and ltr. Directionality can be used to overwrite text direction manually, it’s the simplest solution if you don’t want some part of your app to change in RTL. To obtain text direction specified by the first Directionality widget placed above in the widget tree, you can use: Directionality.of(context)
There is a whole set of widgets in Flutter that are dedicated to building layouts adaptive to directionality, meaning we won’t have to mirror all of the screens by hand. In short, instead of using left and right, they are using start and end, where start describes the side from which you start reading, for LTR it’s left, RTL - right. Rows and Columns use start and end values for mainAxisAlignment and crossAxisAlignment, so you might be already familiar with the concept.
Directionality(
textDirection: TextDirection.ltr,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
RedBox(),
BlueBox(),
],
),
),
Below you can see 6 configurations for different textDirection and mainAxisAlignment values, and their outcomes on the screen. Notice how, based on text direction, not only the alignment changes but also the order of the children, and that’s how it’s supposed to be to get the same experience when reading in reverse.
For the most part, by simply using Rows or Columns, your Flutter app should look quite alright after first switching the locale to right to left one. However, each place where right or left is used, like in Positioned widget, will be displayed wrong. That’s where the directional widgets mentioned earlier come in. Luckily it’s quite easy to refactor your code to use them, and when applied correctly, they won’t impact your left to right layout at all.
Positioned(
left: 50,
child: RedBox(),
),
PositionedDirectional(
start: 50,
child: RedBox(),
),
Same works for AnimatedPositioned and AnimatedPositionedDirectional.
Align(
alignment: Alignment.centerRight,
child: RedBox(),
),
Align(
alignment: AlignmentDirectional.centerEnd,
child: RedBox(),
),
BoxDecoration(
border: Border(left: BorderSide(width: 4)),
color: Colors.red,
),
BoxDecoration(
border: BorderDirectional(start: BorderSide(width: 4)),
color: Colors.red,
),
BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10)),
color: Colors.red,
),
BoxDecoration(
borderRadius: BorderRadiusDirectional.only(topStart: Radius.circular(10)),
color: Colors.red,
),
Padding(
padding: EdgeInsets.only(right: 10),
child: RedBox(),
),
Padding(
padding: EdgeInsetsDirectional.only(end: 10),
child: RedBox(),
),
If your Flutter app uses Material icons you are basically covered since they are automatically handled by Icon widget based on directionality. Not every icon will be mirrored; a list of all material icons that change depending on text direction can be found under the link: Which icons should be mirrored for RTL?
If you don’t like the built-in behavior, the Icon widget has a textDirection field with which you can enforce directionality. In the table below, you can see that icons implying direction, matching layout elements, or having different meanings based on which side they are facing are flipped by default from right to left.
Widgets like Image as well as SvgPicture from the flutter_svg package, are also working almost out-of-the-box. They have a matchTextDirection parameter that defaults to false, for every graphic that you want to display flipped in RTL. You’ll need to set this to true. Additionally, for every CustomPainter or RenderBox in your app, you will have to write some extra code and decide how certain elements should be painted based on the text direction.
Mixed directionality texts themselves can lead to your application displaying way off. That’s why it’s best to apply directionality to a Text widget based on the actual text displayed. There is a very light package called auto_direction with which you can wrap Text or TextField with an AutoDirection widget and pass it the current text value.
It will detect and override text direction making your texts consistent. If you’re not a fan of adding a package for every problem you come upon, detecting directionality can be achieved by using detectRtlDirectionality, one of many interesting functions for working with bidirectional texts enclosed in Bidi class from the intl library, with which I highly recommend getting familiar.
Below you can see the example from the Welliba mobile app:
Supporting RTL with Flutter is as simple and straightforward as it gets, even if it wasn’t included in the initial project specification, since the transition is almost a no-brainer. Flutter has built-in support for RTL languages, and the framework provides several tools and widgets that make it easy to implement RTL design.
Apps that implement the right to left layout for RTL languages are accessible to a much wider group of users and enhance the overall user experience. It’s a small effort with a significant impact, making RTL support a must-have feature in modern apps written not only in Flutter.
You might also be interested in reading our article about Feature-Based Flutter Architecture.