Flutter BottomNavigationBar의 탭 간 전환을 수행하는 방법

전치



Flutter의 BottomNavigationBar를 사용하여 탭 간 전환을 구현하는 방법에 4고 8을 썼습니다.
그 구현 방법을 공유합니다.
아래 그림과 같이 탭 A-화면 1에서 탭 B-화면 2로 전환하고 싶은 경우가 없습니까?
또, 탭 B-화면 2로부터 돌아올 때에는, 탭 A-화면 1이 아니고 탭 B-화면 1로 돌아가고 싶다고 생각했습니다.



주제



Pages API를 state_notifier를 사용하여 구현했습니다.

Pages API에의 사용 방법에 대해서는, 아래의 기사가 참고로 했습니다.

state_notifier의 사용 방법에 대해서는, 아래의 기사를 참고로 했습니다.

그럼, 구현 방법을 자세하게 설명합니다.

State 정의



state_notifier 에 보관 유지하는 State(상태)가 되는 클래스를 정의합니다.
속성으로 갖는 것은 다음 항목입니다.
  • 현재 표시된 탭
  • 탭에 표시되는 페이지 목록

  • 구현 이미지는 아래와 같습니다.
    enum BottomTab {
      タブ1,
      タブ2,
    }
    
    @freezed
    class RouteState with _$RouteState {
      const factory RouteState({
        required BottomTab 選択中のタブ,
        @Default([]) List<Page> タブ1ページ一覧,
        @Default([]) List<Page> タブ2ページ一覧,
      }) = _RouteState;
    }
    

    실제 구현 샘플은 여기입니다.

    State의 값은 다음과 같이 변경됩니다.

    초기 상태 (탭 1-화면 1이 표시됨)


    속성 이름
    속성 값


    선택한 탭
    탭 1

    탭 1 페이지 일람
    []

    탭 2 페이지 일람
    []



    탭 2를 누르십시오 (탭 2 - 화면 1이 표시됨)


    속성 이름
    속성 값


    선택한 탭
    탭 2

    탭 1 페이지 일람
    []

    탭 2 페이지 일람
    []



    화면 2를 누르십시오 (탭 2 - 화면 2가 표시됨)


    속성 이름
    속성 값


    선택한 탭
    탭 2

    탭 1 페이지 일람
    []

    탭 2 페이지 일람
    [탭 2-화면 2]


    Notifier 정의



    State를 갱신하는 Notifier를 아래와 같이 정의합니다.
    class RouteStateNotifier extends StateNotifier<RouteState> with LocatorMixin {
      RouteStateNotifier() : super(const RouteState(tab: initialTab));
    
      Future changeIndex(BottomTab tab) async {
        // 選択中のタブを切り替える
      }
    
      Future push(BottomTab tab, Page page) async {
        // ページ一覧の末尾にページを追加する
      }
    
      Future pop(BottomTab tab) async {
        // ページ一覧の末尾のページを取り除く
      }
    
      Future replace(BottomTab tab, List<Page> pages) async {
        // ページ一覧のまるっと入れ替える
      }
    }
    

    Navigator 정의



    State의 저장된 페이지 목록을 화면에 표시하기 때문에 Navigator의 페이지에 전달합니다.
    class ProjectsNavigator extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final pages = context.select((RouteState state) => state.タブ2ページ一覧);
        return Navigator(
          pages: [
            タブ2-画面1(divisionId: divisionId!, openDrawer: _openDrawer),
            ...pages
          ],
        );
      }
    }
    

    BottomNavigationBar 구현



    BottomNavigationBar와 State를 연결합니다.
    class MainScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final tab = context.select((RouteState state) => state.選択中のタブ);
        final index = tab.getIndex();
        return WillPopScope(
          child: Scaffold(
            body: IndexedStack(
              index: index,
              children: _children,
            ),
            bottomNavigationBar: BottomNavigationBar(
              currentIndex: index,
              items: _tabItems,
              onTap: (int index) {
                final tab = BottomTabExt.getTab(index);
                final notifier = context.read<RouteStateNotifier>();
                if (tab != context.read<RouteState>().tab) {
                  notifier.changeIndex(tab);
                }
              },
            ),
          ),
        );
      }
    }
    

    화면 전환 구현



    나중에 Notifier를 통해 State를 업데이트하여 화면 전환을 실현할 수 있습니다.
    class HomeScreen extends StatefulWidget {
      @override
      _HomeScreenState createState() => _HomeScreenState();
    }
    
    class _HomeScreenState extends State<HomeScreen> {
      Future _onPressedProjectDetail() async {
        final notifier = context.read<RouteStateNotifier>();
        await notifier.changeIndex(BottomTab.タブ2);
        await notifier.replace(BottomTab.タブ2, [
          タブ2-画面2(),
        ]);
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                ElevatedButton(
                  onPressed: _onPressedProjectDetail,
                  child: const Text('タブ2-画面2へ'),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    비고



    그 외, 화면 천이로 구애된 점의 메모입니다.
    이 변의 구현 샘플은, 하기 클래스에 집약되고 있습니다.

    탭 상태 유지



    IndexedStack을 사용하여 탭 상태를 유지합니다.
    다만, 보통으로 실장해 버리면 부모가 빌드되는 타이밍으로 아이의 탭 모두가 빌드되어 버립니다.
    그래서 초기 상태는 LoadingScreen을 유지하고 탭이 불린 타이밍에 본명의 탭(ProjectsNavigator)으로 교체한다는 것을 하고 있습니다.

    선택한 탭 누르기



    선택한 탭을 누르면 첫 화면으로 돌아갑니다.
    (예: 탭 2-화면 2를 표시하고 있는 상태에서 탭 2가 눌러지면 탭 2-화면 1로 돌아갑니다.)

    Android 뒤로 버튼 지원



    WillPopScope를 사용하여 뒤로 버튼을 누르면 아래와 같이 동작하도록 하고 있습니다.
  • Drawer 가 표시되고 있는 상태의 경우, Drawer 를 닫는다.

  • 탭 2-화면 2가 표시되면, 탭 2-화면 1로 되돌아간다.

  • 탭 2-화면 1이 표시되면 탭 1-화면 1로 돌아갑니다.

  • 탭 1 - 화면 1이 표시되면 앱을 닫습니다.

  • 가로 스와이프 대응



    탭 1-화면 1이나 탭 2-화면 1 등 탭 내의 선두의 화면에서 가로 스와이프한 경우는, Drawer를 표시하고 싶습니다만, 탭 2-화면 2등 천이처에 있는 경우는, 가로 스와이프로 전의 화면으로 돌아가고 싶습니다.
    그 때문에, drawerEnableOpenDragGesture 라고 하는 프로퍼티을 State에 맞추어 제어하고 있습니다.

    좋은 웹페이지 즐겨찾기