Compose Multiplatform UI library for iOS Simulator ARM64 target providing declarative UI framework based on Jetpack Compose for multiplatform development
—
Flexible layout engine with built-in layouts and support for custom layout implementations with precise measurement and positioning control for creating sophisticated user interfaces.
Core layout system providing the building blocks for creating custom layout composables with precise measurement and positioning control.
/**
* A composable that lays out and draws its children according to a custom measurement and placement policy.
*/
@Composable
fun Layout(
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
)
/**
* A composable that lays out multiple sets of children according to a custom measurement and placement policy.
*/
@Composable
fun MultiMeasureLayout(
modifier: Modifier = Modifier,
measurePolicy: MultiMeasurePolicy,
content: @Composable () -> Unit
)
/**
* Policy for measuring and placing children in a Layout composable.
*/
fun interface MeasurePolicy {
/**
* Measure and position child composables.
*/
fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult
/**
* Calculate the minimum intrinsic width.
*/
fun IntrinsicMeasureScope.minIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int
): Int = 0
/**
* Calculate the minimum intrinsic height.
*/
fun IntrinsicMeasureScope.minIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int = 0
/**
* Calculate the maximum intrinsic width.
*/
fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int
): Int = 0
/**
* Calculate the maximum intrinsic height.
*/
fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int = 0
}
/**
* The measurement scope used by a Layout's MeasurePolicy.
*/
interface MeasureScope : IntrinsicMeasureScope {
/**
* Create a MeasureResult with the specified size and placement block.
*/
fun layout(
width: Int,
height: Int,
alignmentLines: Map<AlignmentLine, Int> = emptyMap(),
placementBlock: Placeable.PlacementScope.() -> Unit
): MeasureResult
}
/**
* Result of measuring a layout.
*/
interface MeasureResult {
/**
* Width of the measured layout.
*/
val width: Int
/**
* Height of the measured layout.
*/
val height: Int
/**
* Map of alignment lines to their positions.
*/
val alignmentLines: Map<AlignmentLine, Int>
/**
* Place children of the layout.
*/
fun placeChildren()
}
/**
* Represents a child composable that can be measured.
*/
interface Measurable : IntrinsicMeasurable {
/**
* Measure the child with the given constraints.
*/
fun measure(constraints: Constraints): Placeable
/**
* Data associated with this measurable.
*/
val parentData: Any?
}
/**
* Represents a measured child that can be placed.
*/
abstract class Placeable : Measured {
/**
* Width of the placeable after measurement.
*/
abstract val width: Int
/**
* Height of the placeable after measurement.
*/
abstract val height: Int
/**
* Map of alignment lines for this placeable.
*/
open val alignmentLines: Map<AlignmentLine, Int> = emptyMap()
/**
* Measured size as IntSize.
*/
val measuredSize: IntSize
/**
* Place the child at the given position.
*/
protected abstract fun placeAt(
position: IntOffset,
zIndex: Float,
layerBlock: (GraphicsLayerScope.() -> Unit)?
)
/**
* Place the child at the given coordinates.
*/
fun place(x: Int, y: Int) {
place(IntOffset(x, y))
}
/**
* Place the child at the given offset.
*/
fun place(position: IntOffset) {
placeAt(position, 0f, null)
}
/**
* Place the child with a z-index for layering.
*/
fun placeWithLayer(
position: IntOffset,
zIndex: Float = 0f,
layerBlock: GraphicsLayerScope.() -> Unit = {}
) {
placeAt(position, zIndex, layerBlock)
}
/**
* Place the child relative to the layout direction.
*/
fun placeRelative(x: Int, y: Int) {
placeRelative(IntOffset(x, y))
}
/**
* Place the child relative to the layout direction.
*/
fun placeRelative(position: IntOffset) {
placeRelativeAt(position, 0f, null)
}
/**
* The scope for placing children.
*/
abstract class PlacementScope {
/**
* The layout direction for placing children.
*/
protected abstract val parentLayoutDirection: LayoutDirection
/**
* The width of the parent layout.
*/
protected abstract val parentWidth: Int
/**
* Convert coordinates based on layout direction.
*/
protected fun Placeable.placeRelativeAt(
position: IntOffset,
zIndex: Float,
layerBlock: (GraphicsLayerScope.() -> Unit)?
)
}
}Usage Examples:
// Simple custom layout that arranges children vertically
@Composable
fun SimpleColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
// Measure all children with available width
val placeables = measurables.map { measurable ->
measurable.measure(constraints.copy(minHeight = 0))
}
// Calculate total height
val totalHeight = placeables.sumOf { it.height }
val maxWidth = placeables.maxOfOrNull { it.width } ?: 0
// Return layout result
layout(maxWidth, totalHeight) {
var yPosition = 0
placeables.forEach { placeable ->
placeable.place(x = 0, y = yPosition)
yPosition += placeable.height
}
}
}
}
// Custom layout with alignment
@Composable
fun AlignedLayout(
alignment: Alignment.Horizontal,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) }
val maxWidth = placeables.maxOfOrNull { it.width } ?: 0
val totalHeight = placeables.sumOf { it.height }
layout(maxWidth, totalHeight) {
var yPosition = 0
placeables.forEach { placeable ->
val xPosition = alignment.align(
size = placeable.width,
space = maxWidth,
layoutDirection = layoutDirection
)
placeable.place(x = xPosition, y = yPosition)
yPosition += placeable.height
}
}
}
}Constraint system for defining size requirements and limitations during layout measurement.
/**
* Immutable constraints for measuring child layouts.
*/
@Immutable
data class Constraints(
val minWidth: Int = 0,
val maxWidth: Int = Infinity,
val minHeight: Int = 0,
val maxHeight: Int = Infinity
) {
init {
require(minWidth >= 0 && minHeight >= 0) { "minWidth and minHeight must be >= 0" }
require(maxWidth >= minWidth) { "maxWidth must be >= minWidth" }
require(maxHeight >= minHeight) { "maxHeight must be >= minHeight" }
}
companion object {
/**
* A value that represents positive infinity for constraints.
*/
const val Infinity = Int.MAX_VALUE
/**
* Constraints with fixed width and height.
*/
fun fixed(width: Int, height: Int): Constraints
/**
* Constraints with fixed width.
*/
fun fixedWidth(width: Int): Constraints
/**
* Constraints with fixed height.
*/
fun fixedHeight(height: Int): Constraints
}
/**
* Whether the constraints allow only a single width value.
*/
val hasBoundedWidth: Boolean
/**
* Whether the constraints allow only a single height value.
*/
val hasBoundedHeight: Boolean
/**
* Whether there is exactly one width value that satisfies the constraints.
*/
val hasFixedWidth: Boolean
/**
* Whether there is exactly one height value that satisfies the constraints.
*/
val hasFixedHeight: Boolean
/**
* Whether the constraints specify bounded dimensions.
*/
val isZero: Boolean
/**
* Coerces width within the constraints.
*/
fun constrainWidth(width: Int): Int
/**
* Coerces height within the constraints.
*/
fun constrainHeight(height: Int): Int
/**
* Create new constraints by changing individual values.
*/
fun copy(
minWidth: Int = this.minWidth,
maxWidth: Int = this.maxWidth,
minHeight: Int = this.minHeight,
maxHeight: Int = this.maxHeight
): Constraints
/**
* Returns the result of coercing the given size within the constraints.
*/
fun constrain(size: IntSize): IntSize
/**
* Returns constraints with the width constrained between the given values.
*/
fun constrainWidth(minWidth: Int = this.minWidth, maxWidth: Int = this.maxWidth): Constraints
/**
* Returns constraints with the height constrained between the given values.
*/
fun constrainHeight(minHeight: Int = this.minHeight, maxHeight: Int = this.maxHeight): Constraints
/**
* Returns the maximum size that fits these constraints.
*/
val maxSize: IntSize
/**
* Returns the minimum size that fits these constraints.
*/
val minSize: IntSize
}Usage Examples:
// Working with constraints in custom layouts
@Composable
fun ConstraintAwareLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
// Check constraint properties
val hasFixedWidth = constraints.hasFixedWidth
val hasBoundedHeight = constraints.hasBoundedHeight
// Create modified constraints for children
val childConstraints = constraints.copy(
minWidth = 0,
minHeight = 0,
maxWidth = if (constraints.hasBoundedWidth) constraints.maxWidth / 2 else Constraints.Infinity
)
// Measure children with modified constraints
val placeables = measurables.map { measurable ->
measurable.measure(childConstraints)
}
// Calculate layout size respecting constraints
val totalWidth = placeables.sumOf { it.width }
val maxHeight = placeables.maxOfOrNull { it.height } ?: 0
val layoutWidth = constraints.constrainWidth(totalWidth)
val layoutHeight = constraints.constrainHeight(maxHeight)
layout(layoutWidth, layoutHeight) {
var xPosition = 0
placeables.forEach { placeable ->
placeable.place(x = xPosition, y = 0)
xPosition += placeable.width
}
}
}
}
// Fixed size constraints
val fixedConstraints = Constraints.fixed(width = 200, height = 100)
// Flexible constraints with limits
val flexibleConstraints = Constraints(
minWidth = 100,
maxWidth = 300,
minHeight = 50,
maxHeight = Constraints.Infinity
)System for querying preferred dimensions of composables before final measurement and layout.
/**
* Interface for calculating intrinsic measurements.
*/
interface IntrinsicMeasurable {
/**
* Data from parent layout.
*/
val parentData: Any?
/**
* Calculate the minimum width for the given height.
*/
fun minIntrinsicWidth(height: Int): Int
/**
* Calculate the maximum width for the given height.
*/
fun maxIntrinsicWidth(height: Int): Int
/**
* Calculate the minimum height for the given width.
*/
fun minIntrinsicHeight(width: Int): Int
/**
* Calculate the maximum height for the given width.
*/
fun maxIntrinsicHeight(width: Int): Int
}
/**
* Scope for intrinsic measurement calculations.
*/
interface IntrinsicMeasureScope : Density {
/**
* The layout direction for intrinsic calculations.
*/
val layoutDirection: LayoutDirection
}
/**
* Marker interface for measured layouts.
*/
interface Measured {
/**
* The measured width.
*/
val measuredWidth: Int
/**
* The measured height.
*/
val measuredHeight: Int
/**
* Get the position of an alignment line, or AlignmentLine.Unspecified.
*/
operator fun get(alignmentLine: AlignmentLine): Int
}Usage Examples:
// Layout using intrinsic measurements
@Composable
fun IntrinsicAwareLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier,
measurePolicy = object : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
// Query intrinsic sizes before measuring
val minIntrinsicWidth = measurables.sumOf {
it.minIntrinsicWidth(Constraints.Infinity)
}
val maxIntrinsicHeight = measurables.maxOfOrNull {
it.maxIntrinsicHeight(constraints.maxWidth)
} ?: 0
// Use intrinsic information to make measurement decisions
val childConstraints = if (minIntrinsicWidth <= constraints.maxWidth) {
constraints.copy(minWidth = 0)
} else {
constraints.copy(maxWidth = constraints.maxWidth / measurables.size)
}
val placeables = measurables.map { measurable ->
measurable.measure(childConstraints)
}
val layoutWidth = constraints.constrainWidth(
placeables.sumOf { it.width }
)
val layoutHeight = constraints.constrainHeight(
placeables.maxOfOrNull { it.height } ?: 0
)
layout(layoutWidth, layoutHeight) {
var xPosition = 0
placeables.forEach { placeable ->
placeable.place(x = xPosition, y = 0)
xPosition += placeable.width
}
}
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int
): Int {
return measurables.sumOf { it.minIntrinsicWidth(height) }
}
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int
): Int {
return measurables.sumOf { it.maxIntrinsicWidth(height) }
}
}
)
}
// Using intrinsic size modifiers
@Composable
fun IntrinsicExample() {
Column {
// Width based on intrinsic measurements
Row(
modifier = Modifier.width(IntrinsicSize.Max)
) {
Text("Short", modifier = Modifier.fillMaxHeight())
Text("Much longer text", modifier = Modifier.fillMaxHeight())
}
// Height based on intrinsic measurements
Column(
modifier = Modifier.height(IntrinsicSize.Min)
) {
Text("Line 1")
Text("Line 2 with more content")
}
}
}System for responding to layout changes, positioning updates, and size modifications.
/**
* Modifier that provides callback for global position changes.
*/
fun Modifier.onGloballyPositioned(
onGloballyPositioned: (LayoutCoordinates) -> Unit
): Modifier
/**
* Modifier that provides callback for size changes.
*/
fun Modifier.onSizeChanged(
onSizeChanged: (IntSize) -> Unit
): Modifier
/**
* Modifier that provides callback for placement changes.
*/
fun Modifier.onPlaced(
onPlaced: (LayoutCoordinates) -> Unit
): Modifier
/**
* A coordinate system for a layout.
*/
interface LayoutCoordinates {
/**
* The size of this layout in the local coordinate system.
*/
val size: IntSize
/**
* Whether this coordinate system is attached to the root of the composition.
*/
val isAttached: Boolean
/**
* The LayoutCoordinates of the parent layout modifier or parent layout.
*/
val parentLayoutCoordinates: LayoutCoordinates?
/**
* The LayoutCoordinates of the parent composable.
*/
val parentCoordinates: LayoutCoordinates?
/**
* Converts a local position to an ancestor coordinate system.
*/
fun localToWindow(relativeToLocal: Offset): Offset
/**
* Converts a local position to the root coordinate system.
*/
fun localToRoot(relativeToLocal: Offset): Offset
/**
* Converts a position in the ancestor coordinate system to local.
*/
fun windowToLocal(relativeToWindow: Offset): Offset
/**
* Converts a position in the root coordinate system to local.
*/
fun rootToLocal(relativeToRoot: Offset): Offset
/**
* Converts a local position to the coordinate system of another layout.
*/
fun localPositionOf(
sourceCoordinates: LayoutCoordinates,
relativeToSource: Offset
): Offset
/**
* Converts a local bounding box to the coordinate system of another layout.
*/
fun localBoundingBoxOf(
sourceCoordinates: LayoutCoordinates,
clipBounds: Boolean = true
): Rect
/**
* Gets the position of an alignment line in the local coordinate system.
*/
operator fun get(alignmentLine: AlignmentLine): Int
}
/**
* Intrinsic size values for width and height constraints.
*/
enum class IntrinsicSize { Min, Max }
/**
* Modifier for setting intrinsic size constraints.
*/
fun Modifier.width(intrinsicSize: IntrinsicSize): Modifier
fun Modifier.height(intrinsicSize: IntrinsicSize): Modifier
fun Modifier.requiredWidth(intrinsicSize: IntrinsicSize): Modifier
fun Modifier.requiredHeight(intrinsicSize: IntrinsicSize): ModifierUsage Examples:
// Responding to layout position changes
@Composable
fun PositionAwareBox() {
var position by remember { mutableStateOf(Offset.Zero) }
var size by remember { mutableStateOf(IntSize.Zero) }
Box(
modifier = Modifier
.size(200.dp)
.background(Color.Blue)
.onGloballyPositioned { coordinates ->
position = coordinates.localToRoot(Offset.Zero)
size = coordinates.size
}
.onSizeChanged { newSize ->
// React to size changes
println("Size changed to: $newSize")
}
) {
Text(
text = "Position: (${position.x.toInt()}, ${position.y.toInt()})\nSize: ${size.width} x ${size.height}",
color = Color.White,
modifier = Modifier.align(Alignment.Center)
)
}
}
// Coordinate system conversions
@Composable
fun CoordinateExample() {
var clickPosition by remember { mutableStateOf(Offset.Zero) }
Box(
modifier = Modifier
.fillMaxSize()
.onGloballyPositioned { rootCoordinates ->
// Store root coordinates for conversions
}
.pointerInput(Unit) {
detectTapGestures { tapOffset ->
clickPosition = tapOffset
}
}
) {
Box(
modifier = Modifier
.size(100.dp)
.background(Color.Red)
.align(Alignment.Center)
.onGloballyPositioned { childCoordinates ->
// Convert click position to child coordinate system
val localPosition = childCoordinates.windowToLocal(clickPosition)
if (childCoordinates.size.toRect().contains(localPosition)) {
println("Clicked inside child at local position: $localPosition")
}
}
)
}
}System for passing layout-specific data from child to parent composables during layout.
/**
* Modifier for attaching parent data to a layout.
*/
fun Modifier.parentData(parentData: Any?): Modifier
/**
* Marker interface for parent data classes.
*/
interface ParentDataModifier : Modifier.Element {
/**
* Modify the parent data.
*/
fun Density.modifyParentData(parentData: Any?): Any?
}
/**
* Alignment lines for coordinating layout alignment.
*/
abstract class AlignmentLine(internal val merger: (Int, Int) -> Int) {
companion object {
/**
* Unspecified alignment line position.
*/
const val Unspecified: Int = Int.MIN_VALUE
}
}
/**
* Horizontal alignment line.
*/
abstract class HorizontalAlignmentLine(merger: (Int, Int) -> Int) : AlignmentLine(merger)
/**
* Vertical alignment line.
*/
abstract class VerticalAlignmentLine(merger: (Int, Int) -> Int) : AlignmentLine(merger)
/**
* Common alignment lines.
*/
object AlignmentLineOffset {
/**
* First baseline of text.
*/
val FirstBaseline: HorizontalAlignmentLine
/**
* Last baseline of text.
*/
val LastBaseline: HorizontalAlignmentLine
}
/**
* Modifier for providing alignment lines.
*/
fun Modifier.alignmentLineOffset(
alignmentLine: AlignmentLine,
before: Dp = 0.dp,
after: Dp = 0.dp
): Modifier
/**
* Modifier for aligning by a baseline.
*/
fun Modifier.alignBy(alignmentLine: HorizontalAlignmentLine): Modifier
fun Modifier.alignBy(alignmentLineBlock: (Measured) -> Int): ModifierUsage Examples:
// Custom parent data for layout parameters
data class FlexChildData(
val flex: Float,
val alignment: Alignment.Vertical? = null
)
fun Modifier.flex(flex: Float, alignment: Alignment.Vertical? = null) = this.then(
object : ParentDataModifier {
override fun Density.modifyParentData(parentData: Any?): FlexChildData {
return FlexChildData(flex, alignment)
}
}
)
@Composable
fun FlexLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
// Access parent data from children
val flexData = measurables.map { measurable ->
(measurable.parentData as? FlexChildData) ?: FlexChildData(flex = 1f)
}
val totalFlex = flexData.sumOf { it.flex.toDouble() }.toFloat()
val availableWidth = constraints.maxWidth
val placeables = measurables.mapIndexed { index, measurable ->
val flex = flexData[index].flex
val childWidth = ((availableWidth * flex) / totalFlex).toInt()
measurable.measure(
constraints.copy(
minWidth = childWidth,
maxWidth = childWidth
)
)
}
val maxHeight = placeables.maxOfOrNull { it.height } ?: 0
layout(availableWidth, maxHeight) {
var xPosition = 0
placeables.forEachIndexed { index, placeable ->
val alignment = flexData[index].alignment ?: Alignment.CenterVertically
val yPosition = alignment.align(placeable.height, maxHeight)
placeable.place(x = xPosition, y = yPosition)
xPosition += placeable.width
}
}
}
}
// Using the flex layout
@Composable
fun FlexExample() {
FlexLayout(
modifier = Modifier.fillMaxWidth()
) {
Box(
modifier = Modifier
.flex(1f, Alignment.Top)
.height(50.dp)
.background(Color.Red)
)
Box(
modifier = Modifier
.flex(2f, Alignment.CenterVertically)
.height(100.dp)
.background(Color.Green)
)
Box(
modifier = Modifier
.flex(1f, Alignment.Bottom)
.height(75.dp)
.background(Color.Blue)
)
}
}
// Alignment line example
@Composable
fun BaselineAlignedRow() {
Row {
Text(
text = "Small",
fontSize = 12.sp,
modifier = Modifier.alignBy(FirstBaseline)
)
Text(
text = "Large",
fontSize = 24.sp,
modifier = Modifier.alignBy(FirstBaseline)
)
Text(
text = "Medium",
fontSize = 18.sp,
modifier = Modifier.alignBy(FirstBaseline)
)
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-compose-ui--ui-uikitsimarm64