【Flutter】Navigatorでいい感じの遷移アニメーションをつけてみる


![スクリーンショット 2023-12-22 15.10.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/8e81ac5a-a705-c9be-04b0-5a525355b9c7.png)



:::note info
この記事は、<b>株式会社ゆめみの23卒 Advent Calendar 2023</b>のアドベントカレンダーになります！！
色々な種類の記事が投稿されるので、お楽しみに🤗
:::


https://qiita.com/advent-calendar/2023/yumemi-23-graduation


# はじめに

アプリを作る上でどんなアプリがユーザーにとっていいものかを研究するのは個人的には好きですし、作れた時の達成感はすごい嬉しいですね。

今回は画面遷移のアニメーションに着眼点を見つけて、作ってみることにしました。
（作ってみたと言っていますが、ChatGPTに作ってもらい、調整しました💦）

# 色々なアニメーションを作成してみました
まずはいくつか作成してみたもののデモを表示します。

| black out | white out | slide in  |
|:-----------:|:------------:|:------------:|
| ![black.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/a02e430b-2164-dac5-71e2-a902057a8aff.gif) | ![white.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/b569fbf0-47bf-021d-9213-fcdffb140d5d.gif)  | ![slide.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/88a6a2f4-4fa8-81c2-2edb-2a9df196b507.gif)  |


| scale up transition | flip transtion | elatstic transtion |
|:-----------:|:------------:|:------------:|
| ![scale.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/8a032c6c-0c1c-6b49-70e5-27a1331b60bc.gif) | ![flip.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/030143d6-6144-bd7c-6e9b-2430cb1733cc.gif)  | ![elap.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/a35e55ea-6fb9-7b8a-78ad-2187c88a940c.gif)  |



| diagonal slide transtion | vertical flip transtion | fade through color transition |
|:-----------:|:------------:|:------------:|
| ![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.16.11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/de727357-572a-813f-2a1e-d52e9173a11e.gif) |  ![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.16.54.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/f115c0fd-386f-745c-80a1-c7b52124c5bd.gif) | ![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.17.42.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/8f5b4498-f15b-2789-f08f-497d2e7fe848.gif)  |


これ使ってみたい！というものがあれば下に実装例を記載しています〜〜😚



# それぞれの実装を紹介

## blackOut

blackOutを使用すると、画面が黒くなりながら新しい画面に遷移します。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.09.48.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/5e21f8e7-1334-7108-004a-57a264888d18.gif)



```dart
PageRouteBuilder<Object?> blackOut(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionDuration: const Duration(seconds: 1),
    reverseTransitionDuration: const Duration(seconds: 1),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      final color = ColorTween(
        begin: Colors.transparent,
        end: Colors.black,
      ).animate(
        CurvedAnimation(
          parent: animation,
          curve: const Interval(0, 0.5, curve: Curves.easeInOut),
        ),
      );
      final opacity = Tween<double>(
        begin: 0,
        end: 1,
      ).animate(
        CurvedAnimation(
          parent: animation,
          curve: const Interval(0.5, 1, curve: Curves.easeInOut),
        ),
      );
      return AnimatedBuilder(
        animation: animation,
        builder: (context, child) {
          return Container(
            color: color.value,
            child: Opacity(
              opacity: opacity.value,
              child: child,
            ),
          );
        },
        child: child,
      );
    },
  );
}
```
```dart
onPressed: () {
  Navigator.of(context).push(
    blackOut(const SecondScreen()),
  );
},
```


## whiteOut

whitekOutを使うと、画面が白くなりながら新しい画面に遷移します。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.10.36.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/e4f32873-7c07-8476-6e2f-5041ef968ce9.gif)



```dart
PageRouteBuilder<Object?> whiteOut(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionDuration: const Duration(seconds: 1),
    reverseTransitionDuration: const Duration(seconds: 1),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      final color = ColorTween(
        begin: Colors.transparent,
        end: Colors.white,
      ).animate(
        CurvedAnimation(
          parent: animation,
          curve: const Interval(0, 0.5, curve: Curves.easeInOut),
        ),
      );
      final opacity = Tween<double>(
        begin: 0,
        end: 1,
      ).animate(
        CurvedAnimation(
          parent: animation,
          curve: const Interval(0.5, 1, curve: Curves.easeInOut),
        ),
      );
      return AnimatedBuilder(
        animation: animation,
        builder: (context, child) {
          return Container(
            color: color.value,
            child: Opacity(
              opacity: opacity.value,
              child: child,
            ),
          );
        },
        child: child,
      );
    },
  );
}
```

```dart
onPressed: () {
  Navigator.of(context).push(
    whiteOut(const SecondScreen()),
  );
},
```


## slideIn

slideInを使用すると、画面が下から上へスライドしながら現れます。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.11.36.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/a3fc12a5-e706-98fe-f558-5b43bffe5917.gif)


```dart
PageRouteBuilder<Object?> slideIn(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) {
      return screen;
    },
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      const begin = Offset(0, 1);
      const end = Offset.zero;
      final tween = Tween(begin: begin, end: end)
          .chain(CurveTween(curve: Curves.easeInOut));
      final offsetAnimation = animation.drive(tween);
      return SlideTransition(
        position: offsetAnimation,
        child: child,
      );
    },
  );
}
```

```dart
onPressed: () {
  Navigator.of(context).push(
    slideIn(const SecondScreen()),
  );
},
```


## scaleUpTransition

scaleUpTransitionを使用すると、画面が小さくから大きくなりながら現れ、同時にフェードインします。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.12.33.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/1854dc53-387d-e74f-b845-22f6143cc4b5.gif)


