この記事は全部俺 Advent Calendar 2018の2日目の記事です。
頑張っていきます。
この記事で説明すること
このようなSplash画面(Launch画面)を作るとき、FlutterやReactNativeではどうやって作成するのかについて記載します。
オリジナルのGopherくんは、Renée Frenchによってデザインされました。
Splash画面の種類について
Splash画面は、大きく分けて次の2種類に大別できます。
(名称はマテリアルデザインのものになります。)
- Branded launch screen
- アプリ起動ボタンが押された後、アプリが起動するまでの間に表示されるSplash画面
- Placeholder UI
- 画面内でリソース読み込みなどを行っている間に表示されるSplash画面
- Google画像検索やInstagramで画像読み込み中に表示される画面
左:Branded launch screen, 右:Placeholder UI
(material.ioより画像をお借りしました)
この記事では、Branded launch screenをクロスプラットフォーム環境で実装する方法について記載します。
クロスプラットフォーム開発におけるSplash画面の実装方針
結論から言うと、それぞれのプラットフォームごとにSplash画面を実装する必要があります。
クロスプラットフォーム開発なのにそれぞれのプラットフォームごとにSplash画面を実装しなければならない理由には、クロスプラットフォームにおける実行環境が関わっています。
FlutterやReactNativeは、AndroidとiOS上で起動する実行環境上で動作しています。
実際にアプリが起動する際は、以下のような順番で処理が行われることになります。
- アプリのアイコンがタップされ、Branded launch screenが表示される
- クロスプラットフォームの実行環境の立ち上げが行われる(FlutterならDart VM、ReactNativeなら JavaScript Runtime Environment)
- それぞれのクロスプラットフォーム上でコードが実行される
つまり、1. の時点ではクロスプラットフォーム実行環境が立ち上がっていないため、Branded launch screenを表示するコードをクロスプラットフォーム実行環境の上で記載することはできません!
(Placeholder UIについては記載することができます。)
以上の理由から、それぞれのプラットフォームごとにSplash画面を実装する必要があるのです。
注)Branded launch screenについては、読み込み時間を秒数で指定して表示することもできますが、UXを下げる原因になりうるので、この記事ではあくまでも実行環境の立ち上げまでの間に画面を表示することについて記載します。(Twitterを開く度に2秒間待たされる、という状況を考えるとうんざりしますよね?)
FlutterにおけるSplash画面の実装例
上記の理由から、Splash画面の実装はAndroidとiOS双方で行う必要があるわけですが、実装はそこまで難しくありません。
それぞれのプラットフォームで表示したい画像を用意して決められた場所に配置し、設定ファイルを書き換えることになります。
ここではFlutterを想定した実装例を記載しますが、ReactNativeなどの他プラットフォーム開発においても同様に実装すればOKです。
Android側の実装
画像ファイルの配置
下記のmipmap-xxxxディレクトリに、同名で作成したファイル(ここではlaunch_splash.png)を配置します。
styles.xmlの編集
背景色をHexカラーコードでandroid/app/src/main/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>
<color name="background">#84ffff</color>
</resources>
launch_background.xmlの編集
上記で作成・定義した画像ファイルと背景色をandroid/app/src/main/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="@color/background"/>
<item
android:width="200dp"
android:height="200dp"
android:drawable="@mipmap/launch_splash"
android:gravity="center" />
</layer-list>
buildして動作確認
buildして実行すると、以下のようなBranded launch screenが表示されます。
アプリがメモリに残っているときにアプリ履歴から起動した場合は、Branded launch screenはもちろん表示されません。
iOS側の実装
画像ファイルの配置
XCodeからRunner.xcworkspaceを開き、Assets.xassets上のLaunchImageに画像をドラッグ&ドロップで配置します。
LaunchScreen.storyboardの編集
「LaunchScreen.storyboard」→「View」→「Background」から「Custom」を選択し、Hexカラーコードで色を指定します。
必要であれば制約条件を追加します。
例えば、画面幅の0.5倍のサイズで中央に画像が表示されるように設定する方法は以下のようになります。
- ViewとLaunchImageを選択して、画面右下の制約条件から「Equal Widths」を選択して「Add 1 Constraint」
- 作成した制約条件を選び、「Multiplier」の値を「0.5」に変更
buildして動作確認
buildして実行すると、以下のようなBranded launch screenが表示されます。
アプリがメモリに残っているときにアプリ履歴から起動した場合は、Branded launch screenはもちろん表示されません。
まとめ
- Splash画面はBranded launch screenとPlaceholder UIの2種類ある
- クロスプラットフォーム開発において、Branded launch screenはネイティブ実装を行う必要がある
ハマりどころ
- iOSのLaunchScreenが更新されないとき
- キャッシュが残ってしまっている可能性があるので、simulatorをrestartすると良いです。
- Cleanだけでは直らない場合があります。
- Flutterにおいて、Android/iOSのBranded launch screenとFlutterのmain.dart画面の間に一瞬黒い画面が表示される場合
- Flutterのバージョンによるバグです。
- コマンドラインから
flutter upgrade
でアップグレードすれば直ります。
- コマンドラインから
- Branded launch screenとFlutterのmain.dartの間に、dart実行環境の立ち上げが挟まっているために起きていたようです。
- Flutterのバージョンによるバグです。