如何在Flutter中更改状态栏和应用程序栏的颜色?

43

我想将系统状态栏的颜色更改为黑色。配置似乎被AppBar类覆盖了。我可以通过在创建Material App时为主题分配ThemeData.dark(),然后指定一个appBar属性来实现我想要的效果。但我不想要AppBar,并且这样做会改变所有字体颜色。

一个可能的解决方案是继承ThemeData.bright()到一个新类中,然后添加一些仅通过更改系统状态栏的内容。

setSystemUIOverlayStyle

那我需要指定AppBar并使其不可见吗?

文档

main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:english_words/english_words.dart';
import 'layout_widgets.dart' as layout_widgets;

class RandomWords extends StatefulWidget {
  @override
  createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _saved = new Set<WordPair>();
  final _biggerFont = const TextStyle(fontSize: 18.0);

  void _pushSaved() {
     Navigator.of(context).push(
       new MaterialPageRoute(
           builder: (context) {
             final tiles = _saved.map((pair) {
               return new ListTile(
                 title: new Text(pair.asPascalCase,style:_biggerFont)
               );
              }
             );
             final divided = ListTile.divideTiles(
               context:context,
                 tiles: tiles,).toList();
             return new Scaffold(
               appBar: new AppBar(
                 title: new Text('Saved Suggestions'),
               ),
               body: new ListView(children:divided),
             );
           }
       )
     );
  }

  Widget _buildSuggestions() {
    return new ListView.builder(
      padding: const EdgeInsets.all(16.0),
      // The item builder callback is called once per suggested word pairing,
      // and places each suggestion into a ListTile row.
      // For even rows, the function adds a ListTile row for the word pairing.
      // For odd rows, the function adds a Divider widget to visually
      // separate the entries. Note that the divider may be difficult
      // to see on smaller devices.
      itemBuilder: (context, i) {
        // Add a one-pixel-high divider widget before each row in theListView.
        if (i.isOdd) return new Divider();
        // The syntax "i ~/ 2" divides i by 2 and returns an integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings in the ListView,
        // minus the divider widgets.
        final index = i ~/ 2;
        // If you've reached the end of the available word pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

  Widget _buildRow(WordPair pair) {
    final alreadySaved = _saved.contains(pair);
    return new ListTile(
      title: new Text(
          pair.asPascalCase,
        style: _biggerFont,
      ),
      trailing: new Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
      onTap: () {
        setState(() {
          if (alreadySaved) {
            _saved.remove(pair);
          } else {
            _saved.add(pair);
          }
        });
      },
    );
  }


  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Startup Name Generator'),
        actions: <Widget>[
          new IconButton(icon:new Icon(Icons.list), onPressed: _pushSaved),
        ],
      ),
      body: _buildSuggestions(),
    );
  }

}


void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Column buildButtonColumn(IconData icon, String label) {
      Color color = Theme.of(context).primaryColor;
      return new Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new Icon(icon, color: color),
          new Container(
            margin: const EdgeInsets.only(top:8.0),
            child: new Text(
              label,
              style: new TextStyle(
                fontSize: 12.0,
                fontWeight: FontWeight.w400,
                color: color,
              )
            ),
          )
        ],

      );
    }
    Widget titleSection = layout_widgets.titleSection;
    Widget buttonSection = new Container(
      child: new Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          buildButtonColumn(Icons.contact_mail, "CONTACT"),
          buildButtonColumn(Icons.folder_special, "PORTFOLIO"),
          buildButtonColumn(Icons.picture_as_pdf, "BROCHURE"),
          buildButtonColumn(Icons.share, "SHARE"),
        ],
      )
    );
    Widget textSection = new Container(
      padding: const EdgeInsets.all(32.0),
      child: new Text(
        '''
The most awesome apps done here.
        ''',
        softWrap: true,
      ),
    );
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
    return new MaterialApp(
      title: 'Startup Name Generator',
//      theme: new ThemeData(
//          brightness: Brightness.dark,
//          primarySwatch: Colors.blue,
//      ),
//      theme: new ThemeData(),
      debugShowCheckedModeBanner: false,

      home: new Scaffold(
//        appBar: new AppBar(
////          title: new Text('Top Lakes'),
////          brightness: Brightness.light,
//        ),
//        backgroundColor: Colors.white,
        body: new ListView(
          children: [
            new Padding(
              padding: new EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 0.0),
              child: new Image.asset(
                  'images/lacoder-logo.png',
                  width: 600.0,
                  height: 240.0,
                  fit: BoxFit.fitHeight,

              ),
            ),

            titleSection,
            buttonSection,
            textSection,
          ],
        ),
      ),
    );
  }
}

布局控件.dart

import 'package:flutter/material.dart';

