GridConfigurationScope


Scope for configuring the structure of a Grid.

This interface is implemented by the configuration block in Grid. It allows defining columns, rows, and gaps.

The order in which column and row functions are called within the config block is important. Tracks are added to the grid definition sequentially based on these calls. For example, calling column(100.dp) twice defines two columns.

Gap configuration calls (gap, rowGap, columnGap) follow a "last-call-wins" policy for their respective axes.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Grid
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Grid(
    config = {
        // This defines the first column
        column(100.dp)
        // This defines the second column
        column(1.fr)

        // This defines the first row
        row(50.dp)
        // The order is important. additional calls to row() or column() append tracks.

        gap(all = 8.dp) // Set both row and column gaps
        columnGap(16.dp) // Override column gap
    }
) {
    Box(
        modifier = Modifier.gridItem(row = 1, column = 1).background(Color.Blue).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Row: 1, Column: 1", color = Color.White)
    }

    Box(
        modifier = Modifier.gridItem(row = 1, column = 1).background(Color.Blue).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Row: 1, Column: 2", color = Color.White)
    }
}
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Grid
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Grid(
    modifier = Modifier.fillMaxSize().padding(16.dp),
    config = {
        val maxWidthDp = constraints.maxWidth.toDp()
        if (maxWidthDp < 600.dp) {
            // Compact Layout: 2 Columns
            repeat(2) { column(1.fr) }
        } else {
            // Expanded Layout: 4 Columns
            repeat(4) { column(1.fr) }
        }

        // Rows are auto-generated based on content
        gap(8.dp)
    },
) {
    repeat(8) { index ->
        Box(
            modifier = Modifier.background(Color.Cyan).padding(24.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text("Item $index")
        }
    }
}

Summary

Public functions

open Unit
area(areaId: Any, rows: IntRange, columns: IntRange)

Defines a named area within the grid using explicit coordinate ranges.

Cmn
Unit
area(areaId: Any, row: Int, column: Int, rowSpan: Int, columnSpan: Int)

Defines a named area or a 1-dimensional track within the grid by mapping an identifier to physical starting coordinates and spans.

Cmn
Unit
column(percentage: @FloatRange(from = 0.0, to = 1.0) Float)

Defines a percentage-based column.

Cmn
Unit
column(size: Dp)

Defines a fixed-width column.

Cmn
Unit

Defines a new column track with the specified size.

Cmn
Unit
column(weight: Fr)

Defines a flexible column.

Cmn
Unit
columnGap(gap: Dp)

Sets the gap (gutter) size between columns.

Cmn
Unit
gap(all: Dp)

Sets both the row and column gaps (gutters) to all.

Cmn
Unit
gap(row: Dp, column: Dp)

Sets independent gaps for rows and columns.

Cmn
open GridTrackSize
minmax(min: Dp, max: Fr)

A flexible track with an explicitly defined minimum base size and a flexible maximum size.

Cmn
Unit
row(percentage: @FloatRange(from = 0.0, to = 1.0) Float)

Defines a percentage-based row.

Cmn
Unit
row(size: Dp)

Defines a fixed-width row.

Cmn
Unit

Defines a new row track with the specified size.

Cmn
Unit
row(weight: Fr)

Defines a flexible row.

Cmn
Unit
rowGap(gap: Dp)

Sets the gap (gutter) size between rows.

Cmn

Public properties

Constraints

The layout constraints passed to this Grid from its parent.

Cmn
GridFlow

The direction in which items that do not specify a position are placed.

Cmn
open Fr

Creates an Fr unit from a Double.

Cmn
open Fr

Creates an Fr unit from a Float.

Cmn
open Fr

Creates an Fr unit from an Int.

Cmn

Extension functions

Unit

Adds multiple columns with the specified specs.

Cmn
Unit

Adds multiple rows with the specified specs.

Cmn

Inherited functions

From androidx.compose.ui.unit.Density
open Int

Convert Dp to Int by rounding

Cmn
open Int

Convert Sp to Int by rounding

Cmn
open Dp

Convert an Int pixel value to Dp.

Cmn
open Dp

Convert a Float pixel value to a Dp

Cmn
open DpSize

Convert a Size to a DpSize.

Cmn
open Float

Convert Dp to pixels.

Cmn
open Float

Convert Sp to pixels.

Cmn
open Rect

Convert a DpRect to a Rect.

Cmn
open Size

Convert a DpSize to a Size.

Cmn
open TextUnit

Convert an Int pixel value to Sp.

Cmn
open TextUnit

Convert a Float pixel value to a Sp

Cmn
From androidx.compose.ui.unit.FontScaling
Dp

Convert Sp to Dp.

Cmn
TextUnit

Convert Dp to Sp.

Cmn

Inherited properties

From androidx.compose.ui.unit.Density
Float

The logical density of the display.

Cmn
From androidx.compose.ui.unit.FontScaling
Float

Current user preference for the scaling factor for fonts.

Cmn

Public functions

area

open fun area(areaId: Any, rows: IntRange, columns: IntRange): Unit

Defines a named area within the grid using explicit coordinate ranges.

This is a convenience overload that computes the starting coordinate and span based on the provided IntRange boundaries.

Example: area(AppArea.Footer, rows = 2..3, columns = 1..2) is functionally equivalent to area(AppArea.Footer, row = 2, column = 1, rowSpan = 2, columnSpan = 2).

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Grid
import androidx.compose.foundation.layout.columns
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.rows
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Grid(
    modifier = Modifier.fillMaxSize().padding(16.dp),
    config = {
        repeat(4) { column(1.fr) }
        repeat(4) { row(1.fr) }

        // Easily define a 2x2 area right in the center using IntRanges
        area("CenterBox", rows = 2..3, columns = 2..3)
    },
) {
    Box(
        modifier = Modifier.gridItem("CenterBox").background(Color.Blue).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("I span rows 2-3 and columns 2-3", color = Color.White)
    }
}
Parameters
areaId: Any

A user-defined identifier (e.g., an Enum, String, or object marker) that represents this area.

rows: IntRange

The range of rows to occupy (e.g., 1..2). The start determines the 1-based row index, and the size of the range determines the span.

columns: IntRange

The range of columns to occupy (e.g., 1..3). The start determines the 1-based column index, and the size of the range determines the span.

area

fun area(
    areaId: Any,
    row: Int = GridIndexUnspecified,
    column: Int = GridIndexUnspecified,
    rowSpan: Int = 1,
    columnSpan: Int = 1
): Unit

Defines a named area or a 1-dimensional track within the grid by mapping an identifier to physical starting coordinates and spans.

Once defined, this identifier can be referenced by child composables using Modifier.gridItem(areaId) to place them into this specific area. This decouples a component's semantic intent from its exact physical layout coordinates.

1D Areas & Flow: To create a 1-dimensional track, explicitly pass GridIndexUnspecified to the dimension you want to auto-flow. For example, area("Header", row = 1, column = GridIndexUnspecified) restricts the area to the first row, allowing multiple items placed into it to automatically flow side-by-side into available columns.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Grid
import androidx.compose.foundation.layout.columns
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.rows
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Grid(
    modifier = Modifier.fillMaxSize().padding(16.dp),
    config = {
        // 1. Define Physical Tracks
        column(100.dp) // Sidebar track
        column(1.fr) // Main content track

        row(60.dp) // Header track
        row(1.fr) // Main content track
        row(50.dp) // Footer track

        gap(8.dp)

        // 2. Map Semantic Strings to physical coordinates
        area("header", row = 1, column = 1, columnSpan = 2)
        area("sidebar", row = 2, column = 1)
        area("content", row = 2, column = 2)
        area("footer", rows = 3..3, columns = 1..2)
    },
) {
    // 3. Place items purely by semantic intent!
    Box(
        modifier = Modifier.gridItem("header").background(Color.DarkGray).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Header", color = Color.White)
    }

    Box(
        modifier = Modifier.gridItem("sidebar").background(Color.LightGray).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Sidebar")
    }

    Box(
        modifier = Modifier.gridItem("content").background(Color.Cyan).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Main Content")
    }

    Box(
        modifier = Modifier.gridItem("footer").background(Color.Gray).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Footer", color = Color.White)
    }
}
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Grid
import androidx.compose.foundation.layout.columns
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Grid(
    modifier = Modifier.fillMaxSize().padding(16.dp),
    config = {
        // 1. Define physical tracks
        column(100.dp) // Sidebar track
        column(1.fr) // Main content track

        row(60.dp) // Header track
        row(1.fr) // Main content track

        gap(8.dp)

        // 2. Define 1-Dimensional Areas
        // 1D Area: Fix the row, leave column unspecified
        area("header", row = 1)

        // 1D Area: Fix the column, leave row unspecified
        area("sidebar", column = 1)

        // Fully specified 2D area
        area("content", row = 2, column = 2)
    },
) {
    // Because "header" is 1D, items automatically flow into available columns!
    // Logo takes the first available slot (row 1, col 1)
    Box(
        modifier = Modifier.gridItem("header").background(Color.Red).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Logo", color = Color.White)
    }

    // Search automatically flows into the next available slot (row 1, col 2)
    Box(
        modifier = Modifier.gridItem("header").background(Color.Magenta).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Search Bar", color = Color.White)
    }

    // Because "sidebar" is 1D (col=1), it flows into the next available row.
    // Since (row 1, col 1) is taken by Logo, this flows to (row 2, col 1).
    Box(
        modifier = Modifier.gridItem("sidebar").background(Color.Blue).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Sidebar Menu", color = Color.White)
    }

    // Exact 2D placement
    Box(
        modifier = Modifier.gridItem("content").background(Color.Green).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Main Content", color = Color.White)
    }
}
Parameters
areaId: Any

A user-defined identifier (e.g., an Enum, String, or object marker) that represents this area. This identifier must have a stable equals() and hashCode().

row: Int = GridIndexUnspecified

The 1-based starting row index of the area. Defaults to GridIndexUnspecified to create a 1D column-based area where items flow vertically.

column: Int = GridIndexUnspecified

The 1-based starting column index of the area. Defaults to GridIndexUnspecified to create a 1D row-based area where items flow horizontally.

rowSpan: Int = 1

The number of rows this area should occupy. Must be greater than 0. Defaults to 1.

columnSpan: Int = 1

The number of columns this area should occupy. Must be greater than 0. Defaults to 1.

column

fun column(percentage: @FloatRange(from = 0.0, to = 1.0) Float): Unit

Defines a percentage-based column. Maps to GridTrackSize.Percentage.

Parameters
percentage: @FloatRange(from = 0.0, to = 1.0) Float

The percentage (0.0 to 1.0) of the available space.

column

fun column(size: Dp): Unit

Defines a fixed-width column. Maps to GridTrackSize.Fixed.

column

fun column(size: GridTrackSize): Unit

Defines a new column track with the specified size.

column

fun column(weight: Fr): Unit

Defines a flexible column. Maps to GridTrackSize.Flex.

columnGap

fun columnGap(gap: Dp): Unit

Sets the gap (gutter) size between columns.

Precedence: If this is called multiple times, the last call takes precedence. This call will overwrite the column component of any previous gap call.

Throws
IllegalArgumentException

if gap is negative.

gap

fun gap(all: Dp): Unit

Sets both the row and column gaps (gutters) to all.

Precedence: If this is called multiple times, or mixed with columnGap or rowGap, the last call takes precedence.

Throws
IllegalArgumentException

if all is negative.

gap

fun gap(row: Dp, column: Dp): Unit

Sets independent gaps for rows and columns.

Precedence: If this is called multiple times, or mixed with columnGap or rowGap, the last call takes precedence.

Throws
IllegalArgumentException

if row or column is negative.

minmax

open fun minmax(min: Dp, max: Fr): GridTrackSize

A flexible track with an explicitly defined minimum base size and a flexible maximum size. Conceptually, this behaves identically to the CSS Grid minmax(min, max) function.

Usage with Lazy Lists: Because minmax relies on a predefined min size (e.g., 0.dp), it entirely bypasses the intrinsic measurement pass. This makes it the required choice when placing SubcomposeLayout-backed components (such as LazyColumn or LazyRow) inside a flexible grid track.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Grid
import androidx.compose.foundation.layout.GridTrackSize
import androidx.compose.foundation.layout.columns
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

Grid(
    modifier = Modifier.fillMaxSize().padding(16.dp),
    config = {
        column(120.dp) // Sidebar width
        column(minmax(0.dp, 1.fr)) // Content width

        row(60.dp) // Header height
        // IMPORTANT:
        // Flex track '1.fr' queries child intrinsic sizes. Since SubcomposeLayouts
        // (like LazyColumn) crash on intrinsic queries, we MUST use 'GridTrackSize.MinMax' with
        // an explicit minimum size (0.dp) to bypass the measurement crash safely!
        row(minmax(0.dp, 1.fr))

        gap(16.dp)
    },
) {
    // Top Header spanning both columns
    Box(
        Modifier.gridItem(row = 1, column = 1, columnSpan = 2)
            .background(Color.DarkGray)
            .fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("App Header", color = Color.White)
    }

    // Left Sidebar
    Box(
        Modifier.gridItem(row = 2, column = 1).background(Color.LightGray).fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Text("Navigation")
    }

    // Scrollable LazyColumn safely constrained in the flex area
    LazyColumn(
        modifier = Modifier.gridItem(row = 2, column = 2).fillMaxSize(),
        verticalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        items(50) { index ->
            Box(Modifier.fillMaxWidth().background(Color(0xFFE0E0FF)).padding(16.dp)) {
                Text("Scrollable Content #$index")
            }
        }
    }
}
Parameters
min: Dp

The explicit minimum fixed base size (e.g., 0.dp).

max: Fr

The maximum flexible distribution weight (e.g., 1.fr).

row

fun row(percentage: @FloatRange(from = 0.0, to = 1.0) Float): Unit

Defines a percentage-based row. Maps to GridTrackSize.Percentage.

Parameters
percentage: @FloatRange(from = 0.0, to = 1.0) Float

The percentage (0.0 to 1.0) of the available space.

row

fun row(size: Dp): Unit

Defines a fixed-width row. Maps to GridTrackSize.Fixed.

row

fun row(size: GridTrackSize): Unit

Defines a new row track with the specified size.

row

fun row(weight: Fr): Unit

Defines a flexible row. Maps to GridTrackSize.Flex.

rowGap

fun rowGap(gap: Dp): Unit

Sets the gap (gutter) size between rows.

Precedence: If this is called multiple times, the last call takes precedence. This call will overwrite the row component of any previous gap call.

Throws
IllegalArgumentException

if gap is negative.

Public properties

constraints

val constraintsConstraints

The layout constraints passed to this Grid from its parent.

These constraints represent the minimum and maximum size limits that the parent has imposed on this Grid. This can be useful for creating responsive layouts that adapt based on available space.

See also
Constraints

flow

var flowGridFlow

The direction in which items that do not specify a position are placed. Defaults to GridFlow.Row.

@ExperimentalGridApi
open val Double.frFr

Creates an Fr unit from a Double.

@ExperimentalGridApi
open val Float.frFr

Creates an Fr unit from a Float.

@ExperimentalGridApi
open val Int.frFr

Creates an Fr unit from an Int.

Extension functions

GridConfigurationScope.columns

@ExperimentalGridApi
fun GridConfigurationScope.columns(vararg specs: GridTrackSpec): Unit

Adds multiple columns with the specified specs.

GridConfigurationScope.rows

@ExperimentalGridApi
fun GridConfigurationScope.rows(vararg specs: GridTrackSpec): Unit

Adds multiple rows with the specified specs.