【はじめてのRive】Flutterに2Dアニメーションスプラッシュスクリーンを導入する手順

追記 2021.3.6

[Rive2](https://rive.app/) が公開されたのに伴い、[旧Rive](https://flare.rive.app/) のURLが変わりました。
本記事は、[旧Rive](https://flare.rive.app/) について記述してものになりますので、ご了承ください。

# 概要

起動時にアニメーションがあるアプリって、なんかカッコいいですよねっ。

Flutterアプリケーションには、[Rive](https://flare.rive.app/)で作成した2Dアニメーションをスプラッシュスクリーンとして組み込むという選択肢があります。

この記事では、はじめてRiveにふれる方向けの簡単な2Dアニメーションの作り方と、それをスプラッシュスクリーンとしての組み込む手順を紹介します。

# ざっくりいうと・・・

* アプリローディング中はAndroid/iOSネイティブのスプラッシュスクリーンの仕掛けを使います。この記事では[flutter_native_splashプラグイン](https://pub.dev/packages/flutter_native_splash)を使った方法を紹介します。
* アプリローディング後の初期画面としてRiveで作成した2Dアニメーションを組み込みます。組み込みには[flare_flutterプラグイン](https://pub.dev/packages/flare_flutter)を使用します。
* 画面遷移の実装は、[Navigator](https://api.flutter.dev/flutter/widgets/Navigator-class.html)を使用しました。
* ページ遷移のイメージはこんな感じです:
![page transition.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109362/6116413a-ab54-1a7a-1c20-871e10223381.png)

# この記事の検証に使ったリソース

Riveの作品。Forkして改変することも含め、自由に使っていただくことができますので、必要に合わせてご利用ください。

* [app-splash - atsuteru](https://flare.rive.app/a/atsuteru/files/flare/app-splash/preview)

Flutterのソースコード。こちらも改変することも含め、自由に使っていただくことができますので、必要に合わせてご利用ください。

* [flutter_examples/examples_splash_flare_flutter[Qiita_examples_splash_flare_flutter_1.0] - atsuteru](https://github.com/atsuteru/flutter_examples/tree/Qiita_examples_splash_flare_flutter_1.0/examples_splash_flare_flutter)

## 環境

### バージョン

* Flutter 1.20.1

### プロジェクト設定

* この記事では、Flutterプロジェクトの`Use androidx.* artifacts`は`ON`に設定して検証しました。

# 本編

## 1) Riveで2Dアニメーションを作成する

[Rive](https://flare.rive.app/)にアクセスます。アカウントがない場合は作成しましょう。無料アカウントの場合、作成した作品の公開レベルがpublicとなりますが、私の場合はそれ以外特に困ることはありませんでした。

サインインすると、Explorerのページが表示され、公開されている作品を見ることができます。また、これら作品は「OPEN IN RIVE」ボタンを押せば『どのように作られているのか』を見ることもできますし、許可されていればForkもできます。

しかし数が多いので、もし参考になる作品を探したいならば、以下のチュートリアルページの動画を順にみていくことをお勧めします。動画に登場するアニメーションもExplorerページで公開されているので、あなたの最初の教科書になるはずです。
[Tutorials - Rive](https://docs.rive.app/tutorials)

ではさっそくアニメーションを作ってみましょう。

`Your Files`ボタンをクリックし、`+`アイコンをクリックし、`Flare`を選択します。
`File Properties`のページで公開範囲を選択(無料ならPublicを選択)し、`SAVE`ボタンをクリック。

するとまっさらな`Artboard`が表示されます。左上には`New File 1`との表示と、`DESIGN`/`ANIMATE`のページを切り替えるスイッチがありますね。

さて、ここからアニメーションを編集していくわけですが、その流れをざくっり説明すると、以下のようになります。

a. `DESIGN`ページで、パーツの大きさ、配置、エフェクトなどプロパティ値を編集しデザインする。これがアニメーション編集時の「初期値」になります。
b. `ANIMATION`ページで、タイムラインのフレーム毎のパーツのプロパティ値を決める。「初期値」に対する変化をつけていくイメージです。

ではまず左上の作品のタイトル`New File 1`を編集して、そうですね、`app_splash`とでもしておきましょう。
次に、アニメーションを作っていきましょう

### 1-a) アニメーションのパーツをデザインする

`DESIGN`ページではArtboardのサイズを決めたり、左上のメニューからパーツの選択動作を切り替えたり、図形を呼び出したりすることができます。なお複雑な動きを行うための`Bone`や`Node`もここから呼び出せますが、この記事ではそこまでやりません。

ではまずArtboardのサイズを決めます。画面左側の`HIERARCHY`に見えている`Artboard`を選択すると、画面右側に`Artboard`のプロパティが表示されるので、`Size`プロパティを変更してみましょう。私はxhdpiに合わせて WIDTH:720, HEIGHT:1280 としました。

次に図形を配置します。コツを少しだけまとめると、、、

* Artboard全体の拡大縮小はマウスホイール。
* Artboard全体の移動はスペースキーを押しながらドラッグ。
* 別途用意した画像を利用する場合は、画面左下のASSETSにファイルをドラッグ＆ドロップで登録してから、Artboardに配置する。

私は図形だけを使って次のようにしてみました。
![コメント 2020-08-12 132548.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109362/b3998b14-b55c-6d8a-3a00-634fc24f9c3a.png)

星の図形を黄色に塗りつぶし、その周りに`Corner Radius`プロパティで角を削った四角を、45度ごとに`Rotate`して配置したものです。
ではこれを下地にして、アニメーションを作っていきます。

### 1-b) アニメーションを作る

`ANIMATE`ページに切り替えてみましょう。画面左下に`ANIMATIONS`、画面中央下部に再生ボタンや`00:00:00`、その右には`FPS`、`00:10:00`が表示されていますね。

最初に、アニメーション全体の設定を行います。
* `ANIMATIONS`の`Untitled`を右クリックし、`Rename`を行いましょう。今回は1本のアニメーションとしてスプラッシュアニメーションを作るつもりなので、名前は`all`としました。
* 画面中央下部のパネルの右上、`FPS`の値を変更しましょう。1秒当たりのフレーム数です。初期値の60は多いかな・・・私は12にしました。
* 次にアニメーション全体の時間の長さを設定します。画面中央下部のパネルの右上、初期値は`00:10:00`(10秒と0フレーム)。私は`00:03:00`(3秒と0フレーム)にしました。書式が`MM:ss:フレーム数`である点に注意してください。

すると画面中央下部のタイムラインの時間軸が以下のように変わります。
![コメント 2020-08-12 134053.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109362/4d194126-8df0-1f48-cbfd-e5f79276e721.png)

あとはこの時間軸の各フレームごとに、パーツのプロパティ値の変化を作っていく作業になります。ただしパーツのプロパティ値は、例えば「1フレームで0.0, 5フレームで1.0」に固定すると、「2～4フレームでは0.2, 0.4, 0.6, 0.8」というようにプロパティ値が自動計算されるので、作業は「時点毎のプロパティ値を固定していく作業」の繰り返しです。「固定」するためにはプロパティ値の右にある`◇`を`◆`にします。

例えば私の「星」に対する作業の流れをまとめると・・・

0フレーム: 星のScale: 0, 0
12フレーム: 星のScale: 1.2, 1.2
14フレーム: 星のScale: 1.0, 1.0

です。しかしこれだけだとやや単調な動き・・・。そこでタイムラインの「星」の行を選択し、画面右側のプロパティの一番した`KEY INTERPOLATION`の`Type`を`Liner`から`Cubic`に変えてみましょう。するとどうでしょう・・・生き生きしましたね！(語彙力...
![コメント 2020-08-12 132548.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109362/0dd22135-0edb-18ff-40c2-cf954ff6d6b6.gif)

星の周りの四角に対するアニメーションもつけてみました。1～4直角に対して、45度の四角をやや遅れて出しています。えぇ、チュートリアルにあるデザインのパクリ（劣化版）ですね！
![コメント 2020-08-12 132549.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109362/92488bad-6643-3a90-7f4e-5a3faa6d96a8.gif)

他、`DESIGN`ページに戻って`EFFECTS`を足し、ぼかし`Blur`や影`Drop Shaddow`の変化をつけるとそれなりの見た目になっていきますが、本題から逸れるので、これくらいにしておきます。

ではこのアニメーションをFlutterで取り込める形式で保存しましょう。画面右上の出力アイコンから`Export`を選択します。`Format`はBinary。`Duration from Last Keyframe`をONにすると、最後にプロパティを固定したフレーム以降を切り捨てることができます。`EXPORT`ボタンをクリックすると、`app_splash.flr`ファイルがダウンロードされます。

では次に、Flutterの実装を行いましょう。

## 2) Navigatorによるページ遷移を実装する

Navigatorの説明は省略します。ページ遷移はmain.dartに定義します。以下のような実装になります。

```dart:main.dart
import 'package:flutter/material.dart';

import 'view/app_splash_page.dart';
import 'view/my_home_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Flare Splash-screen Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      routes: <String, WidgetBuilder>{
        '/': (_) => AppSplashPage(),
        '/home': (_) => MyHomePage(title: 'Flutter Demo'),
      },
    );
  }
}
```

* AppSplashPageは初期画面であり、アニメーションスプラッシュViewにあたる部分を指します。この後で作ります。
* MyHomePageはホーム画面を指します。ホーム画面はFlutterのサンプル実装でも構いませんが、ホーム画面からスプラッシュ画面への逆行を防ぐため、ホーム画面のルート位置にWidget`WillPopScope`を配置する必要があります。

```dart
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async => false,
      child: Scaffold(
        appBar: AppBar(
          ...(省略)...
```


## 3) 初期画面を作成し、Riveで作成した2Dアニメーション(*.flr)を組み込む。

pubspec.yamlに`flutter_flare`をインストールします。

```yaml:pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flare_flutter: ^2.0.5
```

pubspec.yamlに、Riveで作成する2Dアニメーション(*.flr)をassetsとして登録するための定義を追加します。
※ここでは/assetsディレクトリに置いたものすべてを認識する例にしています。

```yaml:pubspec.yaml
flutter:
  assets:
    - assets/
```

Riveで作成した2Dアニメーション(*.flr)を`assets/`ディレクトリに配置します。

AppSplashPageを実装します。Riveで作成した2Dアニメーション(*.flr)の組み込みは、以下のような実装になります。

```dart:view/app_splash_page.dart
import 'package:flare_flutter/flare_actor.dart';
import 'package:flutter/material.dart';

class AppSplashPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _AppSplashPageState();
}

class _AppSplashPageState extends State<AppSplashPage>{

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
        onWillPop: () async => false,
        child: Container(
          width: double.infinity,
          height: double.infinity,
          color: Colors.blueAccent,
          child: FlareActor(
          'assets/app_splash.flr',
          alignment: Alignment.center,
          fit: BoxFit.contain,
          animation: 'all',
          callback: (name) {
            Navigator.of(context).pushReplacementNamed("/home");
          },
        )
      )
    );
  }
}
```

* Riveで作成した2Dアニメーション(*.flr)は、Widget `FlareActor`を使って組み込みます。
* 引数 `animation`で、再生するアニメーション名を指定します。
* 引数 `callback`で、アニメーション終了後の処理を記述することができます。ここでは、ホーム画面への遷移を指定します。
* ホーム画面への遷移は、メソッド `pushReplacementNamed`を使用して、ホーム画面からスプラッシュ画面への逆行を防ぎます。
* Widget `WillPopScope`を指定することで、AndroidのBackボタンによって画面遷移の逆行が起きる問題を防ぎます。
* FlareActorを囲うContainerは、背景色を指定するためのものです。

ここまでを一度、デバイスで動作させてみましょう。
以下、リリースビルドして動作させた結果です。

```powershell
flutter run --release
```

![コメント 2020-08-12 132550.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109362/4bb20839-47a9-549b-43c9-a633209b55c2.gif)

スプラッシュアニメーションからホーム画面への遷移はスムーズにできましたね！
しかしスプラッシュアニメーションの前に白い画面が見えるのがダサいです。この部分が、私がローディング画面と呼んでいる部分。ネイティブスプラッシュスクリーンを設定することで差し替えましょう。

## 4) ネイティブスプラッシュスクリーンを設定する。

ネイティブスプラッシュスクリーンとして表示する画像を用意し、`assets/app_splash.png`に保存します。
私は720*1280のサイズで、中央に白い★がある透過PNGを保存しました。

次に、Flutterアプリケーションに`flutter_native_splash`プラグインを組み込みます。

```yaml:pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flare_flutter: ^2.0.5
  flutter_native_splash: ^0.1.9
```

さらに`flutter_native_splash`プラグイン用の設定を追記します。

* `image`は表示する画像です。
* `color`は背景色です。私はアニメーションに指定した背景色`blueAccent`と同じ色"448AFF"にしてみました。

```yaml:pubspec.yaml
flutter_native_splash:
  image: assets/app_splash.png
  color: "448AFF"
```

`pubspec.yaml`を保存したら、次のコマンドを実行。するとiOSとAndroid向けにスプラッシュスクリーンが設定されます。

```powershell
flutter pub get
flutter pub pub run flutter_native_splash:create
```

ではデバイスで動作させてみましょう。
以下、リリースビルドして動作させた結果です。

```powershell
flutter run --release
```

![コメント 2020-08-12 132551.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109362/b3db6a8b-0944-31e1-eef6-85e7c36427b7.gif)

ダサい白の画面がなくなって、ほら、いい感じ！（語彙力...

あと、デザインが致命的にアレな点はお許しください。。。

# 最後に

私はプログラマーですが、プログラマーでもそれなりにアニメーションが作れちゃうという点で「Riveは面白いなーっ」て思いました。Flutterはプログラミング経験の浅い人でもクロスプラットフォームに対応するアプリを短期間で作ることができる素晴らしいフレームワークだと感じているので、ぜひそこにRiveで作った2Dアニメーションも導入してもらって、より多くの人にアプリ開発ライフをより楽しんでもらえたらいいな、そんな想いでこの記事をまとめました。

この記事が1人でも多くの方の助けになれば幸いです。

---
(2020/8/13 追記)

続編の記事を公開しました。

* [【Flutter×Rive】FlutterでFlareアニメーションを連続再生する方法](https://qiita.com/kami_teru/items/fea45ba39b2dacef2456)