動作環境
Flutter: 2.8.1
Dart: 2.15.1
はじめに
FlutterではSystemChrome.setSystemUIOverlayStyle
というメソッドが用意されていて、引数に指定したSystemUiOverlayStyle
に対して、statusBarBrightness
(iOS)と、statusBarIconBrightness
(Android)を指定してあげればステータスバーの文字色をBrightnessに合った色に変更することができます。
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarBrightness: Brightness.light, // for iOS
statusBarIconBrightness: Brightness.dark, // for Android
),
);
下記のコードを実行すると、iOSとAndroidそれぞれステータスバーの文字色が変化しているのが確認できます。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// プッシュで遷移する
Future _push(BuildContext context, Widget nextScreen, {bool animated = true}) {
Route route = animated
? MaterialPageRoute(builder: (context) => nextScreen)
: PageRouteBuilder(
pageBuilder: (context, animation1, animation2) => nextScreen,
transitionDuration: const Duration(seconds: 0),
);
return Navigator.of(context).push(route);
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Widget1(),
);
}
}
class Widget1 extends StatelessWidget {
_changeOverlayStyle({
required bool isLight,
}) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarBrightness:
isLight ? Brightness.light : Brightness.dark, // for iOS
statusBarIconBrightness:
isLight ? Brightness.dark : Brightness.light, // for Android
),
);
}
@override
Widget build(BuildContext context) {
// ステータスバーをライトモードにする
_changeOverlayStyle(isLight: true);
return Scaffold(
body: Container(color: Colors.red),
floatingActionButton: FloatingActionButton(
onPressed: () async {
// ステータスバーをダークモードにする
_changeOverlayStyle(isLight: false);
// 画面遷移処理
await _push(context, Widget2());
// ステータスバーをライトモードに戻す
_changeOverlayStyle(isLight: true);
},
child: const Icon(Icons.add),
),
);
}
}
class Widget2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(color: Colors.orange),
);
}
}
しかし上記のコードはScaffoldに対してAppBarが設定されていません。
Widget1
(遷移元)とWidget2
(遷移先)どちらかにAppBarが設定されていると、上記の_changeOverlayStyle
メソッドで文字色を変更するコードは動作しません。
AppBarがないアプリはほとんどないと思うので、実用的ではなさそう。
AppBarが設定されている場合のステータスバーの文字色の挙動
AppBarが設定されている場合はデフォルトでは、AppBarの背景色によってステータスバーの文字色が決まります。
AppBarを設定すると先ほどの_changeOverlayStyleメソッドで文字色変更処理を加えても動作しません。
これはAppBarのbackgroundColor
プロパティによって決定されたSystemUiOverlayStyleに上書きされてしまうからです。 1
なので基本的にはAppBarを使っていれば、ステータスバーの文字色の決定は自動的にやってくれるので気にする必要はないでしょう。
では手動で文字色を決定したい場合はどうすれば良いでしょうか?
文字色の決定の優先度
コードで文字色を設定する場合は、どのように文字色が決まるかの優先度について把握しておく必要があります。
AppBarを使ってステータスバーの文字色が決定される優先度は下記の通りです。
- AppBarの
systemOverlayStyle
プロパティ - MaterialAppに設定するAppBarThemeの
systemOverlayStyle
プロパティ - AppBarの
backgroundColor
プロパティ
1が設定されていれば1によって文字色が決まり、nullの場合は2によって決まる、といった具合です。
AppBarのsystemOverlayStyleプロパティ
一番優先度の高いプロパティです。
AppBar別に設定できるので画面ごとに文字色を変えたい場合に有効です。
下記のコードはAppBarの背景色を黒色にしているにも関わらず、ステータスバーの文字色も黒くなります。
class Widget1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarBrightness: Brightness.light, // for iOS
statusBarIconBrightness: Brightness.dark, // for Android
),
),
body: Container(color: Colors.white),
);
}
}
MaterialAppに設定するAppBarThemeのsystemOverlayStyle
プロパティ
二番目に優先度が高いプロパティです。
AppBar全体に適応できるので、AppBarごとに設定しなくても良くなります。
下記のコードもAppBarの背景色を黒色にしているにも関わらず、ステータスバーの文字色も黒くなります。
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Widget1(),
theme: ThemeData(
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarBrightness: Brightness.light, // for iOS
statusBarIconBrightness: Brightness.dark, // for Android
),
)),
);
}
}
class Widget1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
),
body: Container(color: Colors.white),
);
}
}
AppBarのbackgroundColor
一番優先度が低いプロパティです。
文字色を設定する処理を書かなくても良いので一番楽です。
class Widget1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
),
body: Container(color: Colors.white),
floatingActionButton: FloatingActionButton(
onPressed: () {
_push(context, Widget2());
},
),
);
}
}
class Widget2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: Colors.white),
body: Container(color: Colors.white),
);
}
}
AppBarあり画面からなし画面に遷移する際の挙動
AppBarがある画面から、AppBarがない画面に遷移する際に文字色を変更したい場合はどうすれば良いでしょうか?
例えば下記の動画のように黒いAppBarの画面から、白色背景に遷移する際には、白い背景に合わせて、ステータスバーの文字色も変化してほしいはずです。
先述した通り、AppBarがある画面ではbackgroundColor
によって自動的に文字色を変えてくれますが、AppBarがない画面のことは考えてくれません。
AppBarがない画面でSystemChrome.setSystemUIOverlayStyle
を呼んであげれば良いでしょうか?
残念ながらそれはうまく動作しません。
なぜならAppBarのインスタンスは保持されたままなので、別画面で呼んであげてもAppBarが自動的にSystemUiOverlayStyle
を変更するという動作は変わらないからです。
この場合は、下記のコードのようにAppBarがある画面のsystemOverlayStyle
プロパティを、遷移前と遷移後で変更することで解決できます。
class _Widget1State extends State<Widget1> {
bool isLight = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarBrightness: isLight ? Brightness.light : Brightness.dark,
statusBarIconBrightness: isLight ? Brightness.dark : Brightness.light,
),
),
body: Container(color: Colors.white),
floatingActionButton: FloatingActionButton(
onPressed: () async {
// 遷移前に文字色変更
setState(() {
isLight = !isLight;
});
// 遷移後に元に戻す
await _push(context, Widget2());
setState(() {
isLight = !isLight;
});
},
),
);
}
}
class Widget2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(color: Colors.white),
);
}
}
まとめ
ステータスバーの文字色変更については下記の方針で良いかなと思います。
- 基本的には文字色はAppBarの
backgroundColor
による自動化に任せる。 - アプリ全体でAppBarの背景色が決まっており、自動的に決めたくない場合はAppBarThemeの
systemOverlayStyle
を設定し、文字色を統一する。 - AppBarがない画面など、別個に文字色を調整する必要がある場合はAppBarの
systemOverlayStyle
プロパティを設定する。
以上です。