Flutter備忘録: SafeAreaとAppBarでiOSアプリのステータスバーの色が白くなるバグ?
Flutterはマテリアルデザインをサポートしているので、マテリアルデザインのカラースキーム(色構成)を使うと無難な見た目のアプリを比較的簡単に作る事が出来ます。
Flutterの入門的な事を扱っているネット記事だったりすると、アプリデザインのプライマリカラーを既定値の青色 (MaterialColor.blue)から他の色に変更するのに、Flutterでマテリアルデザイン対応のアプリの基礎としてコード内で使うMaterialAppのthemeプロパティに、使いたい色を指定したテーマデータ (ThemeData)を渡す方法だけが紹介されていたりする場合があったりします。
デバッグモードで開発中のアプリをいじっている時点だとそこまで気にならない事かもしれませんが、これだけだと、アプリのタイトルなどを表示するApp barの色が既定値の水色のままで表示されたり、時間やバッテリー残量が表示されるStatus barの色がおかしかったりたりする場合があります。
アプリとして一般リリースする際にアプリの動作の再確認をしていると、こういった事は絶対NGなので、MaterialAppのthemeプロパティ以外にも必要な設定をここに備忘録としてまとめておきます。
好みの色に変更する方法 (マテリアルデザイン)
Flutterのの安定版のバージョン (3.7.x) のデフォルト設定だと、右上のスクリーンショットにある様に、Status barとApp barはマテリアデザイン2の水色ので表示されます。
Status barとApp barの色を好みの色にカスタマイズするのには次の設定が必要になります。
- MaterialAppのプロパティ設定
- SystemUIOverlayStyle設定
1.MaterialAppのプロパティ
マテリアルデザイン対応のFlutterアプリを作る時に使われるMaterialAppですが、このクラスのtheme、darkThemeとcolorのプロパティを設定します。
MaterialApp( : : : theme: ThemeData( colorScheme: ColorScheme.fromSwatch( brightness: Brightness.light, primarySwatch: Colors.orange, ), ), darkTheme: ThemeData( colorScheme: ColorScheme.fromSwatch( brightness: Brightness.dark, primarySwatch: Colors.orange, ), ), themeMode: ThemeMode.system, color: Colors.orange.shade500, : : : )
themeとdarkThemeプロパティ
themeはライト(明)モード darkThemeはダーク(暗)モードで有効になる
テーマデータ (ThemeData)を設定します。
テーマデータにはマテリアルデザインの各UIで使われる色構成(色スキーム)を指定するcolorSchemeプロパティがあるので、このプロパティに好みの色構成を指定したColorSchemeクラスを渡します。
ColorSchemeクラスの数あるプロパティで色構成を細かく設定出来ますが、上の例では設定を簡単にする為にColorSchemeクラスのfromSwatchコンストラクタを使用しています。 このコンストラクタは、メインの色 (primarySwatchプロパティ)と色スキームの明暗 (britnessプロパティ)だけが必須値なので、この2つの値を指定するだけで、細かい色設定がマテリアルカラーのSwatchスキームに従って配色されます。
※: 上の例ではthemeModeプロパティも設定していますが、これはthemeとdarkThemeのプロパティのどちらを有効にするかの設定で、例ではThemeMode.systemがプロパティの値として渡されているので、システムで選択されているモードを使う設定になっています。
colorプロパティ
colorプロパティはアプリが使うプライマリ色を指定します。 上の例にある様に、themeとdarkThemeプロパティでは色の設定値にMaterialColorクラスの「Colors.orange」が渡されていますが、colorプロパティにはColorクラスの「Colors.orange.shade500」が渡されています。
themeとdarkThemeに渡されるテーマデータでは色スキーム(ColorScheme)の設定も含まれるので、プライマリカラーを再度指定する必要があるのかと思うかもしれませんが、この値は、AndroidだとDarkモードが正式に導入されたAndroid 10 (API 29)以前のパージョンのAndroidで必要になる様です。
次は、MaterialAppのcolorプロパティを設定しなかった場合と設定した場合の比較ですが、比較的古いバージョンのAndroidで最近使ったアプリのリスト(Recents Screen)を表示した場合、設定していな場合はAppBarの色が水色で表示されています。
例: Android Nougat(API 25)でcolorプロパティを設定しない場合/した場合 (エミュレーター使用)
FlutterのソースコードやIssuesリストを確認したりはしていないのであくまでも想像になりますが、すべてWidgetがマテリアルデザインのDark/Lightモードの色スキームをサポートしているという訳ではない為だと思われます。
現状ではAndroid 10以前のバージョンのAndroidもまだ多く現役で使われている様なので、公開アプリを開発するのには必要な設定でしょう。
2.SystemUIOverlayStyle設定
1の設定だけだとApp BarとStatus Barの両方が同じプライマリーカラーで表示されます。
コード内で次の例の様に、flutter/serviceライブラリのSystemChrome.setSystemUIOverlayStyleメソッドを使ってSystemUIOverlayStyleのstatusBarColorプロパティの値を設定する事でStatus barの色を変更出来ますが、現在、これはAndroidには有効ですが、iOSには未対応のようです。
コードで実行
import 'package:flutter/services.dart'; : : : SystemChrome.setSystemUIOverlayStyle( SystemUiOverlayStyle( statusBarColor: Colors.orange.shade800, ), );
AppBarのプロパティとして設定
表示画面上にAppBar (又はSliverAppBar)がある場合は、AppBarのプロパティにsystemOverlayStyle propertyがあるので、次の様に色設定を追加したSystemUIOverlayStyleクラスを渡す事でも変更可能です。
この方法だと、画面ごとで配色を変えたい場合に便利かもしれません。
AppBar( : : : systemOverlayStyle property: SystemUiOverlayStyle( statusBarColor: Colors.orange.shade800, ), : : : );
但し、このAppBarで設定する方法だと実際にAppBarが表示されるまではStatus Barの色設定が反映されない為、アプリの起動時などにはStatus Barが指定した色で表示されるまでにはラグが生じます。
なので、可能であれば、上記の「コードで実行する方法」をmain()ファンクションの初めの行で実行するのが良いと思います。
AnnotatedRegion<SystemUiOverlayStyle>のプロパティとして設定
AppBarが無い場合はAnnotatedRegionクラスを使ってSystemUiOverlayStyleの値を設定すると良いみたいです。
AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light.copyWith(statusBarColor: Colors.orange.shade800),
child: scaffold( //例
...
),
);
そもそも、AppBarのソースコードではAnnotatedRegionを使ってSystemUiOverlayStyleを設定しているそうです (👉StackOverflow「How to change status bar color using AnnotatedRegion?」のコメント)。
SafeArea + AppBarのバグ?
iOSの場合だとStatus barの色設定が出来ないみたいですが、この他に、SafeAreaの下層にAppBar(又はSliverAppBar)を子供ウィジェットとして配置してはいけないみたいです。
例:iPhone14 ProでSafeArea有り/無し (iOSシミュレーター使用)
バグなのか仕様なのかは分かりませんが、SafeAreaの下層にAppBarがある場合はStatus barが真っ白、又は真っ黒で表示されます。
そもそもSafeAreaって?
最近のiPhoneだと液晶画面の四隅が丸い他、iPhone 14 Proだと画面上部に上の「SafeArea無し + AppBar」のスクリーンショットにある様な黒抜きがあったり、iPhone 14だと画面上部に黒い出っ張り(ノッチ)があったりして、画面の表示部が長方形でないデバイスがあります。
Flutterのプログラミング上では、画面は縦横XYの座標を使って長方形の画面として表示される為、デバイスの画面が長方形でない場合は、画面がはみ出して一部表示されないとか、黒抜きやノッチの部分に重なる画面エリアでタッチ操作が出来ないなどの不都合が出ます。
SafeAreaクラスは各デバイスの画面情報を元に、画面上の非表示部分に干渉しない正方形の部分を計算してすべての画面出力が安全に表示してくれます。
(予想ですが、iPhone/iPadの表示画面の四隅が丸い為、SafeAreaがあると丁度StatusBarの部分がSafeArea外になるので、AppBarがSafeAeraの下層にある場合はAppBarによって設定されるマテリアルカラーがStatusBarに適応されないみたいです。)
コメント
コメントを投稿