Search code examples
android-studioandroid-jetpack-composeandroid-compose-textfield

How to define nested Surfaces in Android Jetpack Compose


The following code produces this screenshot. The code is a slightly modified version of the default app generated by Android Studio Flamingo.

What I expect from this code is for the border to appear around the Text, not around the background. I also expect the Text background to be a different color than the app background.

How should this code be modified to produce the expected result?

enter image description here

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicCodelabTheme {
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Surface(color = MaterialTheme.colorScheme.primary) {
        val mods = Modifier.padding(24.dp).border(2.dp, Color.Cyan, RoundedCornerShape(30))
        Text(text = "Hello $name!", modifier = mods)
    }
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    BasicCodelabTheme {
        Greeting("Android")
    }
}

Solution

  • You can solve this by doing these 2 changes:

    @Composable
    fun Greeting(name: String) {
        Surface(color = MaterialTheme.colorScheme.primary, modifier = Modifier.wrapContentSize()) {
            val mods = Modifier.border(2.dp, Color.Cyan, RoundedCornerShape(30)).padding(24.dp)
            Text(text = "Hello $name!", modifier = mods)
        }
    }
    
    @Preview(showBackground = true)
    @Composable
    fun GreetingPreview() {
        BasicCodelabTheme {
            Greeting("Android")
        }
    }
    
    • Adding wrapContentSize() will avoid causing this surface to occupy the entire screen;
    • Change padding order to be applied first. Compose draws elements from top to bottom, so in this case, we'll be applying padding first and then the border will be drawn considering given padding;