【Flutter】Appクラスでcontextを使う方法 〜 'No MaterialLocalizations found.'を解決する〜

2023/04/09に公開

今回はAppクラスでcontextを使う方法を紹介します。

先日強制アップデート機能を実装するに当たって困ったことがありました。AppクラスのinitState内で、contextを使おうとするとエラーが出てきました。

FlutterError (No MaterialLocalizations found.
MyApp widgets require MaterialLocalizations to be provided by a Localizations widget ancestor.
The material library uses Localizations to generate messages, labels, and abbreviations.
To introduce a MaterialLocalizations, either use a MaterialApp at the root of your application to include them automatically, or add a Localization widget with a MaterialLocalizations delegate.
The specific widget that could not find a MaterialLocalizations ancestor was:
  MyApp
The ancestors of this widget were:
  [root])
日本語訳です。ChatGPT先生ありがとうございます。
Flutterエラー(MaterialLocalizationsが見つかりません。
MyAppウィジェットには、Localizationsウィジェットの祖先から提供されたMaterialLocalizationsが必要です。
マテリアルライブラリは、メッセージ、ラベル、略語を生成するためにLocalizationsを使用します。
MaterialLocalizationsを導入するには、自動的にそれらを含めるためにアプリケーションのルートにMaterialAppを使用するか、MaterialLocalizationsデリゲートを持つLocalizationウィジェットを追加します。
MaterialLocalizations祖先が見つからなかった特定のウィジェットは:
MyApp
このウィジェットの祖先は次のとおりでした:
[root])

その解決方法をまとめました。

サンプル

起動時にDialogを出します。Appクラス以降の通常は最初の画面で使えばいいのですが、全画面にわたってlistenさせたい、StatefullConsumerを使いたいなどの理由があり、実装していました。その辺の詳細は割愛します。

サンプルコード
mport 'package:flutter/material.dart';

void main() {
  // runApp(const MyApp());
  runApp(const MaterialApp(home: MyApp())); // MaterialAppで先にラップする
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
      await Future.delayed(const Duration(seconds: 3));
      if (mounted) {
        showDialog<void>(
          context: context,
          builder: (context) => const SampleDialog(),
        );
      }
    });
  }

  
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}
class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  
  Widget build(BuildContext context) {
    return Text(
      'Hello, World!',
      style: Theme.of(context).textTheme.headlineMedium,
    );
  }
}
class SampleDialog extends StatelessWidget {
  const SampleDialog({super.key});

  
  Widget build(BuildContext context) {
    return const AlertDialog(title: Text('This is sample'));
  }
}

解決方法 MyAppをMaterialAppでさらにラップ

どうやら、使用するMyappクラスの前にMaterialAppを使わないといけなかったようです。

このように修正すればOK!

void main() {
  // runApp(const MyApp()); 修正前
  runApp(const MaterialApp(home: MyApp())); // 修正後
}

参考はこちら
https://stackoverflow.com/questions/56275595/no-materiallocalizations-found-myapp-widgets-require-materiallocalizations-to

まとめ

当初悩みましたが、よくエラー文読めば意外と簡単に対処できました。
他に良い解決策もあるかもしれませんが、もし情報ありましたらいただけると幸いです。

参考資料

https://stackoverflow.com/questions/56275595/no-materiallocalizations-found-myapp-widgets-require-materiallocalizations-to

Flutter大学

Discussion