..

Flutter 2.5 スケルトンをながめる(2)

前回 の続き。 Flutter2.5からスケルトンが提供されるようになったので、それをながめてみる。

4. MyApp(2)

ひきつづき、MyAppクラスのbuild関数の内容をながめる。

4.1. restorationScopeId

restorationScopeId: 'app', 

最初にrestorationScopeIdを指定している。アプリケーションが長時間バックグランドで実行されていたために killされたあと、再び利用を開始するときに元の状態に復元できるようにするために必要、ということのよう。 restorationScopeIdを指定している場合、navigatorはその内部状態(ルートの復元可能な状態だけでなく、 画面遷移の履歴をも含めて)を保持し復元する。

restorationScopeId property

ただし、「状態」を復元するためのコードは含まれていない。実際に、エミュレーターで、開発者モードの「アクティビティを保持しない」をONしてバックグランドからフォアグラウンドに切り替えると、テーマのモードはリセットされてしまう。

復旧のためには、RestorationMixinが必要なようだが、これはStatefulWidget用であり、このスケルトンのように StatelessWidgetで作成されている場合は使えない。

RestorationMixin <S extends StatefulWidget> mixin

このスケルトンは、SettingsServiceで設定値を保存して起動時に読み込むことを想定している。設定値が保存されているのであれば(このスケルトンでは保存されない)、状態復元のための特別な処理は不要ということか。

参考 Introduction to State Restoration in Flutter

4.2. 多言語対応

localizationsDelegatesについては、とりあえず、オマジナイということしておく。

localizationsDelegates: [
  AppLocalizations.delegate,
  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
  GlobalCupertinoLocalizations.delegate,
],

対応する言語は、以下のようにsupportedLocalesに設定する。が、英語しかないので、日本語も追加してみる。

supportedLocales: [
  const Locale('en', ''), // English, no country code
  const Locale('ja', ''), // この行を追加した
],

あとは、app_en.arbとおなじところに、app_ja.arbを作成する。

├── main.dart
└── src
    ├── app.dart
    ├── localization
    │   ├── app_en.arb
    │   └── app_ja.arb

このディレクトリは、ルート直下のl10n.yamlに定義されている。l10n.yamlでは、app_en.arbがテンプレートであることも 定義されている。

ファイル名のサフィックスをアンダースコア+言語名とすることで、言語と地域を指定できる。(そのため、それ以外のところにアンダースコアは使えない)。
たとえば、app_ja.arbならja用、app_en_US.arbならen_US用の定義ファイルということになる。

中身は、これだけ。

{
  "appTitle": "私のアプリ"
}

app_en.arbにある@appTitleは作成しない。これは翻訳者向けの情報なので、テンプレートにのみあればよい。 それから、上述のようにファイル名で言語を区別しているので、@@localeはなくてよい。

Internationalization User Guide (PUBLICLY SHARED)によると、「たいていは、ファイル名のサフィックスと @@localeとの両方とで冗長に定義している」とあるのだが、このスケルトンでは省略されている。 ちなみに、ファイル名のサフィックスと@@localeとが矛盾したものであれば、エラーになる。

多言語された情報を扱っているのは、MyAppクラスのbuild関数内の以下のところ

onGenerateTitle: (BuildContext context) =>
  AppLocalizations.of(context)!.appTitle,

なのだが、これがどこで確認できるのか、わからない。モバイルアプリの知識不足が露呈している…。 とりあえず、動作を確認したいので、以下のファイルを書き換えてみる。

└── src
    ├── sample_feature
    │   └── sample_item_list_view.dart
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      // title: const Text('Sample Items'),
      title: Text(AppLocalizations.of(context)!.appTitle),
      actions:

これで、タイトル部分が変更されることを確認できた。

参考ページ 【Flutter】多言語対応のための .arb ファイルの書き方

4.3. 画面遷移(ルート)

MaterialApp の onGenerateRoute を使って画面遷移を定義している。画面遷移が発生する箇所で Anonymous routes を生成して渡すのではなく、Named routes を使うのは、WEBでの利用をサポートするため (URLに相当する情報が必要なため)。

下のように、名前を直接定義することもできるが、これだと、画面遷移が発生する箇所で、 まったく同じ文字列を指定する必要がある。

// これはスケルトンのコードではない

MaterialApp(
  routes: {
    '/': (context) => HomeScreen(),
    '/details': (context) => DetailScreen(),
  },
);

// 遷移時のサンプル
Navigator.pushNamed(context, '/details');

このスケルトンの場合は、遷移先となる画面で、以下のように文字列を定義している。

class SampleItemListView extends StatelessWidget {
  // コンストラクタは省略
  static const routeName = '/';

画面遷移は以下のように行う。

Navigator.restorablePushNamed(context, SampleItemListView.routeName);

で、onGenerateRouteでは、Navigator.restorablePushNamedで渡された文字列(routeSettingsのnameに設定されている)を使って、switch文で遷移先の画面を生成している。
switch文のdefault:のところは、初期画面を表示しているが、WEBだと404 not foundとなるべきところだろう。

onGenerateRoute: (RouteSettings routeSettings) {
  return MaterialPageRoute<void>(
    settings: routeSettings,
    builder: (BuildContext context) {
      switch (routeSettings.name) {
        case SettingsView.routeName:
          return SettingsView(controller: settingsController);
        //略
        default:
          return const SampleItemListView();

とりあえず、ここまで。