```dart

PageRouteBuilder<Object?> scaleUpTransition(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      return ScaleTransition(
        scale: Tween<double>(begin: 0.0, end: 1.0).animate(
          CurvedAnimation(parent: animation, curve: Curves.fastOutSlowIn),
        ),
        child: FadeTransition(
          opacity: Tween<double>(begin: 0.0, end: 1.0).animate(animation),
          child: child,
        ),
      );
    },
  );
}

```

```dart
onPressed: () {
  Navigator.of(context).push(
    scaleUpTransition(const SecondScreen()),
  );
},
```

## flipTransition

flipTransitionを使用すると、画面がY軸を中心に360度回転しながら遷移します。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.14.41.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/129e26f9-3298-f974-1b1d-2b5c2a364d61.gif)



```dart
PageRouteBuilder<Object?> flipTransition(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      return AnimatedBuilder(
        animation: animation,
        builder: (context, child) {
          final flipAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
            CurvedAnimation(parent: animation, curve: Curves.linear),
          );
          return Transform(
            transform: Matrix4.identity()
              ..setEntry(3, 2, 0.001)
              ..rotateY(2 * 3.14 * flipAnimation.value),
            alignment: Alignment.center,
            child: child,
          );
        },
        child: child,
      );
    },
  );
}
```

```dart
onPressed: () {
  Navigator.of(context).push(
    flipTransition(const SecondScreen()),
  );
},
```

## elasticTransition

elasticTransitionを使用すると、ゴムのような効果で画面がズームインします。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.15.10.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/1ea942a4-dc81-2d67-6a2e-3212be7eb41b.gif)


```dart
PageRouteBuilder<Object?> elasticTransition(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      final elasticAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(
        CurvedAnimation(parent: animation, curve: Curves.easeOutExpo),
      );
      return ScaleTransition(
        scale: elasticAnimation,
        child: child,
      );
    },
  );
}
```

```dart
onPressed: () {
  Navigator.of(context).push(
    elasticTransition(const SecondScreen()),
  );
},
```

## diagonalSlideTransition

diagonalSlideTransitionを使用すると、画面が左上から斜めにスライドしながら現れます。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.16.11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/ffbf3439-3758-f7de-1abb-85931b0ed7a0.gif)


```dart
PageRouteBuilder<Object?> diagonalSlideTransition(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      final offsetAnimation = Tween<Offset>(
        begin: const Offset(-1.0, -1.0),
        end: Offset.zero,
      ).animate(animation);

      return SlideTransition(
        position: offsetAnimation,
        child: child,
      );
    },
  );
}
```

```dart
onPressed: () {
  Navigator.of(context).push(
    diagonalSlideTransition(const SecondScreen()),
  );
},
```

## verticalFlipTransition

verticalFlipTransitionを使用すると、画面がX軸を中心に360度回転しながら遷移します。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.16.54.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/a7973aee-cd81-6659-e62b-bc59e4471306.gif)


```dart
PageRouteBuilder<Object?> verticalFlipTransition(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      final flipAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
        CurvedAnimation(parent: animation, curve: Curves.linear),
      );
      return AnimatedBuilder(
        animation: animation,
        builder: (context, child) {
          return Transform(
            alignment: Alignment.center,
            transform: Matrix4.identity()
              ..setEntry(3, 2, 0.001)
              ..rotateX(2 * pi * flipAnimation.value),
            child: child,
          );
        },
        child: child,
      );
    },
  );
}
```

```dart
onPressed: () {
  Navigator.of(context).push(
    verticalFlipTransition(const SecondScreen()),
  );
},
```

## fadeThroughColorTransition

fadeThroughColorTransitionを使用すると、指定された色を通してフェードイン/アウトすることで画面遷移を行います。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-22 at 14.17.42.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/632bd282-46a5-3ba0-9393-6eebd76840bf.gif)


```dart
PageRouteBuilder<Object?> fadeThroughColorTransition(
    Widget screen, Color transitionColor) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      final slowAnimation = CurvedAnimation(
        parent: animation,
        curve: Curves.easeOut,
      );
      return AnimatedBuilder(
        animation: slowAnimation,
        builder: (context, child) {
          return Stack(
            children: [
              FadeTransition(
                opacity:
                    Tween<double>(begin: 1.0, end: 0.0).animate(slowAnimation),
                child: Container(color: transitionColor),
              ),
              FadeTransition(
                opacity:
                    Tween<double>(begin: 0.0, end: 1.0).animate(slowAnimation),
                child: child,
              ),
            ],
          );
        },
        child: child,
      );
    },
  );
}

```

```dart
onPressed: () {
  Navigator.of(context).push(
    fadeThroughColorTransition(
      const SecondScreen(),
      Colors.white,
    ),
  );
},
```


# 最後に

こちらにリポジトリを載せておきます。

https://github.com/iseruuuuu/good_navigator_app

アニメーションについての研究はまだまだできそうな気がするので色々な場面でも使っていこうと思いました！


# ちょっとした宣伝

株式会社ゆめみの23卒のメンバーでアドベントカレンダーを作成しています。
新卒のフレッシュな記事がたくさん投稿予定なので、少しでも興味があれば購読いただけると幸いです！！


https://qiita.com/advent-calendar/2023/yumemi-23-graduation



<a href="https://qiita.com/advent-calendar/2023/yumemi-23-graduation">
  <img alt="YUMEMI New Grad Advent Calendar 2023" width="1240" height="112" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/6d8a7098-9aef-a19a-54fe-83a2d493e033.png">
</a>