Widget titleSection = new Container(
    padding: const EdgeInsets.all(32.0),
    child: new Row(children: [
      new Expanded(
          child: new Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          new Container(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: new Text(
                "Some-Website.com",
                style: new TextStyle(
                  fontWeight: FontWeight.bold,
                ),
              )
          ),
          new Text(
            'Small details',
            style: new TextStyle(
              color: Colors.grey[500],
            )
          )
        ],
      )),
      new Icon(Icons.star,color: Colors.orange[700]),
      new Text('100'),
    ]));

你说你不想要一个应用栏,但是你的代码明显包含了一个。当你说你不想要一个应用栏时,你的意思是什么? - areiser
我想表达的是我不需要一个应用栏,但这并不重要。我试图做的是让系统状态栏变暗,仅此而已。我的代码中有应用栏,因为我正在进行实验和尝试不同的东西。 - 8oh8
这个回答解决了你的问题吗?Flutter - 如何在没有AppBar的情况下设置状态栏颜色 - Hannes Tiltmann
嘿,看看这个:https://dev59.com/Erbna4cB1Zd3GeqPgcIV#59949734 - naman kashyap
10个回答

32

我尝试了SystemChrome.setSystemUIOverlayStyle()方法,在测试过程中(使用Flutter SDK v1.9.1+hotfix.2,运行在iOS 12.1上),它对Android非常有效。但是对于iOS,例如,如果你的第一个屏幕FirstScreen()没有一个AppBar,但第二个SecondScreen()有,那么在启动时该方法确实设置了FirstScreen()中的颜色。然而,在从SecondScreen()返回到FirstScreen()后,状态栏颜色变为透明。

我想到了一个hacky解决方法,通过设置高度为零的AppBar(),然后状态栏的颜色会被AppBar改变,但是AppBar本身是不可见的。希望对某些人有用。

// FirstScreen that doesn't need an AppBar
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: PreferredSize(
        preferredSize: Size.fromHeight(0),
        child: AppBar( // Here we create one to set status bar color
          backgroundColor: Colors.black, // Set any color of status bar you want; or it defaults to your theme's primary color
        )
      )
  );
}

// SecondScreen that does have an AppBar
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar()
  }
}

这是iPhone Xs Max iOS 12.1中FirstScreen的屏幕截图:

enter image description here


嗨@8oh8,截图已上传。干杯 - Ludy Su
但是对于Android呢?那是我们的目标平台。iOS上的系统栏默认不是黑色吗? - 8oh8
如果状态栏颜色和应用栏颜色不同怎么办? - Nabin Dhakal
1
@Nbn 可以用另一种巧妙的方法实现。您可以将具有不同颜色背景的“容器”设置为AppBar的“操作”。但是,您需要注意容器内按钮/标题的布局:appBar: AppBar( backgroundColor: Colors.black, actions: [ Container( width: MediaQuery.of(context).size.width, color: Colors.blue, ),],), - Ludy Su
这甚至不应该被允许。有一种正常的方法可以实现这个。 - Dennis Barzanoff
显示剩余2条评论

32

更新:

Scaffold(
  appBar: AppBar(
    systemOverlayStyle: SystemUiOverlayStyle(
      systemNavigationBarColor: Colors.blue, // Navigation bar
      statusBarColor: Colors.pink, // Status bar
    ),
  ),
)

旧方案(仍然适用)

iOS 和 Android 均适用:

appBar: AppBar(
  backgroundColor: Colors.red, // status bar and navigation bar color
  brightness: Brightness.light, // status bar brightness
)

仅适用于Android(更灵活)

您可以使用SystemChrome类来更改状态栏和导航栏颜色。 首先导入。

import 'package:flutter/services.dart';
在此之后,您需要添加以下几行代码(最好将这些代码放在您的main()方法中)。
void main() {
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
    systemNavigationBarColor: Colors.blue,
    statusBarColor: Colors.pink,
  ));
}


如果您想为应用程序中的状态栏和导航栏设置整个应用程序范围的颜色,则此答案是不错的选择。对于适当的屏幕,您只需要编写几行代码来显示和隐藏UI组件,例如状态栏。 - Purvik Rana
通过AppBarTheme设置systemNavigationBarColor无效。 - Jeremi
@Jeremi 这就是为什么我也提供了 SystemChrome.setSystemUIOverlayStyle 的解决方案。 - CopsOnRoad

14

如果您完全不需要AppBar,则可以在主函数中调用setSystemUIOverlayStyle

void main() async {
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);

  runApp(new MaterialApp(
    home: new Scaffold(),
  ));
}

如果一个 scaffold 中有应用栏而另一个没有,则会更加棘手。在这种情况下,我必须在推送新路由并使用没有应用程序栏的 scaffold 后调用 setSystemUIOverlayStyle

@override
Widget build(BuildContext context) {
  final page = ModalRoute.of(context);
  page.didPush().then((x) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
  });

  return new Scaffold();
}

