9
4

More than 1 year has passed since last update.

【Flutter】ステータスバーの文字色変更の挙動について

Posted at

動作環境

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),
    );
  }
}

first.gif

しかし上記のコードはScaffoldに対してAppBarが設定されていません。

Widget1(遷移元)とWidget2(遷移先)どちらかにAppBarが設定されていると、上記の_changeOverlayStyleメソッドで文字色を変更するコードは動作しません。

AppBarがないアプリはほとんどないと思うので、実用的ではなさそう。

AppBarが設定されている場合のステータスバーの文字色の挙動

AppBarが設定されている場合はデフォルトでは、AppBarの背景色によってステータスバーの文字色が決まります。

AppBarを設定すると先ほどの_changeOverlayStyleメソッドで文字色変更処理を加えても動作しません。

これはAppBarのbackgroundColorプロパティによって決定されたSystemUiOverlayStyleに上書きされてしまうからです。 1

なので基本的にはAppBarを使っていれば、ステータスバーの文字色の決定は自動的にやってくれるので気にする必要はないでしょう。

では手動で文字色を決定したい場合はどうすれば良いでしょうか?

文字色の決定の優先度

コードで文字色を設定する場合は、どのように文字色が決まるかの優先度について把握しておく必要があります。

AppBarを使ってステータスバーの文字色が決定される優先度は下記の通りです。

  1. AppBarのsystemOverlayStyleプロパティ
  2. MaterialAppに設定するAppBarThemeのsystemOverlayStyleプロパティ
  3. 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),
    );
  }
}

変わらん.png

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),
    );
  }
}

変わらん.png

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),
    );
  }
}

自動で変わるよ.gif

AppBarあり画面からなし画面に遷移する際の挙動

AppBarがある画面から、AppBarがない画面に遷移する際に文字色を変更したい場合はどうすれば良いでしょうか?

例えば下記の動画のように黒いAppBarの画面から、白色背景に遷移する際には、白い背景に合わせて、ステータスバーの文字色も変化してほしいはずです。

AppBarがある画面からない画面に遷移する際のサンプル1_AdobeCreativeCloudExpress.gif

先述した通り、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),
    );
  }
}

遷移前と遷移先で変えるよ.gif

まとめ

ステータスバーの文字色変更については下記の方針で良いかなと思います。

  • 基本的には文字色はAppBarのbackgroundColorによる自動化に任せる。
  • アプリ全体でAppBarの背景色が決まっており、自動的に決めたくない場合はAppBarThemeのsystemOverlayStyleを設定し、文字色を統一する。
  • AppBarがない画面など、別個に文字色を調整する必要がある場合はAppBarのsystemOverlayStyleプロパティを設定する。

以上です。

  1. 公式ドキュメント参考

9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4