flutter で Navigator 内に Navigator を配置するときの挙動

最近 flutter に入門したのですが、今作ってるアプリで、デフォルトのNavigator内に別のNavigatorを入れる必要ができたので、そのときの試行錯誤を記事に残しておきます。

# 前提
- アプリ内の一部でデフォルトの画面遷移とは別の画面遷移をしたい、
- Providerで状態管理している。(Providerは本筋ではないです。状態管理ができていたらなんでも大丈夫だと思います)

# 最初の実装
普通にページ内でNavigator遷移を実現しようと思うとこのようになります。

- defaultでFirstViewが呼ばれる。
- ページ内遷移でSecondViewとThirdViewがfullscreenDialogで呼ばれる。

```dart
// ...
class HogeView extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    Consumer<HogeProvider>(
      builder: (context, model, _) {
        return Container(
          child: Navigator(
            initialRoute: 'first',
            onGenerateRoute: (RouteSettings settings) {
              WidgetBuilder builder;
              bool fullscreenDialog = false;
              switch (settings.name) {
                case 'first':
                  builder = (BuildContext _) => FirstView();
                  break;
                case 'second':
                  builder = (BuildContext _) => SecondView();
                  fullscreenDialog = true;
                  break;
                case 'third':
                  builder = (BuildContext _) => ThirdView();
                  fullscreenDialog = true;
                  break;
                default:
                  throw Exception('Invalid route: ${settings.name}');
              }
              return MaterialPageRoute(
                builder: builder,
                settings: settings,
                fullscreenDialog: fullscreenDialog,
              );
            },
          ),
        );
      }
    );
  }
}

class HogeProvider extends ChangeNotifier {
  // ...
}
```

しかし、このままでは、HogeViewがなんらかの理由でrebuildされたときに、Navigatorがリセットます。
これは例えば、「ユーザーがSecondViewを見ていたはずが、HogeViewがrebuildされて、勝手にfirstViewに戻される。」みたいな現象が起こります。
要件によっては良いかもしれませんが、僕は困りました。

# 改善したコード

```dart
// ...
class HogeView extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    Consumer<HogeModel>(
      builder: (context, model, _) {
        return Container(
          child: Navigator(
            initialRoute: 'first',
            key: model.navigationKey,
            onGenerateRoute: (RouteSettings settings) {
              WidgetBuilder builder;
              bool fullscreenDialog = false;
              switch (settings.name) {
                case 'first':
                  builder = (BuildContext _) => FirstView();
                  break;
                case 'second':
                  builder = (BuildContext _) => SecondView();
                  fullscreenDialog = true;
                  break;
                case 'third':
                  builder = (BuildContext _) => ThirdView();
                  fullscreenDialog = true;
                  break;
                default:
                  throw Exception('Invalid route: ${settings.name}');
              }
              return MaterialPageRoute(
                builder: builder,
                settings: settings,
                fullscreenDialog: fullscreenDialog,
              );
            },
          ),
        );
      }
    );
  }
}

class HogeProvider extends ChangeNotifier {
  GlobalKey<NavigatorState> navigationKey = GlobalKey<NavigatorState>();
  // ...
}

```

変更点は、HogeProviderでnavigationKeyを定義し、Navigatorに渡したことです。

```dart
key: model.navigationKey,
```

このnavigationKeyをproviderによってアプリ内で一意に存在させ続けることで、rebuildされても同じNavigatorであることを伝えることができます。

# 感想
- flutterへの理解があまくて、調べるのにかなりの時間がかかった。
- Navigator内にNavigatorを入れる必要になったこと自体、アプリのデザインパターンに問題がある可能性がある。
- flutter初学者なので、認識がたりてないとこがあればコメントください。