1
我想让系统栏变成黑色。所以这个方法不起作用。 - 8oh8
1
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);系统颜色设置为暗色。 - Raouf Rahiche
这样做不起作用,因为它会改变应用程序中其他所有内容的颜色。 - 8oh8
好的,抱歉,我以为你是在使用Theme.dark。我尝试了SystemUiOverlay.dark,但似乎不起作用。我已经将我的代码添加到问题中。我还尝试使用带有“async”关键字的main()函数,但没有结果。 - 8oh8
当您同时使用Navigator时,这种方法无法正常工作。请改用AnnotatedRegion - Dennis Barzanoff
显示剩余3条评论

6
TLDR:你需要使用Scaffold,它可以管理颜色,即使你来回导航。
Scaffold(
    appBar: AppBar(brightness: Brightness.dark), \\ dark content -> white app bar
    body: ...
);

如果您的屏幕没有应用程序栏,则需要使用AnnotatedRegion 脚手架一起使用,以实现相同的效果。
AnnotatedRegion(
    value: SystemUiOverlayStyle.light, // this will make the app bar white
    child: Scaffold(
          body: 
    ),
);

你可以定制SystemUiOverlayStyle.light以外的样式:

SystemUiOverlayStyle(
  statusBarBrightness: Brightness.light, 
  systemNavigationBarDividerColor: Colors.blue,
  ...
);

3
我已经实现了这种方式。
  @override
void initState() {
  super.initState();
  // Transparent status bar
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(statusBarColor: Colors.transparent,));
}

在逗号之后还可以看到不同的属性。

SystemUiOverlayStyle(statusBarColor: Colors.transparent,)

只需在逗号后使用ctrl + space的组合,您就能得到可以使用的内容。

输入图像描述


2

我对StackOverflow还比较陌生,也从未使用过Flutter,但我找到了this这个包,似乎可以让事情变得相对容易。

方法1:使用该包

导入该包后,您只需要添加以下代码片段:

try {
await FlutterStatusbarcolor.setStatusBarColor(Colors.black);
} on PlatformException catch (e) {
print(e);
}

替换setStatusBarColor()的参数应该会给您想要的结果,可以在这里找到完整的颜色列表。

方法2:使用默认函数

如果这种方法不起作用/您不想添加额外的包或库,那么也许 StackOverflow答案可以帮助您。

它涉及使用与上述方法类似的函数:getWindow().setStatusBarColor()getActivity().getWindow().setStatusBarColor()

将参数替换为来自早前相同列表的所需十六进制代码,也可能会得到解决方案。

希望能有所帮助!


2

只需将此代码写在AppBar内部即可

Scaffold(
          drawer: const SideBar(),
          appBar: AppBar(
            title: const Text("SomeThing"),
            systemOverlayStyle: SystemUiOverlayStyle.dark,//this is what you wanted
          ),
          body: YourWidget()

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

2
请阅读这个flutter包。为了将状态栏文字设置为黑色,您可以设置FlutterStatusbarcolor.setStatusBarWhiteForeground(false)。您需要在didChangeAppLifecycleState方法中加入恢复状态的代码行,以便当您去其他应用程序并回来时,状态栏文本颜色会设置为您的初始设置。
另外,您需要设置AppBar的TextTheme,如下所示。
Widget build(BuildContext context) {
FlutterStatusbarcolor.setStatusBarWhiteForeground(false);
return MaterialApp(
    title:// title goes here
    theme:// your theme goes here
    home:   Scaffold(
      appBar: AppBar(
                      backgroundColor: Colors.white,
                      title: _loadAppBarTitle(),
                      textTheme: Theme.of(context).textTheme),
      body: //body's goes here
  );
);

希望这篇文章能帮助有类似问题的人。


1
由于某些原因,当我直接将我的AppBar放在Scaffold中时,它对我不起作用,所以我像这样添加它:
Scaffold(
  extendBodyBehindAppBar: true,
  body: Stack (
    children: [
      AppBar(
        automaticallyImplyLeading: false,
        backgroundColor: Colors.transparent,
        systemOverlayStyle: SystemUiOverlayStyle.light,
      ),
      // ... other widgets
    ],
  ),
)

它还改变了我的状态栏和导航栏的颜色(Android中主页指示器的背景)


这是因为您的应用栏需要在脚手架的应用栏字段中。 - martinseal1987
它实际上并不像这样工作,这就是为什么我把它放在了下面的栈中。我希望它能对你们中的一些人有所帮助。 - Jack'

1

Flutter 2.5.1

'brightness'已经被弃用,不应再使用。该属性不再使用,请改用systemOverlayStyle代替。此功能在v2.4.0-0.0.pre之后被弃用。尝试使用新的代码替换旧的代码。

旧代码

brightness: Brightness.dark,

新代码

systemOverlayStyle: SystemUiOverlayStyle(
 systemNavigationBarColor: Colors.blue, // Navigation bar
 statusBarColor: Colors.red, // Status bar
),

我遇到了这个错误 -> 命名参数“systemOverlayStyle”未定义。请尝试更正名称为现有命名参数的名称,或使用名称“systemOverlayStyle”定义一个命名参数。 - Roopak
请检查您的Flutter版本。 - Vivek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接