Flutter e direzionalità (RTL)

Carlo Lucera (HatDroid)
Flutter Italia Developers
5 min readAug 12, 2021

Come evitare gli errori comuni tenendo bene a mente il supporto RTL

English version here

Cosa è RTL?

Alcune lingue, come l’Arabo, sono pensate per essere lette da destra verso sinistra invece che da sinistra verso destra.
Questo non è solo un cambio a livello testuale infatti tutta l’interfaccia, comprese azioni e icone, deve adeguarsi.
Flutter ci aiuta nel supporto di tutte le lingue RTL (Right to left) in modo da realizzare le nostre applicazioni senza troppi sforzi! Per una panoramica più accurata sui concetti principali del RTL, vi invito a consultare la pagina sul sito Material Design.

Come implementarlo in Flutter

Durante la scrittura del codice possiamo utilizzare Directionality.of(context) per sapere l’attuale direzione del testo e con i valori che ci verranno restituiti da questa chiamata (TextDirection.ltr o TextDirection.rtl), sarà nostra accortezza far in modo che la nostra applicazione venga disegnata in modo opportuno.
Per nostra fortuna, molti Widget sono già pensati per questo.

Rows e Columns

Rows e Columns sono già predisposti alla direzione RTL di default. Usando start e end come allineamento il contenuto verrà mostrato nel modo appropriato.

class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("A"),
Text("B"),
],);
}
}
Row con allineamento start
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("A"),
Text("B"),
],);
}
}
Column con allineamento start

Alignment Geometry

Tutti i widget che gestiscono un AlignmentGeometry(come Container, Align e qualche altro Widget) possono essere facilmente convertiti utilizzando AlignmentDirectional al posto del più comune Alignment

Senza supporto RTL

class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: Container(
width: 20,
height: 20,
color: Colors.red,
));
}
}

Con supporto RTL

class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Align(
alignment: AlignmentDirectional.centerStart,
child: Container(
width: 20,
height: 20,
color: Colors.red,
));
}
}

Margini e Paddings

Tutti i margini e i padding possono supportare l’RTL facilmente utilizzando EdgeInsetsDirectional al posto di EdgeInsets.
Per esempio, invece di usare EdgeInsets.only(left: x) possiamo sostituirlo con EdgeInsetsDirectional.only(start: x) per essere sicuri che venga rispettato nelle lingue RTL.

L’utilizzo di EdgeInsets.all o EdgeInsets.symmetric è già sicuro, in quanto i valori di left e right saranno sempre coincidenti

class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsetsDirectional.only(start: 30),
child: Container(
color: Colors.red,
));
}
}

Icone

La gestione delle icone può risultare un po’ più complessa, perché alcune icone sono già RTL safe, mentre altre no. Un modo sicuro per scoprirlo è controllare la documentazione ufficiale della classe Icons: Icons class — material library — Dart API e vedere se l’icona che vogliamo utilizzare ha il parametro matchTextDirection valorizzato a true; per esempio guardando Icons.arrow_back noteremo le seguenti informazioni:

matchTextDirection: true vuol dire che la nostra icona verrà ribaltata nelle lingue che utilizzano l’RTL e mentre questo si verifica per arrow_back, per altre icone come wheelchair_pickup no.

Questo non vuol necessariamente dire che non possiamo avere le icone specchiate quando viene utilizzata una lingua RTL, ma semplicemente che avremmo bisogno di un pizzico di impegno in più.
Invece di usare la direttamente la costante dentro Icons possiamo usare i suoi parametri per istanziare un nuovo IconData con i valori presi dalla stessa costante e aggiungendo il parametro matchTextDirection: true, come in questo esempio:

class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Icon(IconData(
Icons.wheelchair_pickup.codePoint,
fontFamily: Icons.wheelchair_pickup.fontFamily,
matchTextDirection: true,
)),
);
}
}
Wheelchair specchiata

Attenzione però, il fatto che sia possibile aggiungere il parametro a tutte le icone non vuol sempre dire che sia il caso di farlo! Non tutte le icone DEVONO essere specchiate nell’RTL, infatti una regola di base è quella che dice che le icone che comunicano una direzione come frecce o simili, vanno specchiate, mentre le altre non sempre. Per altre informazioni su quando ribaltare le icone e quando no, suggerisco sempre di controllare il sito Material Design

Altri widgets

Alcuni widget che sembrano non supportare la direzionalità, hanno spesso una controparte che gestisce il supporto RTL in modo semplice. Solitamente questi widget hanno lo stesso nome seguito da Directional; un esempio di questa convenzione è dato dal widget Positioned utilizzato dentro Stack, questo infatti non supporta la Direzionalità ma se abbiamo bisogno del supporto RTL per la nostra applicazione, possiamo semplicemente sostituirlo con il Widget PositionedDirectional PositionedDirectional class - widgets library - Dart API .

Conclusioni

Lavorare sull’internazionalizzazione della propria applicazione non vuol dire semplicemente cambiare le stringhe necessarie alla traduzione, ma richiede un approccio più completo e ragionato, Flutter si dimostra anche in questo caso abbastanza flessibile da riuscire a coprire la maggior parte dei casi d’uso con i quali possiamo trovarci faccia a faccia.
Bisogna porre particolare attenzione ai casi limite, durante i quali non specchiare l’interfaccia è fondamentale, un esempio potrebbe essere quello delle direzioni stradali, dove l’indicazione “a destra” deve necessariamente rimanere una freccia che indica nella direzione corretta!

Se volete conoscere altro su di me, seguitemi su Twitter

--

--