arcanum_jp’s blog

おっさんの日記

flutter でスプラッシュウインドウを使う(flutter_native_splash)

iOS側だとスプラッシュウインドウは必須みたいなのでこういうのははじめから入れておかないと後で辛くなるのでちょい調べた。未来にまたアプリを作る際の作業メモ。

イチから入れる方法は公式から出てるけど、スプラッシュウインドウのパッケージがあるみたいね。

pub.dev


環境は以下で確認してます。

% flutter --version
Flutter 2.8.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 77d935af4d (9 days ago) • 2021-12-16 08:37:33 -0800
Engine • revision 890a5fca2e
Tools • Dart 2.15.1

pubspec.ymlに以下を入れてpub getします。最新バージョンは前述のページで確認してください。2022/01現在、1.3.3。

dev_dependencies:
  flutter_native_splash: ^1.3.3

flutter_native_splash.yaml(スプラッシュウインドウの設定)

スプラッシュウインドウに表示する画像にこんなものを用意してみました。400*400ピクセルpngファイルです。これを/assets/splash/splashicon.pngに保存しました。

f:id:arcanum_jp:20220122111524p:plain


これをアプリが使えるようにpubspec.ymlに登録します。

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/splash/splashicon.png

各種設定はflutter_native_splash.yamlをプロジェクトのルートに作成し色々記載していきます。とりあえず以下の様な内容にしてみました。

flutter_native_splash:
  image: assets/splash/splashicon.png

設定した後以下のコマンドを打つと完了です。

flutter pub run flutter_native_splash:create

以下はコマンドのログです。

% flutter pub run flutter_native_splash:create
[Android] Creating branding images
[Android] Updating launch background(s) with splash image path...
[Android]    - android/app/src/main/res/drawable/launch_background.xml
[Android]    - android/app/src/main/res/drawable-v21/launch_background.xml
[Android] Updating styles...
[Android]    - android/app/src/main/res/values/styles.xml
[iOS] Creating  images
[iOS] Updating LaunchScreen.storyboard with width, and height
[iOS] updating constraints with splash branding
[iOS] Updating ios/Runner/Info.plist for status bar hidden/visible
Web folder not found, skipping web splash update...

Native splash complete. 👍
Now go finish building something awesome! 💪 You rock! 🤘🤩

flutter_native_splash.yaml には以下のようなオプションがあります。

color/ background_image

背景色か背景画像を指定します。いずれかの指定のみです。

  color: "#42a5f5"
  # background_image: xxxxxx
image

スプラッシュウインドウに表示するアイコンです。中央に表示されます。フォーマットはpngオンリーでsized for 4x pixel densityだそうです。sized for 4x pixel densityが分からん・・・

branding/ branding_dark / branding_mode

企業ロゴみたいなものです。例えばimageで中央にアプリのアイコンを大きく表示して画面下に企業ロゴを表示するとか・・・

brandingにimageオプションと同様にイメージファイルへのパスを記載します。branding_darkはiOSのダークモード時に表示するイメージファイルです。いずれも今はimageオプションと同様にpngフォーマットのみ対応しています。

イメージファイルの場所はbranding_modeで指定します。bottom、bottomRight、bottomLeftが指定できます。(指定しないとbottom)
この値はAndroidandroid_gravityとiOSios_content_modeとは無関係です。

  branding: assets/brand_dark.png
  branding_dark: assets/brand_dark.png
  branding_mode: bottom
color_dark/ background_image_dark/ image_dark: assets/splash-invert.png

上記color/ background_image、imageで書いたものがスプラッシュウインドウを表示する際に端末がダークモードの場合こちらの値になります。

android/ ios/ web

スプラッシュウインドウを生成するかどうかです。AndroidはたしかGooglePlayでリリースする際スプラッシュウインドウは必須ではないためこれをfalseにするなどでしょうか。

  android: false
  ios: true
  web: false 
android_gravity/ ios_content_mode/ web_image_mode

imageで設定した画像をどこに表示するか?を指定します。

android_gravityはbottom, center, center_horizontal, center_vertical, clip_horizontal, clip_vertical, end, fill, fill_horizontal, fill_vertical, left, right, start, or top から指定しますが以下のGravityの値に準拠します。
Gravity  |  Android Developers

ios_content_modeはscaleToFill, scaleAspectFit, scaleAspectFill, center, top, bottom, left, right, topLeft, topRight, bottomLeft, or bottomRightから指定しますが、以下のiOSのUIView.ContentModeに準拠します。
Apple Developer Documentation

web_image_modeはcenter, contain, stretch, and coverから指定します。特に指定しないと画面中央に表示されるようですね。

