androidandroid-jetpack-composeandroid-theme

Flickering Statusbar in Edge to Edge mode


I've just setup Edge2Edge mode on a new Android project. Generally that works fine, however on app startup there's a noticable moment where the status bar is not visible at all.

  1. status bar is displaying fine while app logo is shown:

  1. then for some time the status bar is not or not really shown:

  1. finally the status bar is shown:

I have two issues here:

a) The status bar is "flickering" as it's first shown, then not shown, then shown again.

b) The status bar color is close to the white app background, but not completely white.

This is how MainActivity looks like:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        enableEdgeToEdge()


        setContent {
            NewToGetThemesTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize().safeDrawingPadding(),
                    color = MaterialTheme.colorScheme.background,

                ) {
                    Greeting("Android")
                }
            }
        }
    }
}

Theme.kt looks like this (note the commented out side effect):

private val DarkColorScheme = darkColorScheme(
    primary = Purple80,
    secondary = PurpleGrey80,
    tertiary = Pink80
)

private val LightColorScheme = lightColorScheme(
    primary = Purple40,
    secondary = PurpleGrey40,
    tertiary = Pink40

    /* Other default colors to override
    background = Color(0xFFFFFBFE),
    surface = Color(0xFFFFFBFE),
    onPrimary = Color.White,
    onSecondary = Color.White,
    onTertiary = Color.White,
    onBackground = Color(0xFF1C1B1F),
    onSurface = Color(0xFF1C1B1F),
    */
)

@Composable
fun NewToGetThemesTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            val window = (view.context as Activity).window
            // I've commented out the next lines as they made everything worse
            //window.statusBarColor = colorScheme.primary.toArgb()
            //WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
        }
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = content
    )
}

themes.xml looks like this:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="Theme.NewToGetThemes" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

Solution

  • What you see is due to the Activity theming applied through the manifest. If you remove the enableEdgeToEdge() call, you can see how your app would look like with the XML theme applied:

    enter image description here

    As you can see, the status bar content is white because its background is dark.

    Now, if you have the enableEdgeToEdge() call, it internally sets the background color of the status bar to transparent and the content color is set to dark. What you don't see is that before the content color can be applied, the XML values take over thinking it should be a white content color due to the dark background. So the "flickering" happens because of the content first being set to light, then to dark, in the following order:

    1. app opened - splash screen shown, which has its own rules for the status bar with dark content
    2. XML theme gets applied - status bar content turns light (white)
    3. enableEdgeToEdge() called - status bar content turns dark

    If this bothers you much, it can be easily fixed by setting the android:windowLightStatusBar value to true in the theme:

    <style name="Theme.NewToGetThemes" parent="android:Theme.Material.Light.NoActionBar" />
        <item name="android:windowLightStatusBar">true</item>
    </style>
    

    This tells the system that the status bar background is light colored, which means its content should be dark.

    Note that if you plan on using a dark theme as well (highly recommended), you'll need to create the appropriate version of this theme in the values-night folder with its parent set to android:Theme.Material.NoActionBar (note the lack of Light), and the android:windowLightStatusBar value to false.


    As for the second issue of "The status bar color is close to the white app background, but not completely white.": it's actually your background which is likely off-white. If you check out your Theme.kt file, you'll see that only a very few parameters of the theme are set, and there's the background one commented out, which probably shows the default whitish background color (#FFFBFE):

    background = Color(0xFFFFFBFE)
    

    Though you haven't asked about it, I'd recommend either using a Scaffold as the base instead of a Surface or setting the safeDrawingPadding() modifier on the internal components of the Surface, otherwise the background behind the status bar will be the default (XML) theme one, while the app background will come from the compose theme.

    The Scaffold internally manages window insets so it spans the entire window. Besides that, it already applies the theme background color to the container so you don't need to set it manually.