Cet article complète l’article précédent internationalisation.

Difficulté: Débutant

Introduction

Dans mon article précédent, j’ai expliqué une solution pour rendre une application Flutter multilingue et permettre de changer dynamiquement la langue de travail.

Cet article complète l’article précédent de telle sorte qu’il fournit un Widget pour:

  • lister les langues supportées par l’application
  • montrer la langue actuelle
  • permettre à l’utilisateur de sélectionner une autre langue
  • une fois qu’une autre langue est sélectionnée, forcez l’application à “refresh” et utilisez la nouvelle langue

Cet article suppose que vous ayez lu mon article précédent et que vous soyez familier avec les sujets que j’ai couverts.

Le Widget LanguageSelector

Ce Widget est basé sur un ExpansionTile qui:

  • s’ouvre (expand) lorsque l’utilisateur le touche
  • affiche la liste des langues supportées par l’application et met en évidence la langue de travail en cours
  • se referme automatique lorsque l’utilisateur sélectionne une langue
  • force un rafraîchissement (en termes de traductions) de l’application

La principale difficulté de ce widget LanguageSelector réside dans le fait qu’il devrait automatiquement se refermer lorsque l’utilisateur appuie sur l’un de ses ListTile.

Sur Internet, j’ai trouvé une solution sur Stack Overflow. La solution de Simon me semblait un peu trop complexe (du moins par rapport à mes besoins), j’ai alors opté pour une autre solution donnée par Alex Radzishevsky.

Ceci est l’implémentation que j’en ai découlé (language-selector.dart).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import 'dart:math';
import 'package:flutter/material.dart';
import 'application.dart';
import 'translations.dart';

class LanguageSelector extends StatefulWidget {
  @override
  _LanguageSelectorState createState() => new _LanguageSelectorState();
}

class _LanguageSelectorState extends State<LanguageSelector> {
  int _key;

  @override
  void initState() {
    super.initState();
    _collapse();
  }

  @override
  Widget build(BuildContext context) {
    return _buildTiles(context);
  }

  List<Widget> _languageItems() {
    List<Widget> list = [];
    final _transl = Translations.of(context);

    applic.supportedLanguages.forEach((lang) {
      list.add(new ListTile(
        title: new Text(_transl.text('language_$lang')),
        trailing: _transl.currentLanguage == lang
            ? new Icon(Icons.check, color: Colors.green)
            : null,
        selected: _transl.currentLanguage == lang,
        onTap: () {
            applic.onLocaleChanged(new Locale(lang, ''));
            setState(() {
              _collapse();
            });
        },
      ));
    });

    return list;
  }

  Widget _buildTiles(BuildContext context) {
    return new ExpansionTile(
      key: new Key(_key.toString()),
      initiallyExpanded: false,
      title: new Row(
        children: [new Text(Translations.of(context).text('language'))],
      ),
      children: _languageItems(),
    );
  }

  _collapse() {
    int newKey;
    do {
      _key = new Random().nextInt(10000);
    } while (newKey == _key);
  }
}

Cette solution qui permet à ExpansionTile de se fermer automatiquement à chaque fois que l’utilisateur appuie sur l’un de ses enfants est simplement de le recréer.

  • Ceci est assuré par la méthode _collapse() qui génère une clé aléatoire chaque fois qu’elle est appelée. Cette _key est utilisée pour identifier de manière unique le ExpansionTile au niveau de l’arborescence des Widgets (ligne # 50).
  • Lors de l’initialisation, la méthode surchargée initState appelle la méthode _collapse(), avant de rendu initial.
  • La méthode _languageItems() crée une liste de ListTile, basée sur la liste des langues supportées. Le ListTile qui correspond à la langue en cours est mis en évidence, via une icône ‘check’ à la fin de la ligne.

Sélection de la langue

Lignes #36-39.

Lorsque l’utilisateur appuie sur l’un des éléments ListTile, un appel est effectué vers la routine principale de l’application pour forcer une actualisation complète de l’application. Par la suite, nous forçons la fermeture du ExpansionTile via un appel à la méthode _collapse().

Intégration au niveau de l’application

Afin de montrer un exemple d’utilisation de ce Widget, nous ajouterons un Drawer à la page principale. Ce drawer ne contiendra qu’une instance du Widget LanguageSelector.

Le code résultant est le suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'translations.dart';
import 'application.dart';
import 'language-selector.dart';

void main() => runApp(new MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  SpecificLocalizationDelegate _localeOverrideDelegate;

  @override
  void initState(){
    super.initState();
    _localeOverrideDelegate = new SpecificLocalizationDelegate(null);
    ///
    /// Let's save a pointer to this method, should the user wants to change its language
    /// We would then call: applic.onLocaleChanged(new Locale('en',''));
    /// 
    applic.onLocaleChanged = onLocaleChange;
  }

  onLocaleChange(Locale locale){
    setState((){
      _localeOverrideDelegate = new SpecificLocalizationDelegate(locale);
    });
  }

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: new Text('My Application'),
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      localizationsDelegates: [
        _localeOverrideDelegate,
        const TranslationsDelegate(),
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: appli.supportedLocales(),
      home: new Scaffold(
          appBar: new AppBar(
            title: new Text(Translations.of(context).text('main_title')),
          ),
          drawer: new Drawer(
              child: new LanguageSelector(),
              elevation: 2.0,
          ),
          body: new Container(
          ),
      ),        
    );
  }
}

Voilà, c’est tout.

Conclusions

C’est un widget très basique qui montre comment implémenter un changement dynamique de la langue de travail.

La partie la plus intéressante de ce widget donne une solution pour permettre à un ExpansionTile de se fermer automatiquement quand l’un de ses enfants est activé.

En espérant que cela pourrait être utile.

Restez à l’écoute de nouveaux articles et bon codage.