52 lines
2.8 KiB
Dart
52 lines
2.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
/// 단추가 있는 아래 알림 (SnackBar) 을 띄우는 곳은 이것 하나 (E4 U2, 관리자 승인 10/22).
|
|
///
|
|
/// 약속
|
|
/// - [NoticeLife.timed] (기본): [appNoticeDuration] 뒤 저절로 닫힌다. 앱 전체 한 값이다 (화면을 찍는 시험만 [NoticeTiming] 으로 정함).
|
|
/// (Flutter 3.29 부터 단추가 있는 SnackBar 는 기본으로 닫히지 않아, 끝 알림이 다음 일을 하는 내내 아래 줄을 가렸다 - 점검 U2)
|
|
/// - [NoticeLife.untilClosed]: 사용자가 ✕ 를 누르거나 부르는 쪽이 돌려받은 것으로 닫을 때까지 둔다.
|
|
/// 상태가 남는 중요한 안내 (248 DNS · 198 감지한 언어) 와, 부르는 쪽이 일이 끝나면 닫는 것 (다운로드 준비 · 시작 전 [취소]) 만.
|
|
/// - 두 경우 모두 ✕ 로 바로 닫을 수 있다. [action] 을 누르면 알림이 닫히고 그 일을 한다.
|
|
/// - [clearOthers] 면 앞서 떠 있던 알림을 치우고 바로 보인다 (되돌리기처럼 바로 보여야 하는 것).
|
|
/// - [messenger] 가 없으면 아무것도 하지 않고 null.
|
|
const appNoticeDuration = Duration(seconds: 10);
|
|
|
|
/// [NoticeLife.timed] 알림이 저절로 닫히기까지를 아래 화면에 정해 준다. 없으면 [appNoticeDuration] (앱은 늘 없음 - U2 의 값).
|
|
/// 화면을 찍는 시험이 MaterialApp 을 이것으로 감싸 정한다 (실제 시간 10초가 찍는 때와 겹쳐 장면이 실행마다 달라지지 않게)
|
|
class NoticeTiming extends InheritedWidget {
|
|
final Duration duration;
|
|
const NoticeTiming({super.key, required this.duration, required super.child});
|
|
|
|
/// [context] 위에서 정한 알림 시간 (없으면 [appNoticeDuration])
|
|
static Duration of(BuildContext context) => context.getInheritedWidgetOfExactType<NoticeTiming>()?.duration ?? appNoticeDuration;
|
|
|
|
@override
|
|
bool updateShouldNotify(NoticeTiming old) => duration != old.duration;
|
|
}
|
|
|
|
enum NoticeLife { timed, untilClosed }
|
|
|
|
/// 알림의 단추 (이름 · 누르면 할 일)
|
|
typedef NoticeAction = (String label, VoidCallback onPressed);
|
|
|
|
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? showNotice(
|
|
ScaffoldMessengerState? messenger, {
|
|
String? text,
|
|
Widget? content,
|
|
NoticeAction? action,
|
|
NoticeLife life = NoticeLife.timed,
|
|
bool clearOthers = false,
|
|
}) {
|
|
assert((text == null) != (content == null), 'text 와 content 중 하나만'); // l10n-skip
|
|
if (messenger == null) return null;
|
|
if (clearOthers) messenger.clearSnackBars();
|
|
return messenger.showSnackBar(SnackBar(
|
|
content: content ?? Text(text!),
|
|
duration: life == NoticeLife.timed ? NoticeTiming.of(messenger.context) : const Duration(days: 1),
|
|
persist: life == NoticeLife.untilClosed,
|
|
showCloseIcon: true,
|
|
action: action == null ? null : SnackBarAction(label: action.$1, onPressed: action.$2),
|
|
));
|
|
}
|