【Flutter】通知メッセージ表示としてOverlayクラスを導入する
はじめに
前回、こちらの記事で、RenderFlex overflowedエラーの対応法について記載した。
今回は恒久対応として、Overlay
クラスを使用して、他のウィジェットの上に新しいウィジェットレイヤーを表示する、のをやりたいと思う。
ちなみに、現状はアプリのホーム画面に表示する何らかのメッセージを、画面トップにTextウィジェットとして表示している。
overlay_supportパッケージの導入
overlay_supportという、トーストやアプリ内通知を簡単に作成できるパッケージがあったので、こちらを使用することにする。
$ flutter pub add overlay_support
$ flutter pub get
OverlaySupport
でWrapする
AppWidgetをアプリのメインで 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: () {},
);
},
);
},
);
}
});
}
ただし、これをビルドすると、下記のエラーが出る。
続きは、こちらで記載しています。
Discussion