😸

【Flutter】通知メッセージ表示としてOverlayクラスを導入する

2024/04/21に公開

はじめに

前回、こちらの記事で、RenderFlex overflowedエラーの対応法について記載した。

今回は恒久対応として、Overlay クラスを使用して、他のウィジェットの上に新しいウィジェットレイヤーを表示する、のをやりたいと思う。

https://kazulog.fun/others/flutter-renderflex-overflowed/

ちなみに、現状はアプリのホーム画面に表示する何らかのメッセージを、画面トップにTextウィジェットとして表示している。

IMG_52B53F466314-1.jpeg

overlay_supportパッケージの導入

overlay_supportという、トーストやアプリ内通知を簡単に作成できるパッケージがあったので、こちらを使用することにする。

https://pub.dev/packages/overlay_support

$ flutter pub add overlay_support
$ flutter pub get

AppWidgetをOverlaySupportでWrapする

アプリのメインで OverlaySupport.global() を使うために、main.dart に次のように記述。

return OverlaySupport.global(child: MaterialApp());

メッセージ表示クラスを修正

元々、画面トップにTextウィジェットを表示するクラスとして作成していたのがこちら。

メッセージを右の閉じるアイコンで閉じることができ、また、メッセージバーをクリックするとshow dialogが開くというもの。これを、今回のOverlay_supportに置き換える。

// lib/widgets/top_message_bar.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class TopMessageBar extends ConsumerStatefulWidget {
  final String message;
  final VoidCallback onClose;
  final VoidCallback onTapped;

  const TopMessageBar({
    Key? key,
    required this.message,
    required this.onClose,
    required this.onTapped,
  }) : super(key: key);

  
  _TopMessageBarState createState() => _TopMessageBarState();
}

class _TopMessageBarState extends ConsumerState<TopMessageBar> {
  bool isVisible = true;

  
  Widget build(BuildContext context) {
    return Visibility(
      visible: isVisible,
      child: GestureDetector(
        onTap: widget.onTapped,
        child: Container(
          padding: const EdgeInsets.only(left: 16, top: 8, bottom: 8),
          decoration: BoxDecoration(
            color: Colors.blue.shade100,
            borderRadius: BorderRadius.circular(10),
          ),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            children: [
              Expanded(
                child: Text(
                  widget.message,
                  style: const TextStyle(fontSize: 14),
                ),
              ),
              IconButton(
                icon: const Icon(Icons.close),
                onPressed: () {
                  setState(() {
                    isVisible = false;
                  });
                  widget.onClose();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

変更後のコードがこちら。

duration パラメーターをオプショナルとして、デフォルトでメッセージバーを無期限に表示するように設定する。

**OverlaySupportEntry.of(context)?.dismiss();**は、表示されているオーバーレイウィジェット(この場合はメッセージバー)を削除するメソッド。これを使用することで、ユーザーが閉じるボタンを押した時やメッセージバー自体をタップした時にオーバーレイをプログラム的に閉じることができる。

// lib/widgets/top_message_bar.dart
import 'package:flutter/material.dart';
import 'package:overlay_support/overlay_support.dart';

void showCustomMessageBar({
  required BuildContext context,
  required String message,
  required VoidCallback onClose,
  required VoidCallback onTapped,
  Duration? duration, // オプショナルにして、デフォルトはnull(無期限表示)
}) {
  showOverlayNotification((context) {
    return GestureDetector(
      onTap: () {
        onTapped();
        OverlaySupportEntry.of(context)?.dismiss();
      },
      child: Container(
        padding: const EdgeInsets.all(10),
        margin: const EdgeInsets.symmetric(horizontal: 10),
        decoration: BoxDecoration(
          color: Colors.blue.shade100,
          borderRadius: BorderRadius.circular(10),
          boxShadow: [
            BoxShadow(
              color: Colors.black26,
              blurRadius: 10,
              spreadRadius: 1,
              offset: Offset(0, 5),
            ),
          ],
        ),
        child: Row(
          mainAxisSize: MainAxisSize.max,
          children: [
            Expanded(
              child: Text(
                message,
                style: const TextStyle(fontSize: 14),
              ),
            ),
            IconButton(
              icon: const Icon(Icons.close),
              onPressed: () {
                OverlaySupportEntry.of(context)?.dismiss();
                onClose();
              },
            ),
          ],
        ),
      ),
    );
  }, duration: duration); // デフォルトではdurationはnull
}

showCustomMessageBarの呼び出し

_HomeTabState にファームウェアのチェックとメッセージバー表示のロジックを追加する。

WidgetsBinding.instance.addPostFrameCallback を使用して、ビルドプロセスが完了した後にメッセージバーを表示するようにする。

// lib/screens/home/home_tab.dart

void initState() {
  super.initState();
  _checkFirmwareUpdate();
}

void _checkFirmwareUpdate() async {
  final firmwareUpdate = ref.watch(firmwareUpdateProvider);
  firmwareUpdate.whenData((response) {
    if (response.message == "NEW_FIRMWARE_AVAILABLE") {
      showCustomMessageBar(
        context: context,
        message: "新しいファームウェアバージョン(${response.newFirmwareVersion})がダウンロード可能です。",
        onClose: () {},
        onTapped: () {
          showDialog(
            context: context,
            builder: (BuildContext context) {
              return FirmwareUpdateDialog(
                newVersion: response.newFirmwareVersion ?? "",
                onConfirm: () {
                  final userNotifier = ref.read(userProvider.notifier);
                  userNotifier.updateUser(User(firmwareVersion: response.newFirmwareVersion));
                },
                onCancel: () {},
              );
            },
          );
        },
      );
    }
  });
}

ただし、これをビルドすると、下記のエラーが出る。

続きは、こちらで記載しています。
https://kazulog.fun/dev/flutter-oveerlay-support/

Discussion