
앱을 사용하다보면 무언가 실패하거나 성공했을 때 위 이미지처럼 메시지가 출력되고 자동으로 사라지는 경우를 본 적이 있을 것이다.
안드로이드에선 흔히 Toast라고 부른다.
먼저 2가지 방법이 있는데,
Stack 위젯 또는 Overlay를 사용하는 방법이 있다. (더 있을수도 있음)
하지만, Stack의 경우 해당 화면에서 벗어나는 경우 토스트 메세지까지 사라지는 경우가 발생하게 된다.
로직상 문제가 있는 것은 아니지만, 사용자에게 불편감을 안겨줄 수 있다.
Overlay는 특정 위젯을 레이아웃에 영향 받지 않고 위에 그릴 수 있다.
toast() async {
// 앱이 실행중인 상태라면 null일 수가 없지만,
// 위젯 트리에 붙지 않은 상태일 수 있기에 null을 반환할 수 있음.
BuildContext? context = App.navigatorKey.currentContext;
// final 변수로 받을 수 있지만, 위젯 안에 overlayEntry 내장 함수를 써야 하기 때문에
// nullable 변수로 선언하고, null이 아닌 경우에만 실행되도록 처리
OverlayEntry? toast;
if (context == null) return;
final overlay = Overlay.of(context);
toast = OverlayEntry(
builder: (context) => CustomToast(
msg: "test test",
callback: () => toast?.remove(),
),
);
// overlay에 overlayEntry를 넣어준다
overlay.insert(toast);
// toast가 출력되있는 시간
await Future.delayed(const Duration(seconds: 2));
// toast 제거
toast.remove();
}
Overlay는 MaterialApp에 있는 Navigator가 생성하게 된다.
MaterialApp은 화면이 쌓인 상태를 NavigatorState로 가지고 있으며, 해당 상태의 context를 GlobalKey로 가져와 활용했다.context를 인자로 받아 사용할 수 있지만,
어느 상황에서나 편리하게 사용하기 위해 해당 방법을 채택했다.
그런데 토스트를 띄우려니 에러가 발생한다.
No Overlay widget found
해결방법은 MaterialApp 하위 builder 파라미터에 overlay를 랩핑해서 엔트리에 overlayEntry를 배열에 추가하여 리턴시켜주면 된다.
builder: (_, child) => Overlay(
initialEntries: [
OverlayEntry(builder: (context) => child!),
],
),
동일한 타입의 BuildContext이지만, 각 화면에서 사용할 때의 context와 navigatorKey를 통해 가져온 값이 다르게 동작한다.
이 내용은 추후 BuildContext 포스팅에서 정리하자.