【Flutter】Typically, the ScaffoldMessenger widget is introduced ...

ネコニウム研究所

PCを利用したモノづくりに関連する情報や超個人的なナレッジを掲載するブログ

【Flutter】Typically, the ScaffoldMessenger widget is introduced ...

2024-6-18 | ,

Flutterでスナックバーを表示しようとすると発生するTypically, the ScaffoldMessenger widget is introduced by the MaterialApp at the top of your application widget tree.をなんとかしたい!

概要

今回の記事では、Flutterでスナックバーを表示使用すると発生するTypically, the ScaffoldMessenger widget is introduced by the MaterialApp at the top of your application widget tree.をなんとかする手順を掲載する。

こちらは専用のパッケージは不要。

仕様書

環境

  • Android Studio Jellyfish | 2023.3.1 Patch 2
  • Flutter 3.19.6

手順書

スナックバー表示する際の

ScaffoldMessenger.of(context).showSnackBar(snackBar)`

で使われるBuildContextMaterialAppの中のScaffoldで作られてものである必要がある。

エラーが発生する例

import 'package:flutter/material.dart';

void main() {
  runApp(SnackBarSample1());
}

class SnackBarSample1 extends StatelessWidget {
  const SnackBarSample1({super.key});

  @override
  Widget build(BuildContext context) {
    final snackBar = SnackBar(
      action: SnackBarAction(
        label: "Close",
        onPressed: () => {ScaffoldMessenger.of(context).hideCurrentSnackBar()},
      ),
      backgroundColor: Colors.lightBlue,
      content: const Text("Nyahhhhn!"),
      duration: const Duration(seconds: 5),
      behavior: SnackBarBehavior.floating,
    );

    return
      MaterialApp(
        home: Scaffold(
          body: SafeArea(
            child: Center(
              child: SizedBox(
                width: double.infinity,
                child: TextButton(
                  style: TextButton.styleFrom(
                    backgroundColor: Colors.blue,
                    shape: const RoundedRectangleBorder(
                      borderRadius: BorderRadius.all(Radius.circular(4)),
                    ),
                    padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
                  ),
                  onPressed: () => {
                    ScaffoldMessenger.of(context).showSnackBar(snackBar)
                  },
                  child: const Padding(
                    padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
                    child: Text("Show Snack Bar",
                        style: TextStyle(fontSize: 16.0, color: Colors.white)),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

この例だとMaterialAppの外のBuildContextが使われてるため、スナックバーを表示しようとした時にエラーは発生する。

エラーが発生しない例 (1)

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: const SnackBarSample1()
      )
    )
  );
}

class SnackBarSample1 extends StatelessWidget {
  const SnackBarSample1({super.key});

  @override
  Widget build(BuildContext context) {
    final snackBar = SnackBar(
      action: SnackBarAction(
        label: "Close",
        onPressed: () => {ScaffoldMessenger.of(context).hideCurrentSnackBar()},
      ),
      backgroundColor: Colors.lightBlue,
      content: const Text("Nyahhhhn!"),
      duration: const Duration(seconds: 5),
      behavior: SnackBarBehavior.floating,
    );

    return SafeArea(
      child: Center(
        child: SizedBox(
          width: double.infinity,
          child: TextButton(
            style: TextButton.styleFrom(
              backgroundColor: Colors.blue,
              shape: const RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(4)),
              ),
              padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
            ),
            onPressed: () => {
              ScaffoldMessenger.of(context).showSnackBar(snackBar)
            },
            child: const Padding(
              padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
              child: Text("Show Snack Bar",
                  style: TextStyle(fontSize: 16.0, color: Colors.white)),
            ),
          ),
        ),
      ),
    );
  }
}

こんな感じでMaterialAppの中のScaffoldで作られてるBuildContextを使うとエラーが発生しない。

エラーが発生しない例 (2)

どうしても、クラスを分けたくない場合はMaterialAppGlobalKeyを設定してそれを使う。

import 'package:flutter/material.dart';

void main() {
  runApp(SnackBarSample1());
}

class SnackBarSample1 extends StatelessWidget {
  const SnackBarSample1({super.key});

  final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    final snackBar = SnackBar(
      action: SnackBarAction(
        label: "Close",
        onPressed: () => {ScaffoldMessenger.of(navigatorKey.currentContext!).hideCurrentSnackBar()},
      ),
      backgroundColor: Colors.lightBlue,
      content: const Text("Nyahhhhn!"),
      duration: const Duration(seconds: 5),
      behavior: SnackBarBehavior.floating,
    );

    return
      MaterialApp(
        home: Scaffold(
          body: SafeArea(
            child: Center(
              child: SizedBox(
                width: double.infinity,
                child: TextButton(
                  style: TextButton.styleFrom(
                    backgroundColor: Colors.blue,
                    shape: const RoundedRectangleBorder(
                      borderRadius: BorderRadius.all(Radius.circular(4)),
                    ),
                    padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
                  ),
                  onPressed: () => {
                    ScaffoldMessenger.of(navigatorKey.currentContext!).showSnackBar(snackBar);
                  },
                  child: const Padding(
                    padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
                    child: Text("Show Snack Bar",
                        style: TextStyle(fontSize: 16.0, color: Colors.white)),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
      navigatorKey: navigatorKey,
    );
  }
}

スナックバーを呼び出すコードも下記のように変わる。

ScaffoldMessenger.of(navigatorKey.currentContext!).showSnackBar(snackBar)

まとめ(感想文)

なかなかめんどい。