fillMaxWidth, wrapContentSize, weight, and size in Jetpack Compose

Quick answer: fillMaxWidth() fills the maximum width offered by its parent; it does not mean “screen width.” wrapContentSize() lets a child use its desired size within available bounds. weight() divides remaining space among direct Row or Column children. size() requests a preferred fixed size, subject to incoming constraints.

Most sizing surprises in Compose come from forgetting that a child never chooses its size in isolation. Its parent passes minimum and maximum bounds—called constraints—and modifiers can transform those bounds before they reach the content.

The official constraints and modifier order guide is the reference for this model. This article turns the four sizing tools you use most often into practical layout decisions.

Choose the sizing tool by intent

You want to…PreferImportant limitation
Fill the width offered by the parentfillMaxWidth()The parent may already be padded, narrow, or unbounded.
Let content keep its natural size inside a larger allocated areawrapContentSize()It does not ignore maximum bounds by default.
Split remaining sibling spaceweight()It is scoped to direct Row or Column children.
Request a fixed visual dimensionsize()It still respects parent constraints.
Override incoming constraints deliberatelyrequiredSize()The parent can perceive a coerced size.

fillMaxWidth() means the parent’s maximum width

Compose layouts wrap their children by default. Add fillMaxWidth() when a component should take all the width its parent offers:

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun FullWidthNotice() {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .background(Color(0xFFE8DEF8))
            .padding(16.dp),
    ) {
        Text("Your download is ready")
    }
}

The column fills its incoming maximum width. If the parent is a padded Column, dialog, or list item, that maximum may be smaller than the physical device width. That is usually what you want: the child fits the area reserved for it.

fillMaxWidth(fraction) is also relative to the parent. The layout modifier API defines it by setting the width bounds to a fraction of the incoming maximum width.

Do not apply fillMaxWidth() to multiple direct children of a Row when they should share the same horizontal area. Use weight() for that sibling relationship.

Use weight() to divide remaining sibling space

weight() is a scoped modifier for direct Row and Column children. In a Row, unweighted children are measured first, then the remaining horizontal space is divided among weighted children in proportion to their weights.

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.weight
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

@Composable
fun MessageListItem(title: String, preview: String, time: String) {
    Row(modifier = Modifier.fillMaxWidth()) {
        Column(modifier = Modifier.weight(1f)) {
            Text(title)
            Text(preview)
        }
        Text(time)
    }
}

The timestamp measures at its content width; the Column gets what remains. With weight(1f) and weight(2f), the remaining space is divided into three shares. In a Column, the same modifier divides remaining height.

By default, weight() uses fill = true, so a child fills its allocated share. Use fill = false only when a child should remain smaller than its allocation and you have verified the final layout. A weighted modifier must stay on the direct child: putting it inside another Box prevents the Row or Column from using it.

The Column API reference notes that a Column with weighted children uses available height for allocation, so adding fillMaxHeight() merely for that behavior is generally unnecessary.

size() is preferred, not unconditional

Use size(), width(), or height() when the component has an intentional visual dimension, such as an avatar or status indicator.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun StatusIndicator() {
    Box(
        modifier = Modifier
            .size(12.dp)
            .background(Color(0xFF2E7D32)),
    )
}

The request is still constrained by the parent. If a parent supplies a smaller maximum or larger minimum, size() adapts as closely as it can while respecting those bounds. This is why size(400.dp) does not guarantee a 400dp element.

Chaining sizes does not make the last one win:

Modifier
    .size(100.dp)
    .size(50.dp)

The first size() establishes exact constraints for the rest of the chain, so the later request cannot override them. Keep one clear size request unless you deliberately model constraints. Why Modifier Order Matters explains this behavior in detail.

Use requiredSize() only when a child genuinely must override its constraints. Compose hides that mismatch from the parent by reporting a coerced size and may center the child in its allocated space, which is rarely the default behavior wanted in responsive screen UI.

wrapContentSize() reopens space for the child

wrapContentSize() is useful when an earlier modifier has given an element a minimum size, but the content inside should use its desired size and align within that larger area.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun BadgeInAvailableSpace() {
    Box(modifier = Modifier.size(200.dp)) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .wrapContentSize(Alignment.BottomEnd)
                .size(48.dp)
                .background(Color(0xFF6750A4)),
        )
    }
}

fillMaxSize() first uses the available 200dp square. wrapContentSize(Alignment.BottomEnd) then lets the inner content measure at 48dp and places it at the bottom end. The official wrapContentSize reference says it disregards incoming minimum constraints by default; unbounded = true also disregards maximum constraints and is a specialized option to use carefully.

For an ordinary overlay, Box(contentAlignment = …) or a child’s scoped Modifier.align() is often clearer. Use wrapContentSize() when its constraint behavior—not just centering—is what you need. Arrangement and Alignment in Jetpack Compose covers those parent-layout alternatives.

A sizing checklist

  1. Which parent supplies the available maximum width and height?
  2. Should one child fill that space, or should siblings share what remains?
  3. Is the dimension a deliberate design size, or should content decide it?
  4. Does a weight() modifier live on the direct child of the matching Row or Column?
  5. Is your modifier order making outer padding part of the filled or painted area on purpose?

For the render model beneath these decisions, read Jetpack Compose UI Phases.

FAQ

Why does fillMaxWidth() not fill the screen?

It fills the maximum width that its parent passes down. A padded parent, dialog, list item, or constrained container can offer less than the screen width.

Should I use weight() or fillMaxWidth() in a Row?

Use weight() when direct siblings must divide the Row’s remaining horizontal space. Use fillMaxWidth() for the Row itself when it should span the width offered by its parent.

Does wrapContentSize() mean unlimited size?

No. By default it allows the child to ignore minimum constraints while still respecting maximum bounds. unbounded = true changes that maximum-bound behavior.