23

I am following https://developer.android.com/jetpack/compose/tutorial

and the code

@Preview(name = "Light Mode")
@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_UNDEFINED, showBackground = true)
@Preview(name = "Full Preview", showSystemUi = true)
@Composable
fun DefaultPreview() {
    ComposeTutorialTheme {
        MessageCard(Message("Roman", "message body"))
    }
}

enter image description here

rofrol
  • 14,438
  • 7
  • 79
  • 77
  • My team and I have done it manually with a parameter in our base theme which works pretty well for us. – Sebastian Rieger Jul 06 '22 at 09:32
  • 1
    I've checked the tutorial. They use Configuration.UI_MODE_NIGHT_YES not Configuration.UI_MODE_NIGHT_UNDEFINED like you do. Maybe that's the problem – Sebastian Rieger Jul 06 '22 at 09:34
  • 1
    It is harder to notice, but that step of the tutorial also adds a `Surface` in the preview (and does not mention it), as a parent for `MessageCard`. Using this technique you can also remove `showBackground = true`. – Andrei Tudor Diaconu Nov 07 '22 at 15:00

2 Answers2

32

It "works" in the tutorial because they use a Card which uses a Surface under the hood.

The dark background you are seeing is not produced by the @Preview annotation, but rather by the Surface which simply draws its own background based on the MaterialTheme.

Barebones implementation of Surface:

fun Surface(
    color: Color = MaterialTheme.colorScheme.surface,
) {
    Box(
        modifier = modifier.surface(
            backgroundColor = color
        ),
    ) {
        ...
    }
}

Surface also overrides the "content color" with

CompositionLocalProvider(
    LocalContentColor provides contentColorFor(color),
) {
    content()
}

so that the text or icons displayed within the Surface contrast well with the background color.


You still have to set up the MaterialTheme to follow the dark theme. Finally, the preview composable would looks something like this:

@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun BannerPreview() {
    PlaygroundTheme {
        Surface {
            Banner()
        }
    }
}

Here is a comparison of how previews like look when you forget to use a Card/Surface or do not apply an isSystemInDarkTheme()-aware MaterialTheme:

Antimonit
  • 2,846
  • 23
  • 34
  • Where is `PlaygroundTheme` defined? – IgorGanapolsky Jan 17 '23 at 14:20
  • 1
    @IgorGanapolsky The `PlaygroundTheme` function just handles light/dark theme and ultimately delegates to `androidx.compose.material.MaterialTheme`. It is also generated for you when you create a new Compose project, but its name depends on the name of the project. Where you put this function is up to you. – Antimonit Jan 18 '23 at 08:33
1

Another silly reason it might happened even if you added your Theme wrapping Surface wrapping your composable, is that the imports might not be corrected. since i'm using material3 i need to use this import:

import androidx.compose.material3.Surface

Instead of this:

import androidx.compose.material.Surface

If i'm using the old one (without 3) it is compiling and ignoring ui mode.

Udi Oshi
  • 6,787
  • 7
  • 47
  • 65