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()?.duration ?? appNoticeDuration; @override bool updateShouldNotify(NoticeTiming old) => duration != old.duration; } enum NoticeLife { timed, untilClosed } /// 알림의 단추 (이름 · 누르면 할 일) typedef NoticeAction = (String label, VoidCallback onPressed); ScaffoldFeatureController? 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), )); }