fullscreen

trueにすると画面上部にある通知バーも含むフルスクリーンで表示します。

info_plist_files

info.plistの名前を変更した場合、そのファイル名を指定します。iOS系の開発に疎いのでこのファイルを変更できるってことかな?

  info_plist_files:
    - 'ios/Runner/Info-Debug.plist'
    - 'ios/Runner/Info-Release.plist'

flutter_native_splash.yamlを使用しないでの設定

MyApp#build()の変更

readmeページだとflutter_native_splash.yamlに設定を書いて云々とありますが、installページではpubspec.ymlに依存関係書いてあとはパッケージインポートでExampleページではflutter_native_splash.yamlは特に関係ない感じで書いてあります。

flutter_native_splash.yamlで全部設定しても良いですし、コード上で書いてもいいという事でしょうか。例えば自由にスプラッシュウインドウのレイアウトを組んで表示する、などが考えられます。

Flutterプロジェクト作成直後のmain.dartはMyApp#build()でStatefullWidgetのMyHomePageインスタンスを返すようになっていますが、ここでローディング中ならスプラッシュ用のStatelessWidgetを返すように変更します。

以下はFlutterプロジェクト作成直後のmain.dartのMyAppとbuild()とスプラッシュ対応版の比較です。作成直後のMyApp#build()では単純にMaterialAppを返していますが、スプラッシュ対応版はローディング中ならsplash()メソッドを呼び出しスプラッシュ用のStatelessWidgetを返す様になっています。
プロジェクト作成直後ならMyApp#build()全体をコピペでいいみたいです。

import 'package:flutter_native_splash/flutter_native_splash.dart';

...省略
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: Init.instance.initialize(),
      builder: (context, AsyncSnapshot snapshot) {
        // Show splash screen while waiting for app resources to load:
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const MaterialApp(home: Splash());
        } else {
          // Loading is done, return the app:
          return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: const MyHomePage(title: 'Flutter Demo Home Page'),
          );
        }
      },
    );
  }
}||<

*** 実際のSplashメソッド

以下はExampleページから引っ張ってきたコードですが、Splashウインドウのウィジェット定義部分です。あとはmain.dartの下にこれをコピペします。
自由にレイアウトを組む場合はこのSplash#build()で自由なWidgetを返すようにします。

>||
class Splash extends StatelessWidget {
  const Splash({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    bool lightMode =
        MediaQuery.of(context).platformBrightness == Brightness.light;
    return Scaffold(
      backgroundColor:
          lightMode ? const Color(0xffe1f5fe) : const Color(0xff042a49),
      body: Center(
          child: lightMode
              ? Image.asset('assets/splash.png')
              : Image.asset('assets/splash_dark.png')),
    );
  }
}

class Init {
  Init._();
  static final instance = Init._();

  Future initialize() async {
    // This is where you can initialize the resources needed by your app while
    // the splash screen is displayed.  Remove the following example because
    // delaying the user experience is a bad design practice!
    await Future.delayed(const Duration(seconds: 3));
  }
}

flutter pub run flutter_native_splash:createの結果

以下は色々とオプションをガチャガチャ変更してflutter pub runした結果ですがプロジェクト内に色々ファイルが作られたり更新されたりするようですね。という情報でした。

	new file:   android/app/src/main/res/drawable-hdpi/branding.png
	new file:   android/app/src/main/res/drawable-hdpi/splash.png
	new file:   android/app/src/main/res/drawable-mdpi/branding.png
	new file:   android/app/src/main/res/drawable-mdpi/splash.png
	new file:   android/app/src/main/res/drawable-v21/background.png
	modified:   android/app/src/main/res/drawable-v21/launch_background.xml
	new file:   android/app/src/main/res/drawable-xhdpi/branding.png
	new file:   android/app/src/main/res/drawable-xhdpi/splash.png
	new file:   android/app/src/main/res/drawable-xxhdpi/branding.png
	new file:   android/app/src/main/res/drawable-xxhdpi/splash.png
	new file:   android/app/src/main/res/drawable-xxxhdpi/branding.png
	new file:   android/app/src/main/res/drawable-xxxhdpi/splash.png
	new file:   android/app/src/main/res/drawable/background.png
	modified:   android/app/src/main/res/drawable/launch_background.xml
	modified:   android/app/src/main/res/values/styles.xml
	new file:   ios/Runner/Assets.xcassets/BrandingImage.imageset/Contents.json
	new file:   ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
	new file:   ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
	modified:   ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
	modified:   ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
	modified:   ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
	modified:   ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
	modified:   ios/Runner/Base.lproj/LaunchScreen.storyboard
	modified:   ios/Runner/Info.plist