[Flutter] 뒤로 가기 두 번 눌러서 앱 종료되도록 하기 | PopScope

앱 실행 중에 마지막 스택에서 뒤로 가기 버튼을 누르면 "뒤로 가기를 한 번 더 누르시면 종료됩니다"라는 내용의 스낵바가 뜨는 기능을 구현해 보자.

 

 

PopSope 위젯 추가하기

DateTime? backPressedTime;

@override
Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvoked: (bool didPop) {
        if (didPop) {
          return;
        }

        DateTime nowTime = DateTime.now();
        if (backPressedTime == null ||
            nowTime.difference(backPressedTime!) > const Duration(seconds: 2)) {
          backPressedTime = nowTime;
          showSnackbar('한 번 더 누르시면 종료됩니다.');
        } else {
          SystemNavigator.pop(); // 앱 종료
        }
      },
      child: const Scaffold(),
    );
}
  • `Scaffold` 위젯을 `PopScope`로 감싼다.
  • `onPopInvoked`에 뒤로 가기 이벤트가 감지될 때 실행할 코드를 작성한다.
  • 처음 뒤로 가기를 했을 때는 스낵바를 띄우고, 설정한 시간 안에 한 번 더 뒤로 가기를 눌렀을 때 앱이 종료되도록 했다.

 

PageView 사용 시 주의할 점

  • 처음에는 각각의 탭에 `PopScope` 위젯을 추가했다.
  • 그런데 타임라인 탭에서 뒤로 가기를 누르면 홈 탭과 타임라인 탭의 `onPopInvoked`가 동시에 실행되는 문제가 발생했다. 이로 인해 `onPopInvoked`가 두 번 실행되면서 무조건 앱이 종료되었다.
  • 문제의 원인은 `PageView`가 모든 탭을 활성화 상태로 유지하면서 모든 탭의 `onPopInvoked`가 중첩되어 실행되는 것이었다.

 

@override
Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvoked: (bool didPop) {
        // 제외하고 싶은 탭이 있는 경우 if문 추가
        if (_currentIndex != 2) {
            DateTime nowTime = DateTime.now();
            if (backPressedTime == null ||
                nowTime.difference(backPressedTime!) > const Duration(seconds: 2)) {
              backPressedTime = nowTime;
              showSnackbar('한 번 더 누르시면 종료됩니다.');
            } else {
              SystemNavigator.pop(); // 앱 종료
            }
        }
      },
      child: Scaffold(
        body: PageView(
          physics: const NeverScrollableScrollPhysics(), 
          controller: _pageController,
          onPageChanged: _onItemTapped,
          children: _tabs,
        ),
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          currentIndex: _currentIndex,
          onTap: _onItemTapped,
          items: const [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              label: '홈',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.list),
              label: '타임라인',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.image_outlined),
              label: '앨범',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.person),
              label: 'MY',
            ),
          ],
        ),
      ),
    );
}
  • 문제를 해결하기 위해서 각 탭에 추가했던 `PopScope` 위젯을 지우고, `PageView`가 포함된 `Scaffold` 위젯 하나에만 `PopScope`를 사용했다.

 

참고 문서