FlutterのNavigator1.0を完全に理解した気になっていたので再入門した

##何でまとめようと思ったのか
携わっているプロジェクトはFlutterが正式リリースされる前から存在していたので、
年を重ねて大きくなり、遷移も複雑になってきていた。
発端は、いつからかログイン画面が複数回Pushされているような挙動が起きたり、ダイアログ表示の時に背景がだんだん黒くなってきたりといった現象が起き始めていた。（チームでうまくNavigatorが扱えてなかった。）

メンバーの認識も、まちまちで「あれってルートを置き換えてくれるだけの遷移方法だよね？」、「てっきり、置き換えると同時に他も破棄してくれるもんだと思っていた」とバラバラだった。

基本的なところにはなるが、今一度見直してみた。


##画面遷移の種類
※AndroidだろうがiOSだろうがFlutterだろうが
画面遷移に関してはHistoryというものが存在し、
その形式はStack構造になっている。
###push() & pushNamed()
Stackの一番上に、指定したルートを追加する
![スクリーンショット 2021-12-23 163322.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/04a73eb0-c36f-2d24-4dd5-4de87e76e15b.png)

![スクリーンショット 2021-12-23 163408.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/4080362c-197a-5f39-5e49-47cbf291c447.png)


###pop()
Stackの一番上にあるルートを削除する
![スクリーンショット 2021-12-23 164835.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/d7b7590c-2366-8194-825d-db5878c38162.png)


###popAndPushNamed() & pushReplacementNamed()
Stackの一番上にあるルートを削除し、指定したルートを新規にStackの一番上に追加する。
挙動は一緒だが違うのは、削除されるルートのアニメーションの違いにある。

####どんな時に使う？
使用ケース的には、遷移先の画面から前の画面に戻したくないとき。
主にログイン画面からの遷移とかスプラッシュ画面からの遷移とかに使う。

###before
![スクリーンショット 2021-12-23 174512.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/202e5ccd-c0a3-a68a-763b-75a51c0cdc1c.png)
###after
![スクリーンショット 2021-12-23 174836.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/08d78364-b280-0833-419a-2925c27bfb65.png)

###before
![スクリーンショット 2021-12-23 174655.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/4b945720-1456-e69d-06a0-d4fcea78c0da.png)
###after
![スクリーンショット 2021-12-23 174710.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/518058e9-b0f2-0112-dfb3-3f1b1428568e.png)
####popAndPushNamed:
Stackからルートが削除されるとき、popアニメーションが行われる
![ezgif-3-05f5126aaa.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/30650f0e-15d8-7301-31f0-abb059585c59.gif)


####pushReplacementNamed:
Stackからルートが削除されるとき、popアニメーションが行われない
![ezgif-3-ca25755147.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/0bb28979-de96-6d58-4610-02a41eeeccef.gif)


###pushNamedAndRemoveUntil()

ルートを指定しないとPushされたルートより前にあるルートを全部破棄する。

```dart
Navigator.of(context).pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
```
![スクリーンショット 2021-12-23 184455.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/4c9d4c29-b941-40da-c78f-f5272f5c31c8.png)


第2引数にルートを指定すると、指定されたルートより上に存在するルートをすべて削除し、残ったルートの上にルートを追加する。

```dart
Navigator.of(context).pushNamedAndRemoveUntil('/page5', ModalRoute.withName('/page1'));
```
![スクリーンショット 2021-12-23 191322.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/5e384760-c195-3bdd-b68a-f935d8b46863.png)

###popUntil()
指定されたルートより上に溜まっているルートをすべてStackから削除する。

![スクリーンショット 2021-12-23 230534.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84002/9df47353-7d51-c4e3-0611-dedddeaa3db7.png)


### 遷移時の値の受け渡し

だいたい、Argumentsクラス作って渡すことが多いと思う。

```dart
@immutable
class PageArguments{
  final Customer customer;
  final int index;

  PageArguments({
    @required this.customer,
    @required this.index,
  });
}
```

```dart
Navigator.of(context).pushReplacementNamed(
  '/page2',
  arguments: PageArguments(
    customer: customerState.profile 
    index: pageState.pageList
        .indexWhere((element) => element == PageType.normal),
  ),
);
```