Flutter製AndroidアプリできちんとSplashを設定する
Pluginのマイグレーション作業をしてたら気になって調べたのでまとめます。
情報はFlutter v1.17.5のものです。
Splash
Androidのときにまとめましたが、Flutterでも同じです。
Flutterの場合、公式にドキュメントが用意されており、Splash用のViewも使えるように提供されています。
設定できる箇所
公式で推奨されているのでそれに則ると、
- Activityに直指定するtheme(Launch Theme)
- meta-dataとして指定するView(Splash View)
- meta-dataとして指定するtheme(Normal Theme)
があります。それぞれを設定する・しないことで何が起きるかを解説します。
Activityに直指定するtheme(Launch Theme)
- AndroidManifest.xml
<activity android:name=".MyActivity" android:theme="@style/LaunchTheme" // ... >
ここで指定するthemeは起動した瞬間から、FlutterActivityでthemeを設定するまで見えるものです。
何も設定していなければ、次のようになります。
これはFlutterActivityの上にFlutterのレンダリングエンジンで描画されています。Activityのthemeの指定でToolbarを取り除いていないため、Toolbarより下の部分のみFlutterで描画されているわけです。
@android:style/Theme.Black.NoTitleBar
を親にしたthemeを設定することでToolbarを取り除き、全画面描画域にできます。
では次のようなthemeを指定してみましょう。
- res/drawable/launch_background.xml
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@android:color/holo_green_light" /> </layer-list>
- res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <item name="android:windowBackground">@drawable/launch_background</item> </style> </resources>
Toolbarの問題は解決されましたね。ちょっとパッと表示されるのが気になります。
meta-dataとして指定するView(Splash View)
- AndroidManifest.xml
<activity ... <meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/splash_background" />
- res/drawable/splash_background.xml
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@android:color/holo_orange_dark" /> <item> <bitmap android:gravity="center" android:tileMode="mirror" android:src="@mipmap/ic_launcher" /> </item> </layer-list>
ここで指定するDrawableは、FlutterActivityが起動してからFlutterのUIが描画され始めるまで表示されます。
パッと切り替わったのがなくなりました。ここで追加したViewは、アニメーションと共に除去されます。
実はここだけ、内部的にはtheme設定ではなくViewとして利用しているので、Drawableリソースで表現できるものであればだいたい利用できます。
meta-dataとして指定するtheme(Normal Theme)
- AndroidManifest.xml
<application ... <activity ... <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" />
- res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="NormalTheme" parent="@android:style/Theme.WithActionBar"> <item name="android:windowBackground">@android:color/holo_red_light</item> </style> </resources>
ここで指定するthemeはFlutterActivityが起動してからFlutterのUIが描画され始めるまで見えるものです。
この指定で、はじめに説明していた「Launch Theme」を上書きしています。わりと長い間表示されるように見えますが、「Splash View」と合わせることでほとんど見えなくなります。画面回転時などにも見えるので、FlutterのUIに設定している色に近いソリッドな背景色を使用することが公式でオススメされています。
組み合わせ
では3つとも設定した場合にどうなるのかを見てみましょう。
「Normal Theme」は全く見えなかったと思います。「Launch Theme」が一瞬見えて、「Splash View」が表示され、その間にFlutterのUIが描画され、アニメーションで消えるとともにFlutterrのUIが自然と画面に表示される流れになります。
ではちょっといい感じになるように設定してみます。
アプリの背景色をSplashと揃えてTwitterのようにアイコンを入れてみました。今回は前後しましたが、本来はアプリの背景色にSplashを揃えるのが望ましいです。
次のように設定してます。
- AndroidManifest.xml
<application ... <activity android:theme="@style/LaunchTheme" ... <meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/splash_background" /> <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> ...
- res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <!-- 同じものを使いまわしてる --> <item name="android:windowBackground">@drawable/splash_background</item> <!-- status barを透過させて色の急激な変化を抑えている。フルスクリーンにするのもあり。 --> <item name="android:windowTranslucentStatus">true</item> </style> <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> <!-- 色は適当な色 --> <item name="android:windowBackground">@color/bg</item> </style> </resources>
- res/drawable/splash_background.xml
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 色は適当な色 --> <item android:drawable="@color/bg" /> <!-- Material Design Iconsから引っ張ってきたもの --> <item android:drawable="@drawable/ic_android_white" android:gravity="center"/> </layer-list>
iOS
Androidは3つの設定がありましたが、iOSはLaunchScreenというものがあるので、そちらを変更するだけです。
Xcodeを開き、LaunchScreen.storyboardにViewが設定されています。
このViewの色を変えたり、Assetsに画像を追加してViewに乗っけたりすれば反映されます。
シミュレーターで試していて変更が反映されない場合は、シミュレーターを初期化すると反映されます。
試しにこんな感じにしてみました。
この画像はSVGです。Material Design Iconsのものだとそのままでは使えなかったのでSF Symbolsのものを利用してます。
アニメーションについて
各OSで実装することも可能ですが、どちらもOSでも起動した後にViewを操作してアニメーションしてるように見せるものです。なので、FlutterのUIとの切り替えや遅延表示などカスタムする必要が出てきます。よほどの事情がなければ、起動時のOSの差分を受け止めた後の話なのでFlutter側で実装するのがいいと思います。
Rive(旧Flare)などを使うとリッチなものになるでしょう。
まとめ
Flutterを触っていても、FlutterのUIが立ち上がるまでは各OSの世界の話になります。少しの手間で丁寧なアプリの印象を与えられるので取り組んでおいた方が良いと思います。
参考
Adding a splash screen to your mobile app - Flutter
https://developer.apple.com/documentation/xcode/creating_custom_symbol_images_for_your_app
https://developer.apple.com/forums/thread/110295
Rive - Bring your apps and games to life with real-time animation.