Search code examples
androidkotlinflutter-layoutandroid-jetpack-composeandroid-jetpack-compose-layout

What is the equivalent of Expanded widget of Flutter in Jetpack Compose?


I am pretty much new to Jetpack Compose and I want to create a simple business card app like this:

enter image description here

The way I want to achieve this layout is to create two main Columns, one for the upper part (logo with name and title) and one for the lower part of the screen (contact details). I want the first Column to take all the remaining space of the screen, so the lower Column goes to the end of the page. In Flutter I would place both of the Columns in a parent Column and wrap the first Column with Expanded, and it would do the job. So how to achieve the same behavior in Jetpack Compose? The code is as follows:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BusinessCardTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    BusinessCard()
                }
            }
        }
    }
}

@Composable
fun LogoNameTitle() {
    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Image(
            painter = painterResource(id = R.drawable.android_logo),
            contentDescription = "",
        )
        Text("Jennifer Doe", color = Color.White)
        Text("Android Developer Extraordinaire", color = Color.Green)
    }
}

@Composable
fun ContactInformation() {
    Column() {
        Row() {
            Icon(imageVector = Icons.Rounded.Phone, contentDescription = "", tint = Color.Green)
            Text("+11 (123) 444 555 666", color = Color.White)
        }
        Row() {
            Icon(imageVector = Icons.Rounded.Share, contentDescription = "", tint = Color.Green)
            Text("@AndroidDev", color = Color.White)
        }
        Row() {
            Icon(imageVector = Icons.Rounded.Email, contentDescription = "", tint = Color.Green)
            Text("jen.doe@android.com", color = Color.White)
        }
    }
}

@Composable
fun BusinessCard() {
    Surface(color = Color.DarkGray) {
        Column(
            modifier = Modifier.padding(16.dp)
        ) {
            LogoNameTitle()
            ContactInformation()
        }
    }
}

@Preview(showBackground = true, showSystemUi = true)
@Composable
fun BusinessCardPreview() {
    BusinessCardTheme {
        BusinessCard()
    }
}

And the current output is:

enter image description here


Solution

  • Use as parent container a Column and just apply the weight(1f) modifier to the 1st nested Column (name and title).

    Something like:

    Column(modifier = Modifier.fillMaxSize()){
       
       Column(Modifier.fillMaxWidth().weight(1f)){
            //Logo and title    
       }
       Column(){
           //Contact info
            Text("First Name")
            Text("Surname")
            Text("Other info")
       }
    }
    

    enter image description here

    Pls note that the weight modifier requires a ColumnScope. In your case use the modifier as parameter:

    @Composable
    fun LogoNameTitle(modifier: Modifier = Modifier) {
        Column(
            modifier = modifier,
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            //...
        }
    }
    
    @Composable
    fun BusinessCard() {
        Surface(color = Color.DarkGray,modifier = Modifier.fillMaxSize()) {
            Column(
                modifier = Modifier.fillMaxSize().padding(16.dp)
            ) {
                LogoNameTitle(modifier=Modifier.weight(1f))
                ContactInformation()
            }
        }
    }