Flutter 设置Android System Navigation/Status Bar背景色

12 阅读2分钟

背景

公司要求必须使用Edge-to-Edge不允许使用Android中的theme.xmlstyle.xml资源文件来通过android:navigationBarColorandroid:statusBarColor属性进行对自定义系统Bar的背景进行改色,更不用提android.view.Window.setStatusBarColorandroid.view.Window.setNavigationBarColor方法了

如何设置背景色?在哪里设置?为什么在这里设置?我们带着这三个问题,来对这篇文章进行阅读。

如何设置背景色?

直接上代码


SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);

SystemChrome.setSystemUIOverlayStyle(
    SystemUiOverlayStyle(
        systemNavigationBarContrastEnforced: false,
        systemNavigationBarIconBrightness: Brightness.dark, // 图标颜色-> dark深色;light亮色
        systemNavigationBarColor: Colors.white, // 设置导航栏(条)背景颜色
        statusBarColor: Colors.white, // 设置状态栏背景颜色
    ),
);

关键点:

  • 开启Edge-to-Edge沉浸模式

  • systemNavigationBarContrastEnforced 设置为 false,这个参数在部分Android系统中(特别是MIUI)中是控制颜色“反转”或“强制对比”的关键,设置成false能确保你指定的颜色不被系统覆盖

在哪里设置?

  1. 使用了MaterialApp组件作为App入口,那么要在builder返回值之前进行设置:

void main() {
    WidgetsFlutterBinding.ensureInitialized();
    runApp(ProviderScope(child: const MyApp()));
}

class MyApp extends StatelessWidget {

    const MyApp({super.key});

    @override
    Widget build(BuildContext context) {
        return MaterailApp(

        builder: (context,child){
            // 在此设置
            SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
            SystemChrome.setSystemUIOverlayStyle(
                SystemUiOverlayStyle(
                    systemNavigationBarContrastEnforced: false,
                    systemNavigationBarIconBrightness: Brightness.dark,
                    systemNavigationBarColor: Colors.white,
                    statusBarColor: Colors.white,
                ),
            );
            return child!;
        });
    }
}

  1. 没有使用MaterialApp组件作为App入口,那么要在runApp()方法之前执行设置即可:

void main() {
    WidgetsFlutterBinding.ensureInitialized();

    // 在此设置
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
    SystemChrome.setSystemUIOverlayStyle(
        SystemUiOverlayStyle(
            systemNavigationBarContrastEnforced: false,
            systemNavigationBarIconBrightness: Brightness.dark,
            systemNavigationBarColor: Colors.white,
            statusBarColor: Colors.white,
        )
    );
    runApp(ProviderScope(child: const MyApp()));
}

为什么在这里设置?

重点MaterialApp组件中,无论你设置设置怎样的主题样式,都在组件内部调用一次SystemChrome.setSystemUIOverlayStyle函数,这会让你在MaterialApp组件绘制之前设置的SystemBar背景颜色均被MaterialApp的这一次函数调用覆盖。我们来看下源码


class MaterialApp extends StatefulWidget {
    ...
    @override
    State<MaterialApp> createState() => _MaterialAppState();
    ...
}

class _MaterialAppState extends State<MaterialApp> {

    @override
    Widget build(BuildContext context) {
        Widget result = _buildWidgetApp(context);
        ...
        return ScrollConfiguration(
            behavior: widget.scrollBehavior ?? const MaterialScrollBehavior(),
            child: HeroControllerScope(controller: _heroController, child: result),
        );
    }

    Widget _buildWidgetApp(BuildContext context) {
        final Color materialColor = widget.color ?? widget.theme?.primaryColor ?? Colors.blue;
        if (_usesRouter) {
            return WidgetsApp.router(
                ...,
                builder: _materialBuilder, // 重点位置
                ...,
            );
        }

        return WidgetsApp(
            ...,
            builder: _materialBuilder, // 重点位置
            ...,
        );
    }

    Widget _materialBuilder(BuildContext context, Widget? child) {
        final ThemeData theme = _themeBuilder(context); // 重点位置
        final Color effectiveSelectionColor =
        theme.textSelectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
        final Color effectiveCursorColor =
        theme.textSelectionTheme.cursorColor ?? theme.colorScheme.primary;
        Widget childWidget = child ?? const SizedBox.shrink();
        
        // 重点位置
        if (widget.builder != null) {
            childWidget = Builder(
                builder: (BuildContext context) {
                    return widget.builder!(context, child);
                },
            );
        }
        ...
    }

    ThemeData _themeBuilder(BuildContext context) {
        ThemeData? theme;
        ...
        if (useDarkTheme && highContrast && widget.highContrastDarkTheme != null) {
            theme = widget.highContrastDarkTheme;
        } else if (useDarkTheme && widget.darkTheme != null) {
            theme = widget.darkTheme;
        } else if (highContrast && widget.highContrastTheme != null) {
            theme = widget.highContrastTheme;
        }
        theme ??= widget.theme ?? ThemeData();
        // 此处的brightness 是 colorScheme.brightness 无论你设置与否都会将SystemBar 设置成light or dark
        SystemChrome.setSystemUIOverlayStyle(
            theme.brightness == Brightness.dark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark,
        ); // 重点位置
        
        return theme;
    }

}