I am trying to follow this simple tutorial to get started but i ran into a snag. I have it emulating to a device via wifi but i cant seem to find where the problem is. I read back and forth through it and it kinda annoys me that the tutorial is not updated, but can someone help me find the error?
package com.example.tutorial_helloworld
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
// Obv Importing Images
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.ui.res.painterResource
// For configuring the layout
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MessageCard(Message("Android", "Jetpack Compose"))
}
}
}
data class Message(val author: String, val body: String)
@Composable
fun MessageCard(msg: Message)
{
//Adding padding Around the Message/this row
Row(modifier = Modifier.padding(all = 8.dp))
{
Image(
// Imported Image Via Resource Manager
painter = painterResource(R.drawable.profile_picture),
contentDescription = "Contact profile picture",
modifier = Modifier
// Set image size to 40 dp (pixel density/160 pixel er inch)
.size(40.dp)
// Clip image into a circle shape
.clip(CircleShape)
)
// Add a horizontal space between the image and the column
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(text = msg.author)
// Add a vertical space between the author and message texts
Spacer(modifier = Modifier.height(4.dp))
Text(text = msg.body)
}
}
}
@Preview
@Composable
fun PreviewMessageCard() {
MessageCard(
msg = Message("Lexi","Hey, take a look at Jetpack Compose")
)
}
the Preview looks like this

And the emulated version looks like this

[! Image displaying screen of Andorid and Jetpack]
Once again, i am very new, i only started this week so an explanation if its something complex would help. Thank you!
The preview rendering differently between your IDE and your emulator is because you are missing your app's theme composable as the root composable.
The emulated preview just so happens to work because it uses a PreviewActivity activity which does set a background by default as per the system theme, while the IDE preview will not set a default background for your composable unless you set the showBackground parameter in the preview to true:
Ideally, your previews and main activity should be adding your app's theme composable to the composable hierarchy.
When using the Compose-based templates from the new project wizard, a default theme composable is typically generated (typically AppNameTheme) that uses the MaterialTheme composable, along with colour palettes. The Material3 composables you are using (i.e. Text) will then read the colours from this theme composable, or default to a suitable colour if no theme composable exists in the composable hierarchy.
@Composable fun AppNameTheme(content: @Composable () -> Unit) { MaterialTheme( colorScheme = /* ... */, // the new project wizard will likely set other parameters, which are omitted for brevity content = content ) }
For more information, I would advise checking out the Material Design 3 in Compose documentation, which goes into more detail what the MaterialTheme composable's parameters are.
Text composable infers its text coloursSpecifically for Text, it first attempts to read the desired text colour to be used in the following order:
color parameter if it's set to a colour that is not just unspecified (Color.Unspecified)textStyle parameter's color property if it's set to a colour that is not unspecifiedLocalContentColor, or the initial value Color.Black of this composition local if all else fails@Composable fun Text( text: String, color: Color = Color.Unspecified, style: TextStyle = LocalTextStyle.current // (other parameters omitted for brevity) ) { val textColor = color.takeOrElse { style.color.takeOrElse { LocalContentColor.current } } // ... }
The source code of the LocalContentColor composition local is as follows:
/** * CompositionLocal containing the preferred content color for a given position in the hierarchy. * This typically represents the `on` color for a color in [ColorScheme]. For example, if the * background color is [ColorScheme.surface], this color is typically set to * [ColorScheme.onSurface]. * * This color should be used for any typography / iconography, to ensure that the color of these * adjusts when the background color changes. For example, on a dark background, text should be * light, and on a light background, text should be dark. * * Defaults to [Color.Black] if no color has been explicitly set. */ public val LocalContentColor: ProvidableCompositionLocal<Color> = compositionLocalOf { Color.Black }
Because you are:
color, ortextStyle, orLocalContentColor valueIt then defaults to Color.Black.
You will have to add your app's theme as the parent composable which should then propagate the relevant colours to your own composables:
@Preview @Composable fun PreviewMessageCard() { YourAppTheme { MessageCard( msg = Message("Lexi","Hey, take a look at Jetpack Compose") ) } }
Alternatively, you can also create a custom preview wrapper (introduced in Compose 1.11.0) which then wraps your content in the app's theme:
class ThemeWrapper: PreviewWrapperProvider { @Composable override fun Wrap(content: @Composable (() -> Unit)) { JetsnackTheme { content() } } } @PreviewWrapper(ThemeWrapper::class) @Preview @Composable private fun ButtonPreview() { // JetsnackTheme in effect Button(onClick = {}) { Text(text = "Demo") } }
(This is directly taken from the linked blog post)
In previous versions of the IDE, I believe it should by default use the theme composable in your main activity as well, so I suspect they likely removed it in a newer release in favour of the "Navigation UI Activity" template.
Anyways, you will also have to set the app theme in the setContent {} call in your MainActivity:
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... setContent { YourAppTheme { MessageCard(...) } } } }
I would also set a background for your screen-specific components, especially if you want to render it separately from other components.
The foundational composables that are provided by Jetpack Compose (e.g. Row, Column, Box) do not set a background for you, so you will have to either:
background modifier, orSurface composable as the parent composable in your composable hierarchy, orSurface at some point (e.g. an Card/ElevatedCard/OutlinedCard, ListItem, Scaffold for app-level screens, or some other surface-based composable)SurfacePersonally, I would use a Surface composable, or a suitable higher-level Material3 component like a Card in this case, which also adds other convenient stylings like rounded corners and clickable functionality if desired.
But first:
Surface?The Surface composable provides convenient defaults:
> The Surface is responsible for:
>
> 1. Clipping: Surface clips its children to the shape specified by shape
> 2. Borders: If shape has a border, then it will also be drawn.
> 3. Background: Surface fills the shape specified by shape with the color. If color is ColorScheme.surface a color overlay will be applied. The color of the overlay depends on the tonalElevation of this Surface, and the LocalAbsoluteTonalElevation set by any parent surfaces. This ensures that a Surface never appears to have a lower elevation overlay than its ancestors, by summing the elevation of all previous Surfaces.
> 4. Content color: Surface uses contentColor to specify a preferred color for the content of this surface - this is used by the Text and Icon components as a default color.
Note specifically the 3rd and 4th points.
Card composableCard internally delegates to a Surface, providing all of the defaults as mentioned above.
You can use it as follows:
@Composable fun MessageCard(modifier: Modifier = Modifier, message: Message) { Card( modifier = modifier, ) { // Actual content goes here } }
background modifierYou can use the background modifier to specify a background colour.
@Composable fun MessageCard(modifier: Modifier = Modifier) { Row(modifier = modifier.background(...)) { // ... } }
Note that Surface uses this internally along with the clip modifier to clip the inner content to the desired shape.