LoginSignup
4
3

More than 1 year has passed since last update.

【Flutter】go_routerでBottomNavigationBarの永続化に挑戦する

Posted at

界隈で話題のNavigation2.0を簡単に実装出来るgo_routerパッケージ。

コード量も短く、比較的簡単に実装出来るのでとても良いと感じているのですが、使ってみた方に感想を聞いてみるとBottomNavigationBarTabBarなどの永続化が出来ず、そこが難点という話を複数名の方から伺いました。

まだ使い始めだったのでgo_routerの勉強も兼ねて、この問題に挑戦してみた結果の共有をしてみたいと思います。

あくまでも自分なりのアプローチであり、問題点なども洗い出せているわけではないので、自由研究の発表くらいだと思って愛のあるご指摘頂ければと思います

環境

SDK・パッケージ バージョン
Flutter 3.0.4 • channel stable
Dart 2.17.5 (stable)
flutter_riverpod ^1.0.4
go_router ^4.1.0

コード全文

persisted_bar_sample.gif

アプローチ

※ こちらはNavigator1.0とNavigator2.0を併用させたアプローチになります

  1. GoRouterクラスのnavigationBuilderを活用
  2. NavigatorクラスでラップしたBottomNavigationBarをメインのページスタックの上に表示
  3. タブでの画面遷移に見える様、ページ遷移時のアニメーションを調整

navigationBuilderパラメータではメインのページスタックの上にWidgetを重ねる事が出来ます。
https://gorouter.dev/navigator-builder

こちらを活用し、BottomNavigationBarを全てのページスタックの上に重ねて表示しています。

BottomNavigationBarMaterialPage配下に配置されている必要がある為、Navigatorクラスでラップする必要があります。

肝となるのはGoRouterの以下部分です。

  GoRouter(
    ...,
    navigatorBuilder: (context, state, child) {
      return Navigator(
        onPopPage: (route, dynamic __) => false,
        pages: [
          MaterialPage<Widget>(
            child: BottomNav(
              child: child,
            ),
          ),
        ],
      );
    },
  )

またタブでの画面遷移に見える様、トップレベルのページのみ遷移時のアニメーションを調整しています。
https://gorouter.dev/transitions

  GoRoute(
    name: 'simple',
    path: '/simple',
    pageBuilder: (context, state) => CustomTransitionPage(
        key: state.pageKey,
        child: const SimpleNavigationScreen(),
        transitionDuration: Duration.zero, // <= Duration.zeroで遷移する様、調整
        transitionsBuilder:
            (context, animation, secondaryAnimation, child) => child),

ページ別動作のサンプル

BottomNavigationBarの各タブではそれぞれ異なる画面遷移のサンプルを用意しました。

Simple Navigation Screen

simple_nav_sample.gif

サブルートへの画面遷移
https://gorouter.dev/sub-routes

  GoRoute(
    name: 'simple',
    ...,
    routes: [ // routes内に定義する事でsub-routeを繋げる
      GoRoute( 
        name: 'login',
        path: 'login',
        pageBuilder: (context, state) => MaterialPage(
          key: state.pageKey,
          child: const LoginScreen(),
        ),
      ),
      ...,
    ],
  ),

引数を渡した画面遷移
https://gorouter.dev/parameters

  GoRoute(
    name: 'simple',
    ...,
    routes: [
      ...,
      GoRoute(
        name: 'number',
        path: 'number/:id', // 引数を含むパスを定義
        builder: (context, state) {
          final id = state.params['id']!; // stateから引数を取り出す
          return NumberScreen(number: id);
        },
      ),
    ],
  ),

引数を含むパスで遷移

onTap: () => context.go('/simple/number/$id');

Overlay Navigation Screen

overlay_sample.gif

DialogModalBottomSheetの挙動のサンプルです。

DialogModalBottomSheetGoRouterで定義されたページスタックではなく、navigatorBuilder内に定義されたNavigatorクラス上に被さります

その為、これらをクローズする場合はGoRouter.of(context).pop()ではなく、Navigator.of(context).pop()を使います

  await showDialog<void>(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: const Text('This is the Dialog'),
        content: const Text('This is the content'),
        actions: [
          TextButton(
            child: const Text('Cancel'),
            onPressed: () => Navigator.of(context).pop(), // Navigatorのpopを使用
          ),
          TextButton(
            child: const Text('OK'),
            onPressed: () => Navigator.of(context).pop(),
          ),
        ],
      );
    },
  );

またshowModalBottomSheetではuseRootNavigatorパラメータを使う事で、Navigatorクラスの上に被せるか、GoRouterクラスの上に被せるかを操作出来ます

  await showModalBottomSheet<bool>(
    context: context,
    // trueでNavigatorの上、デフォルトのfalseでGoRouterの上に表示
    useRootNavigator: true, 
    builder: (context) {
      ...
    },
  );

TabBar Navigation Screen

tab_bar_sample.gif

BottomNavigationBarTabBarを併用した挙動のサンプルです。

TabBarView内からネストした画面へも問題なく遷移出来ます

問題点

・ブラウザバックに非対応

本アプローチではBottomNavigationBarの選択中インデックスをアイコンをタップする事で変更しています。

その為、Webのブラウザバックに対し、選択中インデックスの更新がされません。

Navigator2.0だけでの対応

本アプローチはNavigator1.0とNavigator2.0を併用したアプローチになります。

Navigator1.0と2.0の併用については公式ドキュメントでも「併用可」とする記述がある為、問題はないと思いますが、可能であれば一本化したい所。

ですが現状、Nabigator2.0のみでTabBarBottomNavigationBarの永続化は行えません。

ただ以下の通り、対応について度々議論されている為、将来的に対応される可能性は十分ありそうです。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3