[go: nahoru, domu]

ui-layout to MPP

Test: ./gradlew buildOnServer
Change-Id: Iad0c0a2dd0004b38518c6080e721e4e39dab0da2
Relnote: N/A
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/AlignmentLine.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/AlignmentLine.kt
new file mode 100644
index 0000000..5525634
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/AlignmentLine.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Composable
+import androidx.compose.Stable
+
+import androidx.ui.core.AlignmentLine
+import androidx.ui.core.Constraints
+import androidx.ui.core.HorizontalAlignmentLine
+import androidx.ui.core.Layout
+import androidx.ui.core.LayoutDirection
+import androidx.ui.core.LayoutModifier
+import androidx.ui.core.Measurable
+import androidx.ui.core.MeasureScope
+import androidx.ui.core.Modifier
+import androidx.ui.unit.Dp
+import androidx.ui.unit.dp
+/**
+ * Note: This composable is on the deprecation path and will be soon replaced with a [Modifier].
+ *
+ * Layout composable that takes a child and tries to position it within itself according to
+ * specified offsets relative to an [alignment line][AlignmentLine], subject to the incoming
+ * layout constraints. The [AlignmentLineOffset] layout will try to size itself to wrap the
+ * child and include the needed padding, such that the distance from the [AlignmentLineOffset]
+ * borders to the [AlignmentLine] of the child will be [before] and [after], respectively.
+ * The [before] and [after] values will be interpreted as offsets on the axis corresponding to
+ * the alignment line.
+ *
+ * @param alignmentLine the alignment line to be used for positioning the child
+ * @param before the offset between the left or top container border and the alignment line
+ * @param after the offset between the bottom or right container border and the alignment line
+ */
+@Deprecated(
+    "AlignmentLineOffset is deprecated. Instead apply Modifier.relativePaddingFrom modifier to " +
+            "the children",
+    replaceWith = ReplaceWith(
+        "Stack(Modifier.relativePaddingFrom(alignmentLine, before, after), children)",
+        "import androidx.ui.layout.relativePaddingFrom",
+        "import androidx.ui.layout.Stack"
+    )
+)
+@Composable
+fun AlignmentLineOffset(
+    alignmentLine: AlignmentLine,
+    modifier: Modifier = Modifier,
+    before: Dp = 0.dp,
+    after: Dp = 0.dp,
+    children: @Composable () -> Unit
+) {
+    Layout(children, modifier) { measurables, constraints ->
+        require(measurables.isNotEmpty()) { "No child found in AlignmentLineOffset" }
+        val placeable = measurables.first().measure(
+            // Loose constraints perpendicular on the alignment line.
+            if (alignmentLine.horizontal) constraints.copy(minHeight = 0)
+            else constraints.copy(minWidth = 0)
+        )
+        val linePosition = placeable[alignmentLine].let {
+            if (it != AlignmentLine.Unspecified) it else 0
+        }
+        val axis = if (alignmentLine.horizontal) placeable.height else placeable.width
+        val axisMax = if (alignmentLine.horizontal) constraints.maxHeight else constraints.maxWidth
+        // Compute padding required to satisfy the total before and after offsets.
+        val paddingBefore = (before.toIntPx() - linePosition).coerceIn(0, axisMax - axis)
+        val paddingAfter = (after.toIntPx() - axis + linePosition).coerceIn(
+            0,
+            axisMax - axis - paddingBefore
+        )
+        // Calculate the size of the AlignmentLineOffset composable & define layout.
+        val containerWidth =
+            if (alignmentLine.horizontal) placeable.width
+            else paddingBefore + placeable.width + paddingAfter
+        val containerHeight =
+            if (alignmentLine.horizontal) paddingBefore + placeable.height + paddingAfter
+            else placeable.height
+        layout(containerWidth, containerHeight) {
+            val x = if (alignmentLine.horizontal) 0 else paddingBefore
+            val y = if (alignmentLine.horizontal) paddingBefore else 0
+            placeable.placeAbsolute(x, y)
+        }
+    }
+}
+
+/**
+ * Allow the content to be positioned according to the specified offset relative to the
+ * [alignment line][AlignmentLine], subject to the incoming layout constraints.
+ *
+ * The modified layout will include the needed padding, such that the distance from its borders
+ * to the [alignmentLine] of the content box will be [before] and [after], respectively.
+ * The [before] and [after] values will be interpreted as offsets on the axis corresponding to
+ * the alignment line.
+ *
+ * @param alignmentLine the alignment line to be used for positioning the content
+ * @param before the offset between the container's top edge and the horizontal alignment line, or
+ * the container's start edge and the vertical alignment line
+ * @param after the offset between the container's bottom edge and the horizontal alignment line, or
+ * the container's end edge and the vertical alignment line
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.RelativePaddingFromSample
+ */
+@Stable
+fun Modifier.relativePaddingFrom(
+    alignmentLine: AlignmentLine,
+    before: Dp = 0.dp,
+    after: Dp = 0.dp
+): Modifier = this + AlignmentLineOffset(alignmentLine, before, after)
+
+private data class AlignmentLineOffset(
+    val alignmentLine: AlignmentLine,
+    val before: Dp = 0.dp,
+    val after: Dp = 0.dp
+) : LayoutModifier {
+    init {
+        require(before.value >= 0f && after.value >= 0f) {
+            "Padding from alignment line must be non-negative"
+        }
+    }
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val placeable = measurable.measure(
+            // Loose constraints perpendicular on the alignment line.
+            if (alignmentLine.horizontal) constraints.copy(minHeight = 0)
+            else constraints.copy(minWidth = 0)
+        )
+        val linePosition = placeable[alignmentLine].let {
+            if (it != AlignmentLine.Unspecified) it else 0
+        }
+        val axis = if (alignmentLine.horizontal) placeable.height else placeable.width
+        val axisMax = if (alignmentLine.horizontal) constraints.maxHeight else constraints.maxWidth
+        // Compute padding required to satisfy the total before and after offsets.
+        val paddingBefore = (before.toIntPx() - linePosition).coerceIn(0, axisMax - axis)
+        val paddingAfter = (after.toIntPx() - axis + linePosition).coerceIn(
+            0,
+            axisMax - axis - paddingBefore
+        )
+
+        val width =
+            if (alignmentLine.horizontal) placeable.width
+            else paddingBefore + placeable.width + paddingAfter
+        val height =
+            if (alignmentLine.horizontal) paddingBefore + placeable.height + paddingAfter
+            else placeable.height
+        return layout(width, height) {
+            val x = if (alignmentLine.horizontal) 0 else paddingBefore
+            val y = if (alignmentLine.horizontal) paddingBefore else 0
+            placeable.place(x, y)
+        }
+    }
+}
+
+private val AlignmentLine.horizontal: Boolean get() = this is HorizontalAlignmentLine
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Column.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Column.kt
new file mode 100644
index 0000000..3761df3
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Column.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
+import androidx.ui.core.Alignment
+import androidx.ui.core.Measured
+import androidx.ui.core.Modifier
+import androidx.ui.core.VerticalAlignmentLine
+import androidx.ui.layout.ColumnScope.alignWithSiblings
+import androidx.ui.layout.RowScope.alignWithSiblings
+import androidx.ui.util.annotation.FloatRange
+
+/**
+ * A layout composable that places its children in a vertical sequence. For a layout composable
+ * that places its children in a horizontal sequence, see [Row].
+ *
+ * The layout model is able to assign children heights according to their weights provided
+ * using the [ColumnScope.weight] modifier. If a child is not provided a weight, it will be
+ * asked for its preferred height before the sizes of the children with weights are calculated
+ * proportionally to their weight based on the remaining available space.
+ *
+ * When none of its children have weights, a [Column] will be as small as possible to fit its
+ * children one on top of the other. In order to change the height of the [Column], use the
+ * [Modifier.height] modifiers; e.g. to make it fill the available height [Modifier.fillMaxHeight]
+ * can be used. If at least one child of a [Column] has a [weight][ColumnScope.weight],
+ * the [Column] will fill the available height, so there is no need for [Modifier.fillMaxHeight].
+ * However, if [Column]'s size should be limited, the [Modifier.height] or [Modifier.size] layout
+ * modifiers should be applied.
+ *
+ * When the size of the [Column] is larger than the sum of its children sizes, a
+ * [verticalArrangement] can be specified to define the positioning of the children inside the
+ * [Column]. See [Arrangement] for available positioning behaviors; a custom arrangement can also
+ * be defined using the constructor of [Arrangement].
+ *
+ * Example usage:
+ *
+ * @sample androidx.ui.layout.samples.SimpleColumn
+ *
+ * @param modifier The modifier to be applied to the Column.
+ * @param verticalArrangement The vertical arrangement of the layout's children.
+ * @param horizontalGravity The horizontal gravity of the layout's children.
+ *
+ * @see Column
+ */
+@Composable
+fun Column(
+    modifier: Modifier = Modifier,
+    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
+    horizontalGravity: Alignment.Horizontal = Alignment.Start,
+    children: @Composable ColumnScope.() -> Unit
+) {
+    RowColumnImpl(
+        orientation = LayoutOrientation.Vertical,
+        modifier = modifier,
+        arrangement = { totalSize, size, _ ->
+            verticalArrangement.arrange(totalSize, size)
+        },
+        crossAxisAlignment = CrossAxisAlignment.horizontal(horizontalGravity),
+        crossAxisSize = SizeMode.Wrap,
+        children = { ColumnScope.children() }
+    )
+}
+
+/**
+ * Scope for the children of [Column].
+ */
+@LayoutScopeMarker
+@Immutable
+object ColumnScope {
+    /**
+     * Position the element horizontally within the [Column] according to [align].
+     *
+     * Example usage:
+     * @sample androidx.ui.layout.samples.SimpleGravityInColumn
+     */
+    @Stable
+    fun Modifier.gravity(align: Alignment.Horizontal) = this + HorizontalGravityModifier(align)
+
+    /**
+     * Position the element horizontally such that its [alignmentLine] aligns with sibling elements
+     * also configured to [alignWithSiblings]. [alignWithSiblings] is a form of [gravity],
+     * so both modifiers will not work together if specified for the same layout.
+     * Within a [Column], all components with [alignWithSiblings] will align horizontally using
+     * the specified [VerticalAlignmentLine]s or values provided using the other
+     * [alignWithSiblings] overload, forming a sibling group.
+     * At least one element of the sibling group will be placed as it had [Alignment.Start] gravity
+     * in [Column], and the alignment of the other siblings will be then determined such that
+     * the alignment lines coincide. Note that if only one element in a [Column] has the
+     * [alignWithSiblings] modifier specified the element will be positioned
+     * as if it had [Alignment.Start] gravity.
+     *
+     * Example usage:
+     * @sample androidx.ui.layout.samples.SimpleRelativeToSiblingsInColumn
+     */
+    @Stable
+    fun Modifier.alignWithSiblings(alignmentLine: VerticalAlignmentLine) =
+        this + SiblingsAlignedModifier.WithAlignmentLine(alignmentLine)
+
+    /**
+     * Size the element's height proportional to its [weight] relative to other weighted sibling
+     * elements in the [Column]. The parent will divide the vertical space remaining after measuring
+     * unweighted child elements and distribute it according to this weight.
+     * When [fill] is true, the element will be forced to occupy the whole height allocated to it.
+     * Otherwise, the element is allowed to be smaller - this will result in [Column] being smaller,
+     * as the unused allocated height will not be redistributed to other siblings.
+     *
+     * @sample androidx.ui.layout.samples.SimpleColumn
+     */
+    @Stable
+    fun Modifier.weight(
+        @FloatRange(from = 0.0, to = 3.4e38 /* POSITIVE_INFINITY */, fromInclusive = false)
+        weight: Float,
+        fill: Boolean = true
+    ): Modifier {
+        require(weight > 0.0) { "invalid weight $weight; must be greater than zero" }
+        return this + LayoutWeightImpl(weight, fill)
+    }
+
+    /**
+     * Position the element horizontally such that the alignment line for the content as
+     * determined by [alignmentLineBlock] aligns with sibling elements also configured to
+     * [alignWithSiblings]. [alignWithSiblings] is a form of [gravity], so both modifiers
+     * will not work together if specified for the same layout.
+     * Within a [Column], all components with [alignWithSiblings] will align horizontally using
+     * the specified [VerticalAlignmentLine]s or values obtained from [alignmentLineBlock],
+     * forming a sibling group.
+     * At least one element of the sibling group will be placed as it had [Alignment.Start] gravity
+     * in [Column], and the alignment of the other siblings will be then determined such that
+     * the alignment lines coincide. Note that if only one element in a [Column] has the
+     * [alignWithSiblings] modifier specified the element will be positioned
+     * as if it had [Alignment.Start] gravity.
+     *
+     * Example usage:
+     * @sample androidx.ui.layout.samples.SimpleRelativeToSiblings
+     */
+    @Stable
+    fun Modifier.alignWithSiblings(
+        alignmentLineBlock: (Measured) -> Int
+    ) = this + SiblingsAlignedModifier.WithAlignmentLineBlock(alignmentLineBlock)
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/DpConstraints.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/DpConstraints.kt
new file mode 100644
index 0000000..fcdbe26
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/DpConstraints.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Immutable
+import androidx.compose.Stable
+import androidx.ui.core.Constraints
+import androidx.ui.unit.Density
+import androidx.ui.unit.Dp
+import androidx.ui.unit.coerceAtLeast
+import androidx.ui.unit.coerceIn
+import androidx.ui.unit.dp
+import androidx.ui.unit.isFinite
+
+/**
+ * Similar to [Constraints], but with constraint values expressed in [Dp].
+ */
+@Immutable
+data class DpConstraints(
+    @Stable
+    val minWidth: Dp = 0.dp,
+    @Stable
+    val maxWidth: Dp = Dp.Infinity,
+    @Stable
+    val minHeight: Dp = 0.dp,
+    @Stable
+    val maxHeight: Dp = Dp.Infinity
+) {
+    init {
+        require(minWidth.isFinite()) { "Constraints#minWidth should be finite" }
+        require(minHeight.isFinite()) { "Constraints#minHeight should be finite" }
+        require(!minWidth.value.isNaN()) { "Constraints#minWidth should not be NaN" }
+        require(!maxWidth.value.isNaN()) { "Constraints#maxWidth should not be NaN" }
+        require(!minHeight.value.isNaN()) { "Constraints#minHeight should not be NaN" }
+        require(!maxHeight.value.isNaN()) { "Constraints#maxHeight should not be NaN" }
+        require(minWidth <= maxWidth) {
+            "Constraints should be satisfiable, but minWidth > maxWidth"
+        }
+        require(minHeight <= maxHeight) {
+            "Constraints should be satisfiable, but minHeight > maxHeight"
+        }
+        require(minWidth >= 0.dp) { "Constraints#minWidth should be non-negative" }
+        require(maxWidth >= 0.dp) { "Constraints#maxWidth should be non-negative" }
+        require(minHeight >= 0.dp) { "Constraints#minHeight should be non-negative" }
+        require(maxHeight >= 0.dp) { "Constraints#maxHeight should be non-negative" }
+    }
+
+    companion object {
+        /**
+         * Creates constraints tight in both dimensions.
+         */
+        @Stable
+        fun fixed(width: Dp, height: Dp) = DpConstraints(width, width, height, height)
+
+        /**
+         * Creates constraints with tight width and loose height.
+         */
+        @Stable
+        fun fixedWidth(width: Dp) = DpConstraints(
+            minWidth = width,
+            maxWidth = width,
+            minHeight = 0.dp,
+            maxHeight = Dp.Infinity
+        )
+
+        /**
+         * Creates constraints with tight height and loose width.
+         */
+        @Stable
+        fun fixedHeight(height: Dp) = DpConstraints(
+            minWidth = 0.dp,
+            maxWidth = Dp.Infinity,
+            minHeight = height,
+            maxHeight = height
+        )
+    }
+}
+
+/**
+ * Whether or not the upper bound on the maximum height.
+ * @see hasBoundedWidth
+ */
+@Stable
+val DpConstraints.hasBoundedHeight get() = maxHeight.isFinite()
+
+/**
+ * Whether or not the upper bound on the maximum width.
+ * @see hasBoundedHeight
+ */
+@Stable
+val DpConstraints.hasBoundedWidth get() = maxWidth.isFinite()
+
+/**
+ * Whether there is exactly one width value that satisfies the constraints.
+ */
+@Stable
+val DpConstraints.hasFixedWidth get() = maxWidth == minWidth
+
+/**
+ * Whether there is exactly one height value that satisfies the constraints.
+ */
+@Stable
+val DpConstraints.hasFixedHeight get() = maxHeight == minHeight
+
+/**
+ * Whether the area of a component respecting these constraints will definitely be 0.
+ * This is true when at least one of maxWidth and maxHeight are 0.
+ */
+@Stable
+val DpConstraints.isZero get() = maxWidth == 0.dp || maxHeight == 0.dp
+
+/**
+ * Whether there is any size that satisfies the current constraints.
+ */
+@Stable
+val DpConstraints.satisfiable get() = minWidth <= maxWidth && minHeight <= maxHeight
+
+/**
+ * Returns the result of coercing the current constraints in a different set of constraints.
+ */
+@Stable
+fun DpConstraints.enforce(otherConstraints: DpConstraints) = DpConstraints(
+    minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
+    maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
+    minHeight = minHeight.coerceIn(otherConstraints.minHeight, otherConstraints.maxHeight),
+    maxHeight = maxHeight.coerceIn(otherConstraints.minHeight, otherConstraints.maxHeight)
+)
+
+/**
+ * Returns the DpConstraints obtained by offsetting the current instance with the given values.
+ */
+@Stable
+fun DpConstraints.offset(horizontal: Dp = 0.dp, vertical: Dp = 0.dp) = DpConstraints(
+    (minWidth + horizontal).coerceAtLeast(0.dp),
+    (maxWidth + horizontal).coerceAtLeast(0.dp),
+    (minHeight + vertical).coerceAtLeast(0.dp),
+    (maxHeight + vertical).coerceAtLeast(0.dp)
+)
+
+/**
+ * Creates the [Constraints] corresponding to the current [DpConstraints].
+ */
+@Stable
+fun Density.Constraints(dpConstraints: DpConstraints) = Constraints(
+    minWidth = dpConstraints.minWidth.toIntPx(),
+    maxWidth = dpConstraints.maxWidth.toIntPx(),
+    minHeight = dpConstraints.minHeight.toIntPx(),
+    maxHeight = dpConstraints.maxHeight.toIntPx()
+)
+
+/**
+ * Creates the [DpConstraints] corresponding to the current [Constraints].
+ */
+@Stable
+fun Density.DpConstraints(constraints: Constraints) = DpConstraints(
+    minWidth = constraints.minWidth.toDp(),
+    maxWidth = constraints.maxWidth.toDp(),
+    minHeight = constraints.minHeight.toDp(),
+    maxHeight = constraints.maxHeight.toDp()
+)
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/ExperimentalLayout.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/ExperimentalLayout.kt
new file mode 100644
index 0000000..853706a
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/ExperimentalLayout.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+@RequiresOptIn("The API of this layout is experimental and is likely to change in the future.")
+annotation class ExperimentalLayout
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Flow.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Flow.kt
new file mode 100644
index 0000000..437afd1
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Flow.kt
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Composable
+import androidx.ui.core.Alignment
+import androidx.ui.core.Constraints
+import androidx.ui.core.Layout
+import androidx.ui.core.Placeable
+import androidx.ui.unit.Dp
+import androidx.ui.unit.IntSize
+import androidx.ui.unit.dp
+import androidx.ui.util.fastForEachIndexed
+import kotlin.math.max
+
+/**
+ * A composable that places its children in a horizontal flow. Unlike [Row], if the
+ * horizontal space is too small to put all the children in one row, multiple rows may be used.
+ *
+ * Note that just like [Row], flex values cannot be used with [FlowRow].
+ *
+ * Example usage:
+ *
+ * @sample androidx.ui.layout.samples.SimpleFlowRow
+ *
+ * @param mainAxisSize The size of the layout in the main axis direction.
+ * @param mainAxisAlignment The alignment of each row's children in the main axis direction.
+ * @param mainAxisSpacing The main axis spacing between the children of each row.
+ * @param crossAxisAlignment The alignment of each row's children in the cross axis direction.
+ * @param crossAxisSpacing The cross axis spacing between the rows of the layout.
+ * @param lastLineMainAxisAlignment Overrides the main axis alignment of the last row.
+ */
+@ExperimentalLayout
+@Composable
+fun FlowRow(
+    mainAxisSize: SizeMode = SizeMode.Wrap,
+    mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
+    mainAxisSpacing: Dp = 0.dp,
+    crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
+    crossAxisSpacing: Dp = 0.dp,
+    lastLineMainAxisAlignment: FlowMainAxisAlignment = mainAxisAlignment,
+    children: @Composable () -> Unit
+) {
+    Flow(
+        orientation = LayoutOrientation.Horizontal,
+        mainAxisSize = mainAxisSize,
+        mainAxisAlignment = mainAxisAlignment,
+        mainAxisSpacing = mainAxisSpacing,
+        crossAxisAlignment = crossAxisAlignment,
+        crossAxisSpacing = crossAxisSpacing,
+        lastLineMainAxisAlignment = lastLineMainAxisAlignment,
+        children = children
+    )
+}
+
+/**
+ * A composable that places its children in a vertical flow. Unlike [Column], if the
+ * vertical space is too small to put all the children in one column, multiple columns may be used.
+ *
+ * Note that just like [Column], flex values cannot be used with [FlowColumn].
+ *
+ * Example usage:
+ *
+ * @sample androidx.ui.layout.samples.SimpleFlowColumn
+ *
+ * @param mainAxisSize The size of the layout in the main axis direction.
+ * @param mainAxisAlignment The alignment of each column's children in the main axis direction.
+ * @param mainAxisSpacing The main axis spacing between the children of each column.
+ * @param crossAxisAlignment The alignment of each column's children in the cross axis direction.
+ * @param crossAxisSpacing The cross axis spacing between the columns of the layout.
+ * @param lastLineMainAxisAlignment Overrides the main axis alignment of the last column.
+ */
+@ExperimentalLayout
+@Composable
+fun FlowColumn(
+    mainAxisSize: SizeMode = SizeMode.Wrap,
+    mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
+    mainAxisSpacing: Dp = 0.dp,
+    crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
+    crossAxisSpacing: Dp = 0.dp,
+    lastLineMainAxisAlignment: FlowMainAxisAlignment = mainAxisAlignment,
+    children: @Composable () -> Unit
+) {
+    Flow(
+        orientation = LayoutOrientation.Vertical,
+        mainAxisSize = mainAxisSize,
+        mainAxisAlignment = mainAxisAlignment,
+        mainAxisSpacing = mainAxisSpacing,
+        crossAxisAlignment = crossAxisAlignment,
+        crossAxisSpacing = crossAxisSpacing,
+        lastLineMainAxisAlignment = lastLineMainAxisAlignment,
+        children = children
+    )
+}
+
+/**
+ * Used to specify the alignment of a layout's children, in cross axis direction.
+ */
+enum class FlowCrossAxisAlignment {
+    /**
+     * Place children such that their center is in the middle of the cross axis.
+     */
+    Center,
+    /**
+     * Place children such that their start edge is aligned to the start edge of the cross axis.
+     */
+    Start,
+    /**
+     * Place children such that their end edge is aligned to the end edge of the cross axis.
+     */
+    End,
+}
+
+typealias FlowMainAxisAlignment = MainAxisAlignment
+
+/**
+ * Layout model that arranges its children in a horizontal or vertical flow.
+ */
+@Composable
+private fun Flow(
+    orientation: LayoutOrientation,
+    mainAxisSize: SizeMode,
+    mainAxisAlignment: FlowMainAxisAlignment,
+    mainAxisSpacing: Dp,
+    crossAxisAlignment: FlowCrossAxisAlignment,
+    crossAxisSpacing: Dp,
+    lastLineMainAxisAlignment: FlowMainAxisAlignment,
+    children: @Composable () -> Unit
+) {
+    fun Placeable.mainAxisSize() =
+        if (orientation == LayoutOrientation.Horizontal) width else height
+    fun Placeable.crossAxisSize() =
+        if (orientation == LayoutOrientation.Horizontal) height else width
+
+    Layout(children) { measurables, outerConstraints ->
+        val sequences = mutableListOf<List<Placeable>>()
+        val crossAxisSizes = mutableListOf<Int>()
+        val crossAxisPositions = mutableListOf<Int>()
+
+        var mainAxisSpace = 0
+        var crossAxisSpace = 0
+
+        val currentSequence = mutableListOf<Placeable>()
+        var currentMainAxisSize = 0
+        var currentCrossAxisSize = 0
+
+        val constraints = OrientationIndependentConstraints(outerConstraints, orientation)
+
+        val childConstraints = if (orientation == LayoutOrientation.Horizontal) {
+            Constraints(maxWidth = constraints.mainAxisMax)
+        } else {
+            Constraints(maxHeight = constraints.mainAxisMax)
+        }
+
+        // Return whether the placeable can be added to the current sequence.
+        fun canAddToCurrentSequence(placeable: Placeable) =
+            currentSequence.isEmpty() || currentMainAxisSize + mainAxisSpacing.toIntPx() +
+                    placeable.mainAxisSize() <= constraints.mainAxisMax
+
+        // Store current sequence information and start a new sequence.
+        fun startNewSequence() {
+            if (sequences.isNotEmpty()) {
+                crossAxisSpace += crossAxisSpacing.toIntPx()
+            }
+            sequences += currentSequence.toList()
+            crossAxisSizes += currentCrossAxisSize
+            crossAxisPositions += crossAxisSpace
+
+            crossAxisSpace += currentCrossAxisSize
+            mainAxisSpace = max(mainAxisSpace, currentMainAxisSize)
+
+            currentSequence.clear()
+            currentMainAxisSize = 0
+            currentCrossAxisSize = 0
+        }
+
+        for (measurable in measurables) {
+            // Ask the child for its preferred size.
+            val placeable = measurable.measure(childConstraints)
+
+            // Start a new sequence if there is not enough space.
+            if (!canAddToCurrentSequence(placeable)) startNewSequence()
+
+            // Add the child to the current sequence.
+            if (currentSequence.isNotEmpty()) {
+                currentMainAxisSize += mainAxisSpacing.toIntPx()
+            }
+            currentSequence.add(placeable)
+            currentMainAxisSize += placeable.mainAxisSize()
+            currentCrossAxisSize = max(currentCrossAxisSize, placeable.crossAxisSize())
+        }
+
+        if (currentSequence.isNotEmpty()) startNewSequence()
+
+        val mainAxisLayoutSize = if (constraints.mainAxisMax != Constraints.Infinity &&
+            mainAxisSize == SizeMode.Expand
+        ) {
+            constraints.mainAxisMax
+        } else {
+            max(mainAxisSpace, constraints.mainAxisMin)
+        }
+        val crossAxisLayoutSize = max(crossAxisSpace, constraints.crossAxisMin)
+
+        val layoutWidth = if (orientation == LayoutOrientation.Horizontal) {
+            mainAxisLayoutSize
+        } else {
+            crossAxisLayoutSize
+        }
+        val layoutHeight = if (orientation == LayoutOrientation.Horizontal) {
+            crossAxisLayoutSize
+        } else {
+            mainAxisLayoutSize
+        }
+
+        layout(layoutWidth, layoutHeight) {
+            sequences.fastForEachIndexed { i, placeables ->
+                val childrenMainAxisSizes = placeables.mapIndexed { j, placeable ->
+                    placeable.mainAxisSize() +
+                        if (j < placeables.lastIndex) mainAxisSpacing.toIntPx() else 0
+                }
+                val arrangement = if (i < sequences.lastIndex) {
+                    mainAxisAlignment.arrangement
+                } else {
+                    lastLineMainAxisAlignment.arrangement
+                }
+                // TODO(soboleva): rtl support
+                // Handle vertical direction
+                val mainAxisPositions = arrangement.arrange(
+                    mainAxisLayoutSize,
+                    childrenMainAxisSizes
+                )
+                placeables.fastForEachIndexed { j, placeable ->
+                    val crossAxis = when (crossAxisAlignment) {
+                        FlowCrossAxisAlignment.Start -> 0
+                        FlowCrossAxisAlignment.End ->
+                            crossAxisSizes[i] - placeable.crossAxisSize()
+                        FlowCrossAxisAlignment.Center ->
+                            Alignment.Center.align(
+                                IntSize(
+                                    width = 0,
+                                    height = crossAxisSizes[i] - placeable.crossAxisSize()
+                                )
+                            ).y
+                    }
+                    if (orientation == LayoutOrientation.Horizontal) {
+                        placeable.placeAbsolute(
+                            x = mainAxisPositions[j],
+                            y = crossAxisPositions[i] + crossAxis
+                        )
+                    } else {
+                        placeable.placeAbsolute(
+                            x = crossAxisPositions[i] + crossAxis,
+                            y = mainAxisPositions[j]
+                        )
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Intrinsic.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Intrinsic.kt
new file mode 100644
index 0000000..a16a59c
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Intrinsic.kt
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Composable
+import androidx.compose.Stable
+import androidx.ui.core.Constraints
+import androidx.ui.core.IntrinsicMeasurable
+import androidx.ui.core.IntrinsicMeasureScope
+import androidx.ui.core.Layout
+import androidx.ui.core.LayoutDirection
+import androidx.ui.core.LayoutModifier
+import androidx.ui.core.Measurable
+import androidx.ui.core.MeasureScope
+import androidx.ui.core.Modifier
+import androidx.ui.core.enforce
+import androidx.ui.unit.IntOffset
+/**
+ * Declare the preferred width of the content to be the same as the min or max intrinsic width of
+ * the content. The incoming measurement [Constraints] may override this value, forcing the content
+ * to be either smaller or larger.
+ *
+ * See [preferredHeight] for options of sizing to intrinsic height.
+ * Also see [preferredWidth] and [preferredWidthIn] for other options to set the preferred width.
+ *
+ * Example usage for min intrinsic:
+ * @sample androidx.ui.layout.samples.SameWidthBoxes
+ *
+ * Example usage for max intrinsic:
+ * @sample androidx.ui.layout.samples.SameWidthTextBoxes
+ */
+@ExperimentalLayout
+@Stable
+fun Modifier.preferredWidth(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {
+    IntrinsicSize.Min -> this + PreferredMinIntrinsicWidthModifier
+    IntrinsicSize.Max -> this + PreferredMaxIntrinsicWidthModifier
+}
+
+/**
+ * Declare the preferred height of the content to be the same as the min or max intrinsic height of
+ * the content. The incoming measurement [Constraints] may override this value, forcing the content
+ * to be either smaller or larger.
+ *
+ * See [preferredWidth] for other options of sizing to intrinsic width.
+ * Also see [preferredHeight] and [preferredHeightIn] for other options to set the preferred height.
+ *
+ * Example usage for min intrinsic:
+ * @sample androidx.ui.layout.samples.MatchParentDividerForText
+ *
+ * Example usage for max intrinsic:
+ * @sample androidx.ui.layout.samples.MatchParentDividerForAspectRatio
+ */
+@ExperimentalLayout
+@Stable
+fun Modifier.preferredHeight(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {
+    IntrinsicSize.Min -> this + PreferredMinIntrinsicHeightModifier
+    IntrinsicSize.Max -> this + PreferredMaxIntrinsicHeightModifier
+}
+
+/**
+ * Intrinsic size used in [preferredWidth] or [preferredHeight] which can refer to width or height.
+ */
+enum class IntrinsicSize { Min, Max }
+
+private object PreferredMinIntrinsicWidthModifier : PreferredIntrinsicSizeModifier {
+    override fun MeasureScope.calculateContentConstraints(
+        measurable: Measurable,
+        constraints: Constraints
+    ): Constraints {
+        val width = measurable.minIntrinsicWidth(constraints.maxHeight)
+        return Constraints.fixedWidth(width)
+    }
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicWidth(height)
+}
+
+private object PreferredMinIntrinsicHeightModifier : PreferredIntrinsicSizeModifier {
+    override fun MeasureScope.calculateContentConstraints(
+        measurable: Measurable,
+        constraints: Constraints
+    ): Constraints {
+        val height = measurable.minIntrinsicHeight(constraints.maxWidth)
+        return Constraints.fixedHeight(height)
+    }
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicHeight(width)
+}
+
+private object PreferredMaxIntrinsicWidthModifier : PreferredIntrinsicSizeModifier {
+    override fun MeasureScope.calculateContentConstraints(
+        measurable: Measurable,
+        constraints: Constraints
+    ): Constraints {
+        val width = measurable.maxIntrinsicWidth(constraints.maxHeight)
+        return Constraints.fixedWidth(width)
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicWidth(height, layoutDirection)
+}
+
+private object PreferredMaxIntrinsicHeightModifier : PreferredIntrinsicSizeModifier {
+    override fun MeasureScope.calculateContentConstraints(
+        measurable: Measurable,
+        constraints: Constraints
+    ): Constraints {
+        val height = measurable.maxIntrinsicHeight(constraints.maxWidth)
+        return Constraints.fixedHeight(height)
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicHeight(width)
+}
+
+private interface PreferredIntrinsicSizeModifier : LayoutModifier {
+    fun MeasureScope.calculateContentConstraints(
+        measurable: Measurable,
+        constraints: Constraints
+    ): Constraints
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val placeable = measurable.measure(
+            calculateContentConstraints(
+                measurable,
+                constraints
+            ).enforce(constraints)
+        )
+        return layout(placeable.width, placeable.height) {
+            placeable.place(IntOffset.Origin)
+        }
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicWidth(height, layoutDirection)
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicHeight(width, layoutDirection)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicWidth(height, layoutDirection)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicHeight(width, layoutDirection)
+}
+
+/**
+ * Layout composable that forces its child to be as wide as its min intrinsic width.
+ * If incoming constraints do not allow this, the closest possible width will be used.
+ */
+@Deprecated("This component is deprecated. " +
+        "Please use the preferredWidth(IntrinsicSize.Min) modifier instead.")
+@Composable
+fun MinIntrinsicWidth(children: @Composable () -> Unit) {
+    Layout(
+        children,
+        minIntrinsicWidthMeasureBlock = { measurables, h ->
+            measurables.firstOrNull()?.minIntrinsicWidth(h) ?: 0
+        },
+        minIntrinsicHeightMeasureBlock = { measurables, w ->
+            measurables.firstOrNull()?.minIntrinsicHeight(w) ?: 0
+        },
+        maxIntrinsicWidthMeasureBlock = { measurables, h ->
+            measurables.firstOrNull()?.minIntrinsicWidth(h) ?: 0
+        },
+        maxIntrinsicHeightMeasureBlock = { measurables, w ->
+            measurables.firstOrNull()?.maxIntrinsicHeight(w) ?: 0
+        }
+    ) { measurables, constraints ->
+        val measurable = measurables.firstOrNull()
+        val width = measurable?.minIntrinsicWidth(constraints.maxHeight, layoutDirection) ?: 0
+        val placeable = measurable?.measure(
+            Constraints.fixedWidth(width).enforce(constraints)
+        )
+        layout(placeable?.width ?: 0, placeable?.height ?: 0) {
+            placeable?.placeAbsolute(0, 0)
+        }
+    }
+}
+
+/**
+ * Layout composable that forces its child to be as tall as its min intrinsic height.
+ * If incoming constraints do not allow this, the closest possible height will be used.
+ */
+@Deprecated("This component is deprecated. " +
+        "Please use the preferredHeight(IntrinsicSize.Min) modifier instead.")
+@Composable
+fun MinIntrinsicHeight(children: @Composable () -> Unit) {
+    Layout(
+        children,
+        minIntrinsicWidthMeasureBlock = { measurables, h ->
+            measurables.firstOrNull()?.minIntrinsicWidth(h) ?: 0
+        },
+        minIntrinsicHeightMeasureBlock = { measurables, w ->
+            measurables.firstOrNull()?.minIntrinsicHeight(w) ?: 0
+        },
+        maxIntrinsicWidthMeasureBlock = { measurables, h ->
+            measurables.firstOrNull()?.maxIntrinsicWidth(h) ?: 0
+        },
+        maxIntrinsicHeightMeasureBlock = { measurables, w ->
+            measurables.firstOrNull()?.minIntrinsicHeight(w) ?: 0
+        }
+    ) { measurables, constraints ->
+        val measurable = measurables.firstOrNull()
+        val height = measurable?.minIntrinsicHeight(constraints.maxWidth) ?: 0
+        val placeable = measurable?.measure(
+            Constraints.fixedHeight(height).enforce(constraints)
+        )
+        layout(placeable?.width ?: 0, placeable?.height ?: 0) {
+            placeable?.placeAbsolute(0, 0)
+        }
+    }
+}
+
+/**
+ * Layout composable that forces its child to be as wide as its max intrinsic width.
+ * If incoming constraints do not allow this, the closest possible width will be used.
+ */
+@Deprecated("This component is deprecated. " +
+        "Please use the preferredWidth(IntrinsicSize.Max) modifier instead.")
+@Composable
+fun MaxIntrinsicWidth(children: @Composable () -> Unit) {
+    Layout(
+        children,
+        minIntrinsicWidthMeasureBlock = { measurables, h ->
+            measurables.firstOrNull()?.maxIntrinsicWidth(h) ?: 0
+        },
+        minIntrinsicHeightMeasureBlock = { measurables, w ->
+            measurables.firstOrNull()?.minIntrinsicHeight(w) ?: 0
+        },
+        maxIntrinsicWidthMeasureBlock = { measurables, h ->
+            measurables.firstOrNull()?.maxIntrinsicWidth(h) ?: 0
+        },
+        maxIntrinsicHeightMeasureBlock = { measurables, w ->
+            measurables.firstOrNull()?.maxIntrinsicHeight(w) ?: 0
+        }
+    ) { measurables, constraints ->
+        val measurable = measurables.firstOrNull()
+        val width = measurable?.maxIntrinsicWidth(constraints.maxHeight) ?: 0
+        val placeable = measurable?.measure(
+            Constraints.fixedWidth(width).enforce(constraints)
+        )
+        layout(placeable?.width ?: 0, placeable?.height ?: 0) {
+            placeable?.placeAbsolute(0, 0)
+        }
+    }
+}
+
+/**
+ * Layout composable that forces its child to be as tall as its max intrinsic height.
+ * If incoming constraints do not allow this, the closest possible height will be used.
+ */
+@Deprecated("This component is deprecated. " +
+        "Please use the preferredHeight(IntrinsicSize.Max) modifier instead.")
+@Composable
+fun MaxIntrinsicHeight(children: @Composable () -> Unit) {
+    Layout(
+        children,
+        minIntrinsicWidthMeasureBlock = { measurables, h ->
+            measurables.firstOrNull()?.minIntrinsicWidth(h) ?: 0
+        },
+        minIntrinsicHeightMeasureBlock = { measurables, w ->
+            measurables.firstOrNull()?.maxIntrinsicHeight(w) ?: 0
+        },
+        maxIntrinsicWidthMeasureBlock = { measurables, h ->
+            measurables.firstOrNull()?.maxIntrinsicWidth(h) ?: 0
+        },
+        maxIntrinsicHeightMeasureBlock = { measurables, w ->
+            measurables.firstOrNull()?.maxIntrinsicHeight(w) ?: 0
+        }
+    ) { measurables, constraints ->
+        val measurable = measurables.firstOrNull()
+        val height = measurable?.maxIntrinsicHeight(constraints.maxWidth) ?: 0
+        val placeable = measurable?.measure(
+            Constraints.fixedHeight(height).enforce(constraints)
+        )
+        layout(placeable?.width ?: 0, placeable?.height ?: 0) {
+            placeable?.placeAbsolute(0, 0)
+        }
+    }
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutAspectRatio.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutAspectRatio.kt
new file mode 100644
index 0000000..4c891a2
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutAspectRatio.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Stable
+import androidx.ui.core.Constraints
+import androidx.ui.core.IntrinsicMeasurable
+import androidx.ui.core.IntrinsicMeasureScope
+import androidx.ui.core.LayoutDirection
+import androidx.ui.core.LayoutModifier
+import androidx.ui.core.Measurable
+import androidx.ui.core.MeasureScope
+import androidx.ui.core.Modifier
+import androidx.ui.core.satisfiedBy
+import androidx.ui.unit.IntSize
+import androidx.ui.util.annotation.FloatRange
+import kotlin.math.roundToInt
+
+/**
+ * Attempts to size the content to match a specified aspect ratio by trying to match one of the
+ * incoming constraints in the following order:
+ * [Constraints.maxWidth], [Constraints.maxHeight], [Constraints.minWidth], [Constraints.minHeight].
+ * The size in the other dimension is determined by the aspect ratio.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleAspectRatio
+ *
+ * @param ratio the desired width/height positive ratio
+ */
+@Stable
+fun Modifier.aspectRatio(
+    @FloatRange(from = 0.0, to = 3.4e38 /* POSITIVE_INFINITY */, fromInclusive = false)
+    ratio: Float
+) = this + AspectRatioModifier(ratio)
+
+private data class AspectRatioModifier(val aspectRatio: Float) : LayoutModifier {
+    init {
+        require(aspectRatio > 0) { "aspectRatio $aspectRatio must be > 0" }
+    }
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val size = constraints.findSizeWith(aspectRatio)
+        val wrappedConstraints = if (size != null) {
+            Constraints.fixed(size.width, size.height)
+        } else {
+            constraints
+        }
+        val placeable = measurable.measure(wrappedConstraints)
+        return layout(placeable.width, placeable.height) {
+            placeable.place(0, 0)
+        }
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = if (height != Constraints.Infinity) {
+        (height * aspectRatio).roundToInt()
+    } else {
+        measurable.minIntrinsicWidth(height)
+    }
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = if (height != Constraints.Infinity) {
+        (height * aspectRatio).roundToInt()
+    } else {
+        measurable.maxIntrinsicWidth(height)
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = if (width != Constraints.Infinity) {
+        (width / aspectRatio).roundToInt()
+    } else {
+        measurable.minIntrinsicHeight(width)
+    }
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = if (width != Constraints.Infinity) {
+        (width / aspectRatio).roundToInt()
+    } else {
+        measurable.maxIntrinsicHeight(width)
+    }
+
+    private fun Constraints.findSizeWith(aspectRatio: Float): IntSize? {
+        val maxWidth = this.maxWidth
+        if (maxWidth != Constraints.Infinity) {
+            val height = (maxWidth / aspectRatio).roundToInt()
+            if (height > 0) {
+                val size = IntSize(maxWidth, height)
+                if (satisfiedBy(size)) {
+                    return size
+                }
+            }
+        }
+        val maxHeight = this.maxHeight
+        if (maxHeight != Constraints.Infinity) {
+            val width = (maxHeight * aspectRatio).roundToInt()
+            if (width > 0) {
+                val size = IntSize(width, maxHeight)
+                if (satisfiedBy(size)) {
+                    return size
+                }
+            }
+        }
+        val minWidth = this.minWidth
+        val height = (minWidth / aspectRatio).roundToInt()
+        if (height > 0) {
+            val size = IntSize(minWidth, height)
+            if (satisfiedBy(size)) {
+                return size
+            }
+        }
+        val minHeight = this.minHeight
+        val width = (minHeight * aspectRatio).roundToInt()
+        if (width > 0) {
+            val size = IntSize(width, minHeight)
+            if (satisfiedBy(size)) {
+                return size
+            }
+        }
+        return null
+    }
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutDirections.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutDirections.kt
new file mode 100644
index 0000000..5253a30
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutDirections.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.ui.core.Constraints
+import androidx.ui.core.IntrinsicMeasurable
+import androidx.ui.core.IntrinsicMeasureScope
+import androidx.ui.core.LayoutDirection
+import androidx.ui.core.LayoutModifier
+import androidx.ui.core.Measurable
+import androidx.ui.core.MeasureScope
+import androidx.ui.core.Modifier
+/**
+ * [Modifier] that changes the [LayoutDirection] of the wrapped layout to [LayoutDirection.Ltr].
+ */
+val Modifier.ltr: Modifier get() = this + LtrModifier
+
+/**
+ * [Modifier] that changes the [LayoutDirection] of the wrapped layout to [LayoutDirection.Rtl].
+ */
+val Modifier.rtl: Modifier get() = this + RtlModifier
+
+private val LtrModifier = LayoutDirectionModifier(LayoutDirection.Ltr)
+
+private val RtlModifier = LayoutDirectionModifier(LayoutDirection.Rtl)
+
+private data class LayoutDirectionModifier(
+    val prescribedLayoutDirection: LayoutDirection
+) : LayoutModifier {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val placeable = measurable.measure(constraints, prescribedLayoutDirection)
+        return layout(placeable.width, placeable.height) {
+            placeable.place(0, 0)
+        }
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicWidth(height, prescribedLayoutDirection)
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicHeight(width, prescribedLayoutDirection)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicWidth(height, prescribedLayoutDirection)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicHeight(width, prescribedLayoutDirection)
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutOffset.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutOffset.kt
new file mode 100644
index 0000000..96a7168
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutOffset.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Stable
+import androidx.compose.State
+import androidx.compose.mutableStateOf
+import androidx.ui.core.Constraints
+import androidx.ui.core.LayoutDirection
+import androidx.ui.core.LayoutModifier
+import androidx.ui.core.Measurable
+import androidx.ui.core.MeasureScope
+import androidx.ui.core.Modifier
+import androidx.ui.unit.Dp
+import androidx.ui.unit.dp
+import kotlin.math.roundToInt
+
+/**
+ * Offset the content by ([x] dp, [y] dp). The offsets can be positive as well as non positive.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.LayoutOffsetModifier
+ */
+@Stable
+fun Modifier.offset(x: Dp = 0.dp, y: Dp = 0.dp) = this + OffsetModifier(x, y)
+
+/**
+ * Offset the content by ([x] px, [y]px). The offsets can be positive as well as non positive.
+ * This modifier is designed to be used for offsets that change, possibly due to user interactions.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.LayoutOffsetPxModifier
+ */
+fun Modifier.offsetPx(
+    x: State<Float> = mutableStateOf(0f),
+    y: State<Float> = mutableStateOf(0f)
+) = this + OffsetPxModifier(x, y)
+
+private data class OffsetModifier(val x: Dp, val y: Dp) : LayoutModifier {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val placeable = measurable.measure(constraints)
+        return layout(placeable.width, placeable.height) {
+            placeable.place(x.toIntPx(), y.toIntPx())
+        }
+    }
+}
+
+private data class OffsetPxModifier(val x: State<Float>, val y: State<Float>) : LayoutModifier {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val placeable = measurable.measure(constraints)
+        return layout(placeable.width, placeable.height) {
+            placeable.place(x.value.roundToInt(), y.value.roundToInt())
+        }
+    }
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutPadding.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutPadding.kt
new file mode 100644
index 0000000..54762d8
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutPadding.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Immutable
+import androidx.compose.Stable
+import androidx.ui.core.Constraints
+import androidx.ui.core.LayoutDirection
+import androidx.ui.core.Modifier
+import androidx.ui.core.LayoutModifier
+import androidx.ui.core.Measurable
+import androidx.ui.core.MeasureScope
+import androidx.ui.core.constrainHeight
+import androidx.ui.core.constrainWidth
+import androidx.ui.core.offset
+import androidx.ui.unit.Dp
+import androidx.ui.unit.dp
+
+/**
+ * Apply additional space along each edge of the content in [Dp]: [start], [top], [end] and
+ * [bottom]. The start and end edges will be determined by the current [LayoutDirection].
+ * Padding is applied before content measurement and takes precedence; content may only be as large
+ * as the remaining space.
+ *
+ * Negative padding is not permitted. See [offset].
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.PaddingModifier
+ */
+@Stable
+fun Modifier.padding(
+    start: Dp = 0.dp,
+    top: Dp = 0.dp,
+    end: Dp = 0.dp,
+    bottom: Dp = 0.dp
+) = this + PaddingModifier(
+    start = start,
+    top = top,
+    end = end,
+    bottom = bottom,
+    rtlAware = true
+)
+
+/**
+ * Apply [horizontal] dp space along the left and right edges of the content, and [vertical] dp
+ * space along the top and bottom edges.
+ * Padding is applied before content measurement and takes precedence; content may only be as large
+ * as the remaining space.
+ *
+ * Negative padding is not permitted. See [offset].
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SymmetricPaddingModifier
+ */
+@Stable
+fun Modifier.padding(
+    horizontal: Dp = 0.dp,
+    vertical: Dp = 0.dp
+) = this + PaddingModifier(
+    start = horizontal,
+    top = vertical,
+    end = horizontal,
+    bottom = vertical,
+    rtlAware = true
+)
+
+/**
+ * Apply [all] dp of additional space along each edge of the content, left, top, right and bottom.
+ * Padding is applied before content measurement and takes precedence; content may only be as large
+ * as the remaining space.
+ *
+ * Negative padding is not permitted. See [offset].
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.PaddingAllModifier
+ */
+@Stable
+fun Modifier.padding(all: Dp) =
+    this + PaddingModifier(start = all, top = all, end = all, bottom = all, rtlAware = true)
+
+/**
+ * Apply [InnerPadding] to the component as additional space along each edge of the content's left,
+ * top, right and bottom. Padding is applied before content measurement and takes precedence;
+ * content may only be as large as the remaining space.
+ *
+ * Negative padding is not permitted. See [offset].
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.PaddingInnerPaddingModifier
+ */
+fun Modifier.padding(padding: InnerPadding) =
+    this + PaddingModifier(
+        start = padding.start,
+        top = padding.top,
+        end = padding.end,
+        bottom = padding.bottom,
+        rtlAware = true
+    )
+
+/**
+ * Apply additional space along each edge of the content in [Dp]: [left], [top], [right] and
+ * [bottom]. These paddings are applied without regard to the current [LayoutDirection], see
+ * [padding] to apply relative paddings. Padding is applied before content measurement and takes
+ * precedence; content may only be as large as the remaining space.
+ *
+ * Negative padding is not permitted. See [offset].
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.AbsolutePaddingModifier
+ */
+@Stable
+fun Modifier.absolutePadding(
+    left: Dp = 0.dp,
+    top: Dp = 0.dp,
+    right: Dp = 0.dp,
+    bottom: Dp = 0.dp
+) = this + PaddingModifier(
+    start = left,
+    top = top,
+    end = right,
+    bottom = bottom,
+    rtlAware = false
+)
+
+private data class PaddingModifier(
+    val start: Dp = 0.dp,
+    val top: Dp = 0.dp,
+    val end: Dp = 0.dp,
+    val bottom: Dp = 0.dp,
+    val rtlAware: Boolean
+) : LayoutModifier {
+    init {
+        require(start.value >= 0f && top.value >= 0f && end.value >= 0f && bottom.value >= 0f) {
+            "Padding must be non-negative"
+        }
+    }
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val horizontal = start.toIntPx() + end.toIntPx()
+        val vertical = top.toIntPx() + bottom.toIntPx()
+
+        val placeable = measurable.measure(constraints.offset(-horizontal, -vertical))
+
+        val width = constraints.constrainWidth(placeable.width + horizontal)
+        val height = constraints.constrainHeight(placeable.height + vertical)
+        return layout(width, height) {
+            if (rtlAware) {
+                placeable.place(start.toIntPx(), top.toIntPx())
+            } else {
+                placeable.placeAbsolute(start.toIntPx(), top.toIntPx())
+            }
+        }
+    }
+}
+
+/**
+ * Describes a padding to be applied along the edges inside a box.
+ */
+@Immutable
+data class InnerPadding(
+    @Stable
+    val start: Dp = 0.dp,
+    @Stable
+    val top: Dp = 0.dp,
+    @Stable
+    val end: Dp = 0.dp,
+    @Stable
+    val bottom: Dp = 0.dp
+) {
+    constructor(all: Dp) : this(all, all, all, all)
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutScopeMarker.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutScopeMarker.kt
new file mode 100644
index 0000000..09aac5a
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutScopeMarker.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+@DslMarker
+annotation class LayoutScopeMarker
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutSize.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutSize.kt
new file mode 100644
index 0000000..21e2b15
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/LayoutSize.kt
@@ -0,0 +1,605 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Stable
+import androidx.ui.core.Alignment
+import androidx.ui.core.Constraints
+import androidx.ui.core.IntrinsicMeasurable
+import androidx.ui.core.IntrinsicMeasureScope
+import androidx.ui.core.LayoutDirection
+import androidx.ui.core.LayoutModifier
+import androidx.ui.core.Measurable
+import androidx.ui.core.MeasureScope
+import androidx.ui.core.Modifier
+import androidx.ui.core.constrainHeight
+import androidx.ui.core.constrainWidth
+import androidx.ui.core.enforce
+import androidx.ui.unit.Density
+import androidx.ui.unit.Dp
+import androidx.ui.unit.IntOffset
+import androidx.ui.unit.IntSize
+import kotlin.math.max
+
+/**
+ * Declare the preferred width of the content to be exactly [width]dp. The incoming measurement
+ * [Constraints] may override this value, forcing the content to be either smaller or larger.
+ *
+ * For a modifier that sets the width of the content regardless of the incoming constraints see
+ * [Modifier.width]. See [preferredHeight] or [preferredSize] to set other preferred dimensions.
+ * See [preferredWidthIn], [preferredHeightIn] or [preferredSizeIn] to set a preferred size range.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimplePreferredWidthModifier
+ */
+@Stable
+fun Modifier.preferredWidth(width: Dp) = preferredSizeIn(minWidth = width, maxWidth = width)
+
+/**
+ * Declare the preferred height of the content to be exactly [height]dp. The incoming measurement
+ * [Constraints] may override this value, forcing the content to be either smaller or larger.
+ *
+ * For a modifier that sets the height of the content regardless of the incoming constraints see
+ * [Modifier.height]. See [preferredWidth] or [preferredSize] to set other preferred dimensions.
+ * See [preferredWidthIn], [preferredHeightIn] or [preferredSizeIn] to set a preferred size range.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimplePreferredHeightModifier
+ */
+@Stable
+fun Modifier.preferredHeight(height: Dp) = preferredSizeIn(minHeight = height, maxHeight = height)
+
+/**
+ * Declare the preferred size of the content to be exactly [size]dp square. The incoming measurement
+ * [Constraints] may override this value, forcing the content to be either smaller or larger.
+ *
+ * For a modifier that sets the size of the content regardless of the incoming constraints, see
+ * [Modifier.size]. See [preferredWidth] or [preferredHeight] to set width or height alone.
+ * See [preferredWidthIn], [preferredHeightIn] or [preferredSizeIn] to set a preferred size range.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimplePreferredSizeModifier
+ */
+@Stable
+fun Modifier.preferredSize(size: Dp) = preferredSizeIn(size, size, size, size)
+
+/**
+ * Declare the preferred size of the content to be exactly [width]dp by [height]dp. The incoming
+ * measurement [Constraints] may override this value, forcing the content to be either smaller or
+ * larger.
+ *
+ * For a modifier that sets the size of the content regardless of the incoming constraints, see
+ * [Modifier.size]. See [preferredWidth] or [preferredHeight] to set width or height alone.
+ * See [preferredWidthIn], [preferredHeightIn] or [preferredSizeIn] to set a preferred size range.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimplePreferredSizeModifier
+ */
+@Stable
+fun Modifier.preferredSize(width: Dp, height: Dp) = preferredSizeIn(
+    minWidth = width,
+    maxWidth = width,
+    minHeight = height,
+    maxHeight = height
+)
+
+/**
+ * Constrain the width of the content to be between [minWidth]dp and [maxWidth]dp as permitted
+ * by the incoming measurement [Constraints]. If the incoming constraints are more restrictive
+ * the requested size will obey the incoming constraints and attempt to be as close as possible
+ * to the preferred size.
+ */
+@Stable
+fun Modifier.preferredWidthIn(
+    minWidth: Dp = Dp.Unspecified,
+    maxWidth: Dp = Dp.Unspecified
+) = preferredSizeIn(minWidth = minWidth, maxWidth = maxWidth)
+
+/**
+ * Constrain the height of the content to be between [minHeight]dp and [maxHeight]dp as permitted
+ * by the incoming measurement [Constraints]. If the incoming constraints are more restrictive
+ * the requested size will obey the incoming constraints and attempt to be as close as possible
+ * to the preferred size.
+ */
+@Stable
+fun Modifier.preferredHeightIn(
+    minHeight: Dp = Dp.Unspecified,
+    maxHeight: Dp = Dp.Unspecified
+) = preferredSizeIn(minHeight = minHeight, maxHeight = maxHeight)
+
+/**
+ * Constrain the size of the content to be within [constraints] as permitted by the incoming
+ * measurement [Constraints]. If the incoming measurement constraints are more restrictive the
+ * requested size will obey the incoming constraints and attempt to be as close as possible to
+ * the preferred size.
+ */
+@Stable
+fun Modifier.preferredSizeIn(constraints: DpConstraints) = preferredSizeIn(
+    constraints.minWidth,
+    constraints.minHeight,
+    constraints.maxWidth,
+    constraints.maxHeight
+)
+
+/**
+ * Constrain the width of the content to be between [minWidth]dp and [maxWidth]dp and the height
+ * of the content to be between [minHeight] and [maxHeight] as permitted by the incoming
+ * measurement [Constraints]. If the incoming constraints are more restrictive the requested size
+ * will obey the incoming constraints and attempt to be as close as possible to the preferred size.
+ */
+@Stable
+fun Modifier.preferredSizeIn(
+    minWidth: Dp = Dp.Unspecified,
+    minHeight: Dp = Dp.Unspecified,
+    maxWidth: Dp = Dp.Unspecified,
+    maxHeight: Dp = Dp.Unspecified
+) = this + SizeModifier(minWidth, minHeight, maxWidth, maxHeight, true)
+
+/**
+ * Declare the width of the content to be exactly [width]dp. The incoming measurement
+ * [Constraints] will not override this value. If the content chooses a size that does not
+ * satisfy the incoming [Constraints], the parent layout will be reported a size coerced
+ * in the [Constraints], and the position of the content will be automatically offset to be
+ * centered on the space assigned to the child by the parent layout under the assumption that
+ * [Constraints] were respected.
+ *
+ * See [widthIn] and [sizeIn] to set a size range.
+ * See [preferredWidth] to set a preferred width, which is only respected when the incoming
+ * constraints allow it.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleWidthModifier
+ */
+@Stable
+fun Modifier.width(width: Dp) = sizeIn(minWidth = width, maxWidth = width)
+
+/**
+ * Declare the height of the content to be exactly [height]dp. The incoming measurement
+ * [Constraints] will not override this value. If the content chooses a size that does not
+ * satisfy the incoming [Constraints], the parent layout will be reported a size coerced
+ * in the [Constraints], and the position of the content will be automatically offset to be
+ * centered on the space assigned to the child by the parent layout under the assumption that
+ * [Constraints] were respected.
+ *
+ * See [heightIn] and [sizeIn] to set a size range.
+ * See [preferredHeight] to set a preferred height, which is only respected when the incoming
+ * constraints allow it.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleHeightModifier
+ */
+@Stable
+fun Modifier.height(height: Dp) = sizeIn(minHeight = height, maxHeight = height)
+
+/**
+ * Declare the size of the content to be exactly [size]dp width and height. The incoming measurement
+ * [Constraints] will not override this value. If the content chooses a size that does not
+ * satisfy the incoming [Constraints], the parent layout will be reported a size coerced
+ * in the [Constraints], and the position of the content will be automatically offset to be
+ * centered on the space assigned to the child by the parent layout under the assumption that
+ * [Constraints] were respected.
+ *
+ * See [sizeIn] to set a size range.
+ * See [preferredSize] to set a preferred size, which is only respected when the incoming
+ * constraints allow it.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleSizeModifier
+ */
+@Stable
+fun Modifier.size(size: Dp) = sizeIn(size, size, size, size)
+
+/**
+ * Declare the size of the content to be exactly [width]dp and [height]dp. The incoming measurement
+ * [Constraints] will not override this value. If the content chooses a size that does not
+ * satisfy the incoming [Constraints], the parent layout will be reported a size coerced
+ * in the [Constraints], and the position of the content will be automatically offset to be
+ * centered on the space assigned to the child by the parent layout under the assumption that
+ * [Constraints] were respected.
+ *
+ * See [sizeIn] to set a size range.
+ * See [preferredSize] to set a preferred size, which is only respected when the incoming
+ * constraints allow it.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleWidthModifier
+ */
+@Stable
+fun Modifier.size(width: Dp, height: Dp) = sizeIn(
+    minWidth = width,
+    maxWidth = width,
+    minHeight = height,
+    maxHeight = height
+)
+
+/**
+ * Constrain the width of the content to be between [minWidth]dp and [maxWidth]dp.
+ * If the content chooses a size that does not satisfy the incoming [Constraints], the
+ * parent layout will be reported a size coerced in the [Constraints], and the position
+ * of the content will be automatically offset to be centered on the space assigned to
+ * the child by the parent layout under the assumption that [Constraints] were respected.
+ */
+@Stable
+fun Modifier.widthIn(
+    minWidth: Dp = Dp.Unspecified,
+    maxWidth: Dp = Dp.Unspecified
+) = sizeIn(minWidth = minWidth, maxWidth = maxWidth)
+
+/**
+ * Constrain the height of the content to be between [minHeight]dp and [maxHeight]dp.
+ * If the content chooses a size that does not satisfy the incoming [Constraints], the
+ * parent layout will be reported a size coerced in the [Constraints], and the position
+ * of the content will be automatically offset to be centered on the space assigned to
+ * the child by the parent layout under the assumption that [Constraints] were respected.
+ */
+@Stable
+fun Modifier.heightIn(
+    minHeight: Dp = Dp.Unspecified,
+    maxHeight: Dp = Dp.Unspecified
+) = sizeIn(minHeight = minHeight, maxHeight = maxHeight)
+
+/**
+ * Constrain the size of the content to be within [constraints].
+ * If the content chooses a size that does not satisfy the incoming [Constraints], the
+ * parent layout will be reported a size coerced in the [Constraints], and the position
+ * of the content will be automatically offset to be centered on the space assigned to
+ * the child by the parent layout under the assumption that [Constraints] were respected.
+ */
+@Stable
+fun Modifier.sizeIn(constraints: DpConstraints) = sizeIn(
+    constraints.minWidth,
+    constraints.minHeight,
+    constraints.maxWidth,
+    constraints.maxHeight
+)
+
+/**
+ * Constrain the width of the content to be between [minWidth]dp and [maxWidth]dp, and the
+ * height of the content to be between [minHeight]dp and [maxHeight]dp.
+ * If the content chooses a size that does not satisfy the incoming [Constraints], the
+ * parent layout will be reported a size coerced in the [Constraints], and the position
+ * of the content will be automatically offset to be centered on the space assigned to
+ * the child by the parent layout under the assumption that [Constraints] were respected.
+ */
+@Stable
+fun Modifier.sizeIn(
+    minWidth: Dp = Dp.Unspecified,
+    minHeight: Dp = Dp.Unspecified,
+    maxWidth: Dp = Dp.Unspecified,
+    maxHeight: Dp = Dp.Unspecified
+) = this + SizeModifier(minWidth, minHeight, maxWidth, maxHeight, false)
+
+/**
+ * Have the content fill the [Constraints.maxWidth] of the incoming measurement constraints
+ * by setting the [minimum width][Constraints.minWidth] to be equal to the
+ * [maximum width][Constraints.maxWidth]. If the incoming maximum width is [Constraints.Infinity] this
+ * modifier will have no effect.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleFillWidthModifier
+ */
+@Stable
+fun Modifier.fillMaxWidth() = this + FillModifier(Direction.Horizontal)
+
+/**
+ * Have the content fill the [Constraints.maxHeight] of the incoming measurement constraints
+ * by setting the [minimum height][Constraints.minHeight] to be equal to the
+ * [maximum height][Constraints.maxHeight]. If the incoming maximum height is [Constraints.Infinity] this
+ * modifier will have no effect.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleFillHeightModifier
+ */
+@Stable
+fun Modifier.fillMaxHeight() = this + FillModifier(Direction.Vertical)
+
+/**
+ * Have the content fill the [Constraints.maxWidth] and [Constraints.maxHeight] of the incoming
+ * measurement constraints by setting the [minimum width][Constraints.minWidth] to be equal to the
+ * [maximum width][Constraints.maxWidth] and the [minimum height][Constraints.minHeight] to be
+ * equal to the [maximum height][Constraints.maxHeight]. If the incoming maximum width or height
+ * is [Constraints.Infinity] this modifier will have no effect in that dimension.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleFillModifier
+ */
+@Stable
+fun Modifier.fillMaxSize() = this + FillModifier(Direction.Both)
+
+/**
+ * Allow the content to measure at its desired width without regard for the incoming measurement
+ * [minimum width constraint][Constraints.minWidth]. If the content's measured size is smaller
+ * than the minimum width constraint, [align] it within that minimum width space.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleWrapContentHorizontallyAlignedModifier
+ */
+@Stable
+// TODO(popam): avoid recreating modifier for common align
+fun Modifier.wrapContentWidth(align: Alignment.Horizontal = Alignment.CenterHorizontally) = this +
+        AlignmentModifier(Direction.Horizontal) { size, layoutDirection ->
+            IntOffset(align.align(size.width, layoutDirection), 0)
+        }
+
+/**
+ * Allow the content to measure at its desired height without regard for the incoming measurement
+ * [minimum height constraint][Constraints.minHeight]. If the content's measured size is smaller
+ * than the minimum height constraint, [align] it within that minimum height space.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleWrapContentVerticallyAlignedModifier
+ */
+// TODO(popam): avoid recreating modifier for common align
+@Stable
+fun Modifier.wrapContentHeight(align: Alignment.Vertical = Alignment.CenterVertically) =
+    this + AlignmentModifier(Direction.Vertical) { size, _ ->
+        IntOffset(0, align.align(size.height))
+    }
+
+/**
+ * Allow the content to measure at its desired size without regard for the incoming measurement
+ * [minimum width][Constraints.minWidth] or [minimum height][Constraints.minHeight] constraints.
+ * If the content's measured size is smaller than the minimum size constraint, [align] it
+ * within that minimum sized space.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.SimpleWrapContentAlignedModifier
+ */
+@Stable
+fun Modifier.wrapContentSize(align: Alignment = Alignment.Center) =
+    this + AlignmentModifier(Direction.Both) { size, layoutDirection ->
+        align.align(size, layoutDirection)
+    }
+
+/**
+ * Constrain the size of the wrapped layout only when it would be otherwise unconstrained:
+ * the [minWidth] and [minHeight] constraints are only applied when the incoming corresponding
+ * constraint is `0`.
+ * The modifier can be used, for example, to define a default min size of a component,
+ * while still allowing it to be overidden with smaller min sizes across usages.
+ *
+ * Example usage:
+ * @sample androidx.ui.layout.samples.DefaultMinSizeConstraintsSample
+ */
+@Stable
+fun Modifier.defaultMinSizeConstraints(
+    minWidth: Dp = Dp.Unspecified,
+    minHeight: Dp = Dp.Unspecified
+) = this + UnspecifiedConstraintsModifier(minWidth, minHeight)
+
+private data class FillModifier(private val direction: Direction) : LayoutModifier {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val wrappedConstraints = Constraints(
+            minWidth = if (constraints.hasBoundedWidth && direction != Direction.Vertical) {
+                constraints.maxWidth
+            } else {
+                constraints.minWidth
+            },
+            maxWidth = constraints.maxWidth,
+            minHeight = if (constraints.hasBoundedHeight && direction != Direction.Horizontal) {
+                constraints.maxHeight
+            } else {
+                constraints.minHeight
+            },
+            maxHeight = constraints.maxHeight
+        )
+        val placeable = measurable.measure(wrappedConstraints)
+
+        return layout(placeable.width, placeable.height) {
+            placeable.place(0, 0)
+        }
+    }
+}
+
+private data class SizeModifier(
+    private val minWidth: Dp = Dp.Unspecified,
+    private val minHeight: Dp = Dp.Unspecified,
+    private val maxWidth: Dp = Dp.Unspecified,
+    private val maxHeight: Dp = Dp.Unspecified,
+    private val enforceIncoming: Boolean
+) : LayoutModifier {
+    private val Density.targetConstraints
+        get() = Constraints(
+            minWidth = if (minWidth != Dp.Unspecified) minWidth.toIntPx() else 0,
+            minHeight = if (minHeight != Dp.Unspecified) minHeight.toIntPx() else 0,
+            maxWidth = if (maxWidth != Dp.Unspecified) maxWidth.toIntPx() else Constraints.Infinity,
+            maxHeight = if (maxHeight != Dp.Unspecified) {
+                maxHeight.toIntPx()
+            } else {
+                Constraints.Infinity
+            }
+        )
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val wrappedConstraints = targetConstraints.let { targetConstraints ->
+            if (enforceIncoming) {
+                targetConstraints.enforce(constraints)
+            } else {
+                val resolvedMinWidth = if (minWidth != Dp.Unspecified) {
+                    targetConstraints.minWidth
+                } else {
+                    constraints.minWidth.coerceAtMost(targetConstraints.maxWidth)
+                }
+                val resolvedMaxWidth = if (maxWidth != Dp.Unspecified) {
+                    targetConstraints.maxWidth
+                } else {
+                    constraints.maxWidth.coerceAtLeast(targetConstraints.minWidth)
+                }
+                val resolvedMinHeight = if (minHeight != Dp.Unspecified) {
+                    targetConstraints.minHeight
+                } else {
+                    constraints.minHeight.coerceAtMost(targetConstraints.maxHeight)
+                }
+                val resolvedMaxHeight = if (maxHeight != Dp.Unspecified) {
+                    targetConstraints.maxHeight
+                } else {
+                    constraints.maxHeight.coerceAtLeast(targetConstraints.minHeight)
+                }
+                Constraints(
+                    resolvedMinWidth,
+                    resolvedMaxWidth,
+                    resolvedMinHeight,
+                    resolvedMaxHeight
+                )
+            }
+        }
+        val placeable = measurable.measure(wrappedConstraints)
+        return layout(placeable.width, placeable.height) {
+            placeable.place(0, 0)
+        }
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicWidth(height, layoutDirection).let {
+        val constraints = targetConstraints
+        constraints.constrainWidth(it)
+    }
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicWidth(height, layoutDirection).let {
+        val constraints = targetConstraints
+        constraints.constrainWidth(it)
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicHeight(width, layoutDirection).let {
+        val constraints = targetConstraints
+        constraints.constrainHeight(it)
+    }
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicHeight(width, layoutDirection).let {
+        val constraints = targetConstraints
+        constraints.constrainHeight(it)
+    }
+}
+
+private data class AlignmentModifier(
+    private val direction: Direction,
+    private val alignmentCallback: (IntSize, LayoutDirection) -> IntOffset
+) : LayoutModifier {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val wrappedConstraints = when (direction) {
+            Direction.Both -> constraints.copy(minWidth = 0, minHeight = 0)
+            Direction.Horizontal -> constraints.copy(minWidth = 0)
+            Direction.Vertical -> constraints.copy(minHeight = 0)
+        }
+        val placeable = measurable.measure(wrappedConstraints)
+        val wrapperWidth = max(constraints.minWidth, placeable.width)
+        val wrapperHeight = max(constraints.minHeight, placeable.height)
+        return layout(
+            wrapperWidth,
+            wrapperHeight
+        ) {
+            val position = alignmentCallback(
+                IntSize(wrapperWidth - placeable.width, wrapperHeight - placeable.height),
+                layoutDirection
+            )
+            placeable.placeAbsolute(position)
+        }
+    }
+}
+
+private data class UnspecifiedConstraintsModifier(
+    val minWidth: Dp = Dp.Unspecified,
+    val minHeight: Dp = Dp.Unspecified
+) : LayoutModifier {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MeasureScope.MeasureResult {
+        val wrappedConstraints = Constraints(
+            if (minWidth != Dp.Unspecified && constraints.minWidth == 0) {
+                minWidth.toIntPx().coerceAtMost(constraints.maxWidth)
+            } else {
+                constraints.minWidth
+            },
+            constraints.maxWidth,
+            if (minHeight != Dp.Unspecified && constraints.minHeight == 0) {
+                minHeight.toIntPx().coerceAtMost(constraints.maxHeight)
+            } else {
+                constraints.minHeight
+            },
+            constraints.maxHeight
+        )
+        val placeable = measurable.measure(wrappedConstraints)
+        return layout(placeable.width, placeable.height) {
+            placeable.place(0, 0)
+        }
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicWidth(height).coerceAtLeast(
+        if (minWidth != Dp.Unspecified) minWidth.toIntPx() else 0
+    )
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicWidth(height).coerceAtLeast(
+        if (minWidth != Dp.Unspecified) minWidth.toIntPx() else 0
+    )
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.minIntrinsicHeight(width).coerceAtLeast(
+        if (minHeight != Dp.Unspecified) minHeight.toIntPx() else 0
+    )
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int,
+        layoutDirection: LayoutDirection
+    ) = measurable.maxIntrinsicHeight(width).coerceAtLeast(
+        if (minHeight != Dp.Unspecified) minHeight.toIntPx() else 0
+    )
+}
+
+internal enum class Direction {
+    Vertical, Horizontal, Both
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Row.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Row.kt
new file mode 100644
index 0000000..ece82731
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Row.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
+import androidx.ui.core.Alignment
+import androidx.ui.core.HorizontalAlignmentLine
+import androidx.ui.core.Measured
+import androidx.ui.core.Modifier
+import androidx.ui.util.annotation.FloatRange
+
+/**
+ * A layout composable that places its children in a horizontal sequence. For a layout composable
+ * that places its children in a vertical sequence, see [Column].
+ *
+ * The layout model is able to assign children widths according to their weights provided
+ * using the [RowScope.weight] modifier. If a child is not provided a weight, it will be
+ * asked for its preferred width before the sizes of the children with weights are calculated
+ * proportionally to their weight based on the remaining available space.
+ *
+ * When none of its children have weights, a [Row] will be as small as possible to fit its
+ * children one next to the other. In order to change the width of the [Row], use the
+ * [Modifier.width] modifiers; e.g. to make it fill the available width [Modifier.fillMaxWidth]
+ * can be used. If at least one child of a [Row] has a [weight][RowScope.weight], the [Row] will
+ * fill the available width, so there is no need for [Modifier.fillMaxWidth]. However, if [Row]'s
+ * size should be limited, the [Modifier.width] or [Modifier.size] layout modifiers should be
+ * applied.
+ *
+ * When the size of the [Row] is larger than the sum of its children sizes, a
+ * [horizontalArrangement] can be specified to define the positioning of the children inside
+ * the [Row]. See [Arrangement] for available positioning behaviors; a custom arrangement can
+ * also be defined using the constructor of [Arrangement].
+ *
+ * Example usage:
+ *
+ * @sample androidx.ui.layout.samples.SimpleRow
+ *
+ * @param modifier The modifier to be applied to the Row.
+ * @param horizontalArrangement The horizontal arrangement of the layout's children.
+ * @param verticalGravity The vertical gravity of the layout's children.
+ *
+ * @see Column
+ */
+@Composable
+fun Row(
+    modifier: Modifier = Modifier,
+    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
+    verticalGravity: Alignment.Vertical = Alignment.Top,
+    children: @Composable RowScope.() -> Unit
+) {
+    RowColumnImpl(
+        orientation = LayoutOrientation.Horizontal,
+        modifier = modifier,
+        arrangement = { totalSize, size, layoutDirection ->
+            horizontalArrangement.arrange(totalSize, size, layoutDirection)
+        },
+        crossAxisAlignment = CrossAxisAlignment.vertical(verticalGravity),
+        crossAxisSize = SizeMode.Wrap,
+        children = { RowScope.children() }
+    )
+}
+
+/**
+ * Scope for the children of [Row].
+ */
+@LayoutScopeMarker
+@Immutable
+object RowScope {
+    /**
+     * Position the element vertically within the [Row] according to [align].
+     *
+     * Example usage:
+     * @sample androidx.ui.layout.samples.SimpleGravityInRow
+     */
+    @Stable
+    fun Modifier.gravity(align: Alignment.Vertical) = this + VerticalGravityModifier(align)
+
+    /**
+     * Position the element vertically such that its [alignmentLine] aligns with sibling elements
+     * also configured to [alignWithSiblings]. [alignWithSiblings] is a form of [gravity],
+     * so both modifiers will not work together if specified for the same layout.
+     * [alignWithSiblings] can be used to align two layouts by baseline inside a [Row],
+     * using `alignWithSiblings(FirstBaseline)`.
+     * Within a [Row], all components with [alignWithSiblings] will align vertically using
+     * the specified [HorizontalAlignmentLine]s or values provided using the other
+     * [alignWithSiblings] overload, forming a sibling group.
+     * At least one element of the sibling group will be placed as it had [Alignment.Top] gravity
+     * in [Row], and the alignment of the other siblings will be then determined such that
+     * the alignment lines coincide. Note that if only one element in a [Row] has the
+     * [alignWithSiblings] modifier specified the element will be positioned
+     * as if it had [Alignment.Top] gravity.
+     *
+     * Example usage:
+     * @sample androidx.ui.layout.samples.SimpleRelativeToSiblingsInRow
+     */
+    @Stable
+    fun Modifier.alignWithSiblings(alignmentLine: HorizontalAlignmentLine) =
+        this + SiblingsAlignedModifier.WithAlignmentLine(alignmentLine)
+
+    /**
+     * Size the element's width proportional to its [weight] relative to other weighted sibling
+     * elements in the [Row]. The parent will divide the horizontal space remaining after measuring
+     * unweighted child elements and distribute it according to this weight.
+     * When [fill] is true, the element will be forced to occupy the whole width allocated to it.
+     * Otherwise, the element is allowed to be smaller - this will result in [Row] being smaller,
+     * as the unused allocated width will not be redistributed to other siblings.
+     */
+    @Stable
+    fun Modifier.weight(
+        @FloatRange(from = 0.0, to = 3.4e38 /* POSITIVE_INFINITY */, fromInclusive = false)
+        weight: Float,
+        fill: Boolean = true
+    ): Modifier {
+        require(weight > 0.0) { "invalid weight $weight; must be greater than zero" }
+        return this + LayoutWeightImpl(weight, fill)
+    }
+
+    /**
+     * Position the element vertically such that the alignment line for the content as
+     * determined by [alignmentLineBlock] aligns with sibling elements also configured to
+     * [alignWithSiblings]. [alignWithSiblings] is a form of [gravity], so both modifiers
+     * will not work together if specified for the same layout.
+     * Within a [Row], all components with [alignWithSiblings] will align vertically using
+     * the specified [HorizontalAlignmentLine]s or values obtained from [alignmentLineBlock],
+     * forming a sibling group.
+     * At least one element of the sibling group will be placed as it had [Alignment.Top] gravity
+     * in [Row], and the alignment of the other siblings will be then determined such that
+     * the alignment lines coincide. Note that if only one element in a [Row] has the
+     * [alignWithSiblings] modifier specified the element will be positioned
+     * as if it had [Alignment.Top] gravity.
+     *
+     * Example usage:
+     * @sample androidx.ui.layout.samples.SimpleRelativeToSiblings
+     */
+    @Stable
+    fun Modifier.alignWithSiblings(
+        alignmentLineBlock: (Measured) -> Int
+    ) = this + SiblingsAlignedModifier.WithAlignmentLineBlock(alignmentLineBlock)
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/RowColumnImpl.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/RowColumnImpl.kt
new file mode 100644
index 0000000..e968409
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/RowColumnImpl.kt
@@ -0,0 +1,1140 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
+import androidx.ui.core.Alignment
+import androidx.ui.core.AlignmentLine
+import androidx.ui.core.Constraints
+import androidx.ui.core.IntrinsicMeasurable
+import androidx.ui.core.IntrinsicMeasureBlock2
+import androidx.ui.core.Layout
+import androidx.ui.core.LayoutDirection
+import androidx.ui.core.Measured
+import androidx.ui.core.Modifier
+import androidx.ui.core.ParentDataModifier
+import androidx.ui.core.Placeable
+import androidx.ui.layout.LayoutOrientation.Horizontal
+import androidx.ui.layout.LayoutOrientation.Vertical
+import androidx.ui.unit.Density
+import androidx.ui.util.fastForEach
+import kotlin.math.max
+import kotlin.math.roundToInt
+import kotlin.math.sign
+
+/**
+ * Shared implementation for [Row] and [Column].
+ */
+@Composable
+internal fun RowColumnImpl(
+    orientation: LayoutOrientation,
+    modifier: Modifier = Modifier,
+    arrangement: (Int, List<Int>, LayoutDirection) -> List<Int>,
+    crossAxisSize: SizeMode,
+    crossAxisAlignment: CrossAxisAlignment,
+    children: @Composable () -> Unit
+) {
+    fun Placeable.mainAxisSize() =
+        if (orientation == LayoutOrientation.Horizontal) width else height
+
+    fun Placeable.crossAxisSize() =
+        if (orientation == LayoutOrientation.Horizontal) height else width
+
+    Layout(
+        children,
+        modifier = modifier,
+        minIntrinsicWidthMeasureBlock = MinIntrinsicWidthMeasureBlock(orientation),
+        minIntrinsicHeightMeasureBlock = MinIntrinsicHeightMeasureBlock(orientation),
+        maxIntrinsicWidthMeasureBlock = MaxIntrinsicWidthMeasureBlock(orientation),
+        maxIntrinsicHeightMeasureBlock = MaxIntrinsicHeightMeasureBlock(orientation)
+    ) { measurables, outerConstraints ->
+        val constraints = OrientationIndependentConstraints(outerConstraints, orientation)
+
+        var totalWeight = 0f
+        var fixedSpace = 0
+        var crossAxisSpace = 0
+
+        var anyAlignWithSiblings = false
+        val placeables = arrayOfNulls<Placeable>(measurables.size)
+        val rowColumnParentData = Array(measurables.size) { measurables[it].data }
+
+        // First measure children with zero weight.
+        for (i in measurables.indices) {
+            val child = measurables[i]
+            val parentData = rowColumnParentData[i]
+            val weight = parentData.weight
+
+            if (weight > 0f) {
+                totalWeight += weight
+            } else {
+                val mainAxisMax = constraints.mainAxisMax
+                val placeable = child.measure(
+
+                    // Ask for preferred main axis size.
+                    constraints.copy(
+                        mainAxisMin = 0,
+                        mainAxisMax = if (mainAxisMax == Constraints.Infinity) {
+                            Constraints.Infinity
+                        } else {
+                            mainAxisMax - fixedSpace
+                        },
+                        crossAxisMin = 0
+                    ).toBoxConstraints(orientation)
+                )
+                fixedSpace += placeable.mainAxisSize()
+                crossAxisSpace = max(crossAxisSpace, placeable.crossAxisSize())
+                anyAlignWithSiblings = anyAlignWithSiblings || parentData.isRelative
+                placeables[i] = placeable
+            }
+        }
+
+        // Then measure the rest according to their weights in the remaining main axis space.
+        val targetSpace = if (totalWeight > 0f && constraints.mainAxisMax != Constraints.Infinity) {
+            constraints.mainAxisMax
+        } else {
+            constraints.mainAxisMin
+        }
+
+        val weightUnitSpace = if (totalWeight > 0) {
+            (targetSpace.toFloat() - fixedSpace.toFloat()) / totalWeight
+        } else {
+            0f
+        }
+
+        var remainder = targetSpace - fixedSpace - rowColumnParentData.sumBy {
+            (weightUnitSpace * it.weight).roundToInt()
+        }
+
+        var weightedSpace = 0
+
+        for (i in measurables.indices) {
+            if (placeables[i] == null) {
+                val child = measurables[i]
+                val parentData = rowColumnParentData[i]
+                val weight = parentData.weight
+                check(weight > 0) { "All weights <= 0 should have placeables" }
+                val remainderUnit = remainder.sign
+                remainder -= remainderUnit
+                val childMainAxisSize = max(
+                    0,
+                    (weightUnitSpace * weight).roundToInt() + remainderUnit
+                )
+                val placeable = child.measure(
+                    OrientationIndependentConstraints(
+                        if (parentData.fill && childMainAxisSize != Constraints.Infinity) {
+                            childMainAxisSize
+                        } else {
+                            0
+                        },
+                        childMainAxisSize,
+                        0,
+                        constraints.crossAxisMax
+                    ).toBoxConstraints(orientation)
+                )
+                weightedSpace += placeable.mainAxisSize()
+                crossAxisSpace = max(crossAxisSpace, placeable.crossAxisSize())
+                anyAlignWithSiblings = anyAlignWithSiblings || parentData.isRelative
+                placeables[i] = placeable
+            }
+        }
+
+        var beforeCrossAxisAlignmentLine = 0
+        var afterCrossAxisAlignmentLine = 0
+        if (anyAlignWithSiblings) {
+            for (i in placeables.indices) {
+                val placeable = placeables[i]!!
+                val parentData = rowColumnParentData[i]
+                val alignmentLinePosition = parentData.crossAxisAlignment
+                    ?.calculateAlignmentLinePosition(placeable)
+                if (alignmentLinePosition != null) {
+                    beforeCrossAxisAlignmentLine = max(
+                        beforeCrossAxisAlignmentLine,
+                        alignmentLinePosition.let { if (it != AlignmentLine.Unspecified) it else 0 }
+                    )
+                    afterCrossAxisAlignmentLine = max(
+                        afterCrossAxisAlignmentLine,
+                        placeable.crossAxisSize() -
+                                (alignmentLinePosition.let {
+                                    if (it != AlignmentLine.Unspecified) {
+                                        it
+                                    } else {
+                                        placeable.crossAxisSize()
+                                    }
+                                })
+                    )
+                }
+            }
+        }
+
+        // Compute the Row or Column size and position the children.
+        val mainAxisLayoutSize =
+            if (totalWeight > 0f && constraints.mainAxisMax != Constraints.Infinity) {
+                constraints.mainAxisMax
+            } else {
+                max(fixedSpace + weightedSpace, constraints.mainAxisMin)
+            }
+        val crossAxisLayoutSize = if (constraints.crossAxisMax != Constraints.Infinity &&
+            crossAxisSize == SizeMode.Expand
+        ) {
+            constraints.crossAxisMax
+        } else {
+            max(
+                crossAxisSpace,
+                max(
+                    constraints.crossAxisMin,
+                    beforeCrossAxisAlignmentLine + afterCrossAxisAlignmentLine
+                )
+            )
+        }
+        val layoutWidth = if (orientation == LayoutOrientation.Horizontal) {
+            mainAxisLayoutSize
+        } else {
+            crossAxisLayoutSize
+        }
+        val layoutHeight = if (orientation == LayoutOrientation.Horizontal) {
+            crossAxisLayoutSize
+        } else {
+            mainAxisLayoutSize
+        }
+
+        layout(layoutWidth, layoutHeight) {
+            val childrenMainAxisSize = placeables.map { it!!.mainAxisSize() }
+            val mainAxisPositions = arrangement(
+                mainAxisLayoutSize,
+                childrenMainAxisSize,
+                layoutDirection
+            )
+
+            placeables.forEachIndexed { index, placeable ->
+                placeable!!
+                val parentData = rowColumnParentData[index]
+                val childCrossAlignment = parentData.crossAxisAlignment ?: crossAxisAlignment
+
+                val crossAxis = childCrossAlignment.align(
+                    size = crossAxisLayoutSize - placeable.crossAxisSize(),
+                    layoutDirection = if (orientation == LayoutOrientation.Horizontal) {
+                        LayoutDirection.Ltr
+                    } else {
+                        layoutDirection
+                    },
+                    placeable = placeable,
+                    beforeCrossAxisAlignmentLine = beforeCrossAxisAlignmentLine
+                )
+
+                if (orientation == LayoutOrientation.Horizontal) {
+                    placeable.placeAbsolute(mainAxisPositions[index], crossAxis)
+                } else {
+                    placeable.placeAbsolute(crossAxis, mainAxisPositions[index])
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Used to specify the arrangement of the layout's children in [Row] or [Column] in the main axis
+ * direction (horizontal and vertical, respectively).
+ */
+@Immutable
+object Arrangement {
+    /**
+     * Used to specify the vertical arrangement of the layout's children in a [Column].
+     */
+    interface Vertical {
+        /**
+         * Vertically places the layout children inside the [Column].
+         *
+         * @param totalSize Available space that can be occupied by the children.
+         * @param size A list of sizes of all children.
+         */
+        fun arrange(totalSize: Int, size: List<Int>): List<Int>
+    }
+
+    /**
+     * Used to specify the horizontal arrangement of the layout's children in a [Row].
+     */
+    interface Horizontal {
+        /**
+         * Horizontally places the layout children inside the [Row].
+         *
+         * @param totalSize Available space that can be occupied by the children.
+         * @param size A list of sizes of all children.
+         * @param layoutDirection A layout direction, left-to-right or right-to-left, of the parent
+         * layout that should be taken into account when determining positions of the children.
+         */
+        fun arrange(totalSize: Int, size: List<Int>, layoutDirection: LayoutDirection): List<Int>
+    }
+
+    /**
+     * Place children horizontally such that they are as close as possible to the beginning of the
+     * main axis.
+     */
+    object Start : Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = if (layoutDirection == LayoutDirection.Ltr) {
+            placeLeftOrTop(size)
+        } else {
+            placeRightOrBottom(totalSize, size.asReversed()).asReversed()
+        }
+    }
+
+    /**
+     * Place children horizontally such that they are as close as possible to the end of the main
+     * axis.
+     */
+    object End : Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = if (layoutDirection == LayoutDirection.Ltr) {
+            placeRightOrBottom(totalSize, size)
+        } else {
+            placeLeftOrTop(size.asReversed()).asReversed()
+        }
+    }
+
+    /**
+     * Place children vertically such that they are as close as possible to the top of the main
+     * axis.
+     */
+    object Top : Vertical {
+        override fun arrange(totalSize: Int, size: List<Int>) = placeLeftOrTop(size)
+    }
+
+    /**
+     * Place children vertically such that they are as close as possible to the bottom of the main
+     * axis.
+     */
+    object Bottom : Vertical {
+        override fun arrange(totalSize: Int, size: List<Int>) = placeRightOrBottom(totalSize, size)
+    }
+
+    /**
+     * Place children such that they are as close as possible to the middle of the main axis.
+     */
+    object Center : Vertical, Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = if (layoutDirection == LayoutDirection.Ltr) {
+            placeCenter(totalSize, size)
+        } else {
+            placeCenter(totalSize, size.asReversed()).asReversed()
+        }
+
+        override fun arrange(totalSize: Int, size: List<Int>) = placeCenter(totalSize, size)
+    }
+
+    /**
+     * Place children such that they are spaced evenly across the main axis, including free
+     * space before the first child and after the last child.
+     */
+    object SpaceEvenly : Vertical, Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = if (layoutDirection == LayoutDirection.Ltr) {
+            placeSpaceEvenly(totalSize, size)
+        } else {
+            placeSpaceEvenly(totalSize, size.asReversed()).asReversed()
+        }
+
+        override fun arrange(totalSize: Int, size: List<Int>) = placeSpaceEvenly(totalSize, size)
+    }
+
+    /**
+     * Place children such that they are spaced evenly across the main axis, without free
+     * space before the first child or after the last child.
+     */
+    object SpaceBetween : Vertical, Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = if (layoutDirection == LayoutDirection.Ltr) {
+            placeSpaceBetween(totalSize, size)
+        } else {
+            placeSpaceBetween(totalSize, size.asReversed()).asReversed()
+        }
+
+        override fun arrange(totalSize: Int, size: List<Int>) = placeSpaceBetween(totalSize, size)
+    }
+
+    /**
+     * Place children such that they are spaced evenly across the main axis, including free
+     * space before the first child and after the last child, but half the amount of space
+     * existing otherwise between two consecutive children.
+     */
+    object SpaceAround : Vertical, Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = if (layoutDirection == LayoutDirection.Ltr) {
+            placeSpaceAround(totalSize, size)
+        } else {
+            placeSpaceAround(totalSize, size.asReversed()).asReversed()
+        }
+
+        override fun arrange(totalSize: Int, size: List<Int>) = placeSpaceAround(totalSize, size)
+    }
+
+    internal fun placeRightOrBottom(totalSize: Int, size: List<Int>): List<Int> {
+        val consumedSize = size.fold(0) { a, b -> a + b }
+        val positions = mutableListOf<Int>()
+        var current = totalSize - consumedSize
+        size.fastForEach {
+            positions.add(current)
+            current += it
+        }
+        return positions
+    }
+
+    internal fun placeLeftOrTop(size: List<Int>): List<Int> {
+        val positions = mutableListOf<Int>()
+        var current = 0
+        size.fastForEach {
+            positions.add(current)
+            current += it
+        }
+        return positions
+    }
+
+    internal fun placeCenter(totalSize: Int, size: List<Int>): List<Int> {
+        val consumedSize = size.fold(0) { a, b -> a + b }
+        val positions = mutableListOf<Int>()
+        var current = (totalSize - consumedSize).toFloat() / 2
+        size.fastForEach {
+            positions.add(current.roundToInt())
+            current += it.toFloat()
+        }
+        return positions
+    }
+
+    internal fun placeSpaceEvenly(totalSize: Int, size: List<Int>): List<Int> {
+        val consumedSize = size.fold(0) { a, b -> a + b }
+        val gapSize = (totalSize - consumedSize).toFloat() / (size.size + 1)
+        val positions = mutableListOf<Int>()
+        var current = gapSize
+        size.fastForEach {
+            positions.add(current.roundToInt())
+            current += it.toFloat() + gapSize
+        }
+        return positions
+    }
+
+    internal fun placeSpaceBetween(totalSize: Int, size: List<Int>): List<Int> {
+        val consumedSize = size.fold(0) { a, b -> a + b }
+        val gapSize = if (size.size > 1) {
+            (totalSize - consumedSize).toFloat() / (size.size - 1)
+        } else {
+            0f
+        }
+        val positions = mutableListOf<Int>()
+        var current = 0f
+        size.fastForEach {
+            positions.add(current.roundToInt())
+            current += it.toFloat() + gapSize
+        }
+        return positions
+    }
+
+    internal fun placeSpaceAround(totalSize: Int, size: List<Int>): List<Int> {
+        val consumedSize = size.fold(0) { a, b -> a + b }
+        val gapSize = if (size.isNotEmpty()) {
+            (totalSize - consumedSize).toFloat() / size.size
+        } else {
+            0f
+        }
+        val positions = mutableListOf<Int>()
+        var current = gapSize / 2
+        size.fastForEach {
+            positions.add(current.roundToInt())
+            current += it.toFloat() + gapSize
+        }
+        return positions
+    }
+}
+
+@Immutable
+object AbsoluteArrangement {
+    /**
+     * Place children horizontally such that they are as close as possible to the left edge of
+     * the [Row].
+     *
+     * Unlike [Arrangement.Start], in RTL context positions of the children will not be
+     * mirrored and as such children will appear in the order they are put inside the [Row].
+     */
+    object Left : Arrangement.Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = Arrangement.placeLeftOrTop(size)
+    }
+
+    /**
+     * Place children such that they are as close as possible to the middle of the [Row].
+     *
+     * Unlike [Arrangement.Center], in RTL context positions of the children will not be
+     * mirrored and as such children will appear in the order they are put inside the [Row].
+     */
+    object Center : Arrangement.Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = Arrangement.placeCenter(totalSize, size)
+    }
+
+    /**
+     * Place children horizontally such that they are as close as possible to the right edge of
+     * the [Row].
+     *
+     * Unlike [Arrangement.End], in RTL context positions of the children will not be
+     * mirrored and as such children will appear in the order they are put inside the [Row].
+     */
+    object Right : Arrangement.Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = Arrangement.placeRightOrBottom(totalSize, size)
+    }
+
+    /**
+     * Place children such that they are spaced evenly across the main axis, without free
+     * space before the first child or after the last child.
+     *
+     * Unlike [Arrangement.SpaceBetween], in RTL context positions of the children will not be
+     * mirrored and as such children will appear in the order they are put inside the [Row].
+     */
+    object SpaceBetween : Arrangement.Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = Arrangement.placeSpaceBetween(totalSize, size)
+    }
+
+    /**
+     * Place children such that they are spaced evenly across the main axis, including free
+     * space before the first child and after the last child.
+     *
+     * Unlike [Arrangement.SpaceEvenly], in RTL context positions of the children will not be
+     * mirrored and as such children will appear in the order they are put inside the [Row].
+     */
+    object SpaceEvenly : Arrangement.Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = Arrangement.placeSpaceEvenly(totalSize, size)
+    }
+
+    /**
+     * Place children such that they are spaced evenly horizontally, including free
+     * space before the first child and after the last child, but half the amount of space
+     * existing otherwise between two consecutive children.
+     *
+     * Unlike [Arrangement.SpaceAround], in RTL context positions of the children will not be
+     * mirrored and as such children will appear in the order they are put inside the [Row].
+     */
+    object SpaceAround : Arrangement.Horizontal {
+        override fun arrange(
+            totalSize: Int,
+            size: List<Int>,
+            layoutDirection: LayoutDirection
+        ) = Arrangement.placeSpaceAround(totalSize, size)
+    }
+}
+
+/**
+ * [Row] will be [Horizontal], [Column] is [Vertical].
+ */
+internal enum class LayoutOrientation {
+    Horizontal,
+    Vertical
+}
+
+/**
+ * Used to specify how a layout chooses its own size when multiple behaviors are possible.
+ */
+// TODO(popam): remove this when Flow is reworked
+enum class SizeMode {
+    /**
+     * Minimize the amount of free space by wrapping the children,
+     * subject to the incoming layout constraints.
+     */
+    Wrap,
+    /**
+     * Maximize the amount of free space by expanding to fill the available space,
+     * subject to the incoming layout constraints.
+     */
+    Expand
+}
+
+/**
+ * Used to specify the alignment of a layout's children, in main axis direction.
+ */
+enum class MainAxisAlignment(internal val arrangement: Arrangement.Vertical) {
+    // TODO(soboleva) support RTl in Flow
+    // workaround for now - use Arrangement that equals to previous Arrangement
+    /**
+     * Place children such that they are as close as possible to the middle of the main axis.
+     */
+    Center(Arrangement.Center),
+
+    /**
+     * Place children such that they are as close as possible to the start of the main axis.
+     */
+    Start(Arrangement.Top),
+
+    /**
+     * Place children such that they are as close as possible to the end of the main axis.
+     */
+    End(Arrangement.Bottom),
+
+    /**
+     * Place children such that they are spaced evenly across the main axis, including free
+     * space before the first child and after the last child.
+     */
+    SpaceEvenly(Arrangement.SpaceEvenly),
+
+    /**
+     * Place children such that they are spaced evenly across the main axis, without free
+     * space before the first child or after the last child.
+     */
+    SpaceBetween(Arrangement.SpaceBetween),
+
+    /**
+     * Place children such that they are spaced evenly across the main axis, including free
+     * space before the first child and after the last child, but half the amount of space
+     * existing otherwise between two consecutive children.
+     */
+    SpaceAround(Arrangement.SpaceAround);
+}
+
+/**
+ * Used to specify the alignment of a layout's children, in cross axis direction.
+ */
+// TODO(popam): refine this API surface with modifiers - add type safety for alignment orientation.
+@Immutable
+sealed class CrossAxisAlignment {
+    /**
+     * Aligns to [size]. If this is a vertical alignment, [layoutDirection] should be
+     * [LayoutDirection.Ltr].
+     *
+     * @param size The remaining space (total size - content size) in the container.
+     * @param layoutDirection The layout direction of the content if horizontal or
+     * [LayoutDirection.Ltr] if vertical.
+     * @param placeable The item being aligned.
+     * @param beforeCrossAxisAlignmentLine The space before the cross-axis alignment line if
+     * an alignment line is being used or 0 if no alignment line is being used.
+     */
+    internal abstract fun align(
+        size: Int,
+        layoutDirection: LayoutDirection,
+        placeable: Placeable,
+        beforeCrossAxisAlignmentLine: Int
+    ): Int
+
+    /**
+     * Returns `true` if this is [Relative].
+     */
+    internal open val isRelative: Boolean
+        get() = false
+
+    /**
+     * Returns the alignment line position relative to the left/top of the space or `null` if
+     * this alignment doesn't rely on alignment lines.
+     */
+    internal open fun calculateAlignmentLinePosition(placeable: Placeable): Int? = null
+
+    companion object {
+        /**
+         * Place children such that their center is in the middle of the cross axis.
+         */
+        @Stable
+        val Center: CrossAxisAlignment = CenterCrossAxisAlignment
+        /**
+         * Place children such that their start edge is aligned to the start edge of the cross
+         * axis. TODO(popam): Consider rtl directionality.
+         */
+        @Stable
+        val Start: CrossAxisAlignment = StartCrossAxisAlignment
+        /**
+         * Place children such that their end edge is aligned to the end edge of the cross
+         * axis. TODO(popam): Consider rtl directionality.
+         */
+        @Stable
+        val End: CrossAxisAlignment = EndCrossAxisAlignment
+
+        /**
+         * Align children by their baseline.
+         */
+        fun AlignmentLine(alignmentLine: AlignmentLine): CrossAxisAlignment =
+            AlignmentLineCrossAxisAlignment(AlignmentLineProvider.Value(alignmentLine))
+
+        /**
+         * Align children relative to their siblings using the alignment line provided as a
+         * parameter using [AlignmentLineProvider].
+         */
+        internal fun Relative(alignmentLineProvider: AlignmentLineProvider): CrossAxisAlignment =
+            AlignmentLineCrossAxisAlignment(alignmentLineProvider)
+
+        /**
+         * Align children with vertical alignment.
+         */
+        internal fun vertical(vertical: Alignment.Vertical): CrossAxisAlignment =
+            VerticalCrossAxisAlignment(vertical)
+
+        /**
+         * Align children with horizontal alignment.
+         */
+        internal fun horizontal(horizontal: Alignment.Horizontal): CrossAxisAlignment =
+            HorizontalCrossAxisAlignment(horizontal)
+    }
+
+    private object CenterCrossAxisAlignment : CrossAxisAlignment() {
+        override fun align(
+            size: Int,
+            layoutDirection: LayoutDirection,
+            placeable: Placeable,
+            beforeCrossAxisAlignmentLine: Int
+        ): Int {
+            return size / 2
+        }
+    }
+
+    private object StartCrossAxisAlignment : CrossAxisAlignment() {
+        override fun align(
+            size: Int,
+            layoutDirection: LayoutDirection,
+            placeable: Placeable,
+            beforeCrossAxisAlignmentLine: Int
+        ): Int {
+            return if (layoutDirection == LayoutDirection.Ltr) 0 else size
+        }
+    }
+
+    private object EndCrossAxisAlignment : CrossAxisAlignment() {
+        override fun align(
+            size: Int,
+            layoutDirection: LayoutDirection,
+            placeable: Placeable,
+            beforeCrossAxisAlignmentLine: Int
+        ): Int {
+            return if (layoutDirection == LayoutDirection.Ltr) size else 0
+        }
+    }
+
+    private class AlignmentLineCrossAxisAlignment(
+        val alignmentLineProvider: AlignmentLineProvider
+    ) : CrossAxisAlignment() {
+        override val isRelative: Boolean
+            get() = true
+
+        override fun calculateAlignmentLinePosition(placeable: Placeable): Int? {
+            return alignmentLineProvider.calculateAlignmentLinePosition(placeable)
+        }
+
+        override fun align(
+            size: Int,
+            layoutDirection: LayoutDirection,
+            placeable: Placeable,
+            beforeCrossAxisAlignmentLine: Int
+        ): Int {
+            val alignmentLinePosition =
+                alignmentLineProvider.calculateAlignmentLinePosition(placeable)
+            return if (alignmentLinePosition != null) {
+                val line = beforeCrossAxisAlignmentLine - alignmentLinePosition
+                if (layoutDirection == LayoutDirection.Rtl) {
+                    size - line
+                } else {
+                    line
+                }
+            } else {
+                0
+            }
+        }
+    }
+
+    private class VerticalCrossAxisAlignment(
+        val vertical: Alignment.Vertical
+    ) : CrossAxisAlignment() {
+        override fun align(
+            size: Int,
+            layoutDirection: LayoutDirection,
+            placeable: Placeable,
+            beforeCrossAxisAlignmentLine: Int
+        ): Int {
+            return vertical.align(size)
+        }
+    }
+
+    private class HorizontalCrossAxisAlignment(
+        val horizontal: Alignment.Horizontal
+    ) : CrossAxisAlignment() {
+        override fun align(
+            size: Int,
+            layoutDirection: LayoutDirection,
+            placeable: Placeable,
+            beforeCrossAxisAlignmentLine: Int
+        ): Int {
+            return horizontal.align(size, layoutDirection)
+        }
+    }
+}
+
+/**
+ * Box [Constraints], but which abstract away width and height in favor of main axis and cross axis.
+ */
+internal data class OrientationIndependentConstraints(
+    val mainAxisMin: Int,
+    val mainAxisMax: Int,
+    val crossAxisMin: Int,
+    val crossAxisMax: Int
+) {
+    constructor(c: Constraints, orientation: LayoutOrientation) : this(
+        if (orientation === LayoutOrientation.Horizontal) c.minWidth else c.minHeight,
+        if (orientation === LayoutOrientation.Horizontal) c.maxWidth else c.maxHeight,
+        if (orientation === LayoutOrientation.Horizontal) c.minHeight else c.minWidth,
+        if (orientation === LayoutOrientation.Horizontal) c.maxHeight else c.maxWidth
+    )
+
+    // Creates a new instance with the same main axis constraints and maximum tight cross axis.
+    fun stretchCrossAxis() = OrientationIndependentConstraints(
+        mainAxisMin,
+        mainAxisMax,
+        if (crossAxisMax != Constraints.Infinity) crossAxisMax else crossAxisMin,
+        crossAxisMax
+    )
+
+    // Given an orientation, resolves the current instance to traditional constraints.
+    fun toBoxConstraints(orientation: LayoutOrientation) =
+        if (orientation === LayoutOrientation.Horizontal) {
+            Constraints(mainAxisMin, mainAxisMax, crossAxisMin, crossAxisMax)
+        } else {
+            Constraints(crossAxisMin, crossAxisMax, mainAxisMin, mainAxisMax)
+        }
+
+    // Given an orientation, resolves the max width constraint this instance represents.
+    fun maxWidth(orientation: LayoutOrientation) =
+        if (orientation === LayoutOrientation.Horizontal) {
+            mainAxisMax
+        } else {
+            crossAxisMax
+        }
+
+    // Given an orientation, resolves the max height constraint this instance represents.
+    fun maxHeight(orientation: LayoutOrientation) =
+        if (orientation === LayoutOrientation.Horizontal) {
+            crossAxisMax
+        } else {
+            mainAxisMax
+        }
+}
+
+private val IntrinsicMeasurable.data: RowColumnParentData?
+    get() = parentData as? RowColumnParentData
+
+private val RowColumnParentData?.weight: Float
+    get() = this?.weight ?: 0f
+
+private val RowColumnParentData?.fill: Boolean
+    get() = this?.fill ?: true
+
+private val RowColumnParentData?.crossAxisAlignment: CrossAxisAlignment?
+    get() = this?.crossAxisAlignment
+
+private val RowColumnParentData?.isRelative: Boolean
+    get() = this.crossAxisAlignment?.isRelative ?: false
+
+private /*inline*/ fun MinIntrinsicWidthMeasureBlock(orientation: LayoutOrientation) =
+    if (orientation == LayoutOrientation.Horizontal) {
+        IntrinsicMeasureBlocks.HorizontalMinWidth
+    } else {
+        IntrinsicMeasureBlocks.VerticalMinWidth
+    }
+
+private /*inline*/ fun MinIntrinsicHeightMeasureBlock(orientation: LayoutOrientation) =
+    if (orientation == LayoutOrientation.Horizontal) {
+        IntrinsicMeasureBlocks.HorizontalMinHeight
+    } else {
+        IntrinsicMeasureBlocks.VerticalMinHeight
+    }
+
+private /*inline*/ fun MaxIntrinsicWidthMeasureBlock(orientation: LayoutOrientation) =
+    if (orientation == LayoutOrientation.Horizontal) {
+        IntrinsicMeasureBlocks.HorizontalMaxWidth
+    } else {
+        IntrinsicMeasureBlocks.VerticalMaxWidth
+    }
+
+private /*inline*/ fun MaxIntrinsicHeightMeasureBlock(orientation: LayoutOrientation) =
+    if (orientation == LayoutOrientation.Horizontal) {
+        IntrinsicMeasureBlocks.HorizontalMaxHeight
+    } else {
+        IntrinsicMeasureBlocks.VerticalMaxHeight
+    }
+
+private object IntrinsicMeasureBlocks {
+    val HorizontalMinWidth: IntrinsicMeasureBlock2 = { measurables, availableHeight ->
+        intrinsicSize(
+            measurables,
+            { h -> minIntrinsicWidth(h) },
+            { w -> maxIntrinsicHeight(w) },
+            availableHeight,
+            LayoutOrientation.Horizontal,
+            LayoutOrientation.Horizontal
+        )
+    }
+    val VerticalMinWidth: IntrinsicMeasureBlock2 = { measurables, availableHeight ->
+        intrinsicSize(
+            measurables,
+            { h -> minIntrinsicWidth(h) },
+            { w -> maxIntrinsicHeight(w) },
+            availableHeight,
+            LayoutOrientation.Vertical,
+            LayoutOrientation.Horizontal
+        )
+    }
+    val HorizontalMinHeight: IntrinsicMeasureBlock2 = { measurables, availableWidth ->
+        intrinsicSize(
+            measurables,
+            { w -> minIntrinsicHeight(w) },
+            { h -> maxIntrinsicWidth(h) },
+            availableWidth,
+            LayoutOrientation.Horizontal,
+            LayoutOrientation.Vertical
+        )
+    }
+    val VerticalMinHeight: IntrinsicMeasureBlock2 = { measurables, availableWidth ->
+        intrinsicSize(
+            measurables,
+            { w -> minIntrinsicHeight(w) },
+            { h -> maxIntrinsicWidth(h) },
+            availableWidth,
+            LayoutOrientation.Vertical,
+            LayoutOrientation.Vertical
+        )
+    }
+    val HorizontalMaxWidth: IntrinsicMeasureBlock2 = { measurables, availableHeight ->
+        intrinsicSize(
+            measurables,
+            { h -> maxIntrinsicWidth(h) },
+            { w -> maxIntrinsicHeight(w) },
+            availableHeight,
+            LayoutOrientation.Horizontal,
+            LayoutOrientation.Horizontal
+        )
+    }
+    val VerticalMaxWidth: IntrinsicMeasureBlock2 = { measurables, availableHeight ->
+        intrinsicSize(
+            measurables,
+            { h -> maxIntrinsicWidth(h) },
+            { w -> maxIntrinsicHeight(w) },
+            availableHeight,
+            LayoutOrientation.Vertical,
+            LayoutOrientation.Horizontal
+        )
+    }
+    val HorizontalMaxHeight: IntrinsicMeasureBlock2 = { measurables, availableWidth ->
+        intrinsicSize(
+            measurables,
+            { w -> maxIntrinsicHeight(w) },
+            { h -> maxIntrinsicWidth(h) },
+            availableWidth,
+            LayoutOrientation.Horizontal,
+            LayoutOrientation.Vertical
+        )
+    }
+    val VerticalMaxHeight: IntrinsicMeasureBlock2 = { measurables, availableWidth ->
+        intrinsicSize(
+            measurables,
+            { w -> maxIntrinsicHeight(w) },
+            { h -> maxIntrinsicWidth(h) },
+            availableWidth,
+            LayoutOrientation.Vertical,
+            LayoutOrientation.Vertical
+        )
+    }
+}
+
+private fun intrinsicSize(
+    children: List<IntrinsicMeasurable>,
+    intrinsicMainSize: IntrinsicMeasurable.(Int) -> Int,
+    intrinsicCrossSize: IntrinsicMeasurable.(Int) -> Int,
+    crossAxisAvailable: Int,
+    layoutOrientation: LayoutOrientation,
+    intrinsicOrientation: LayoutOrientation
+) = if (layoutOrientation == intrinsicOrientation) {
+    intrinsicMainAxisSize(children, intrinsicMainSize, crossAxisAvailable)
+} else {
+    intrinsicCrossAxisSize(children, intrinsicCrossSize, intrinsicMainSize, crossAxisAvailable)
+}
+
+private fun intrinsicMainAxisSize(
+    children: List<IntrinsicMeasurable>,
+    mainAxisSize: IntrinsicMeasurable.(Int) -> Int,
+    crossAxisAvailable: Int
+): Int {
+    var weightUnitSpace = 0
+    var fixedSpace = 0
+    var totalWeight = 0f
+    children.fastForEach { child ->
+        val weight = child.data.weight
+        val size = child.mainAxisSize(crossAxisAvailable)
+        if (weight == 0f) {
+            fixedSpace += size
+        } else if (weight > 0f) {
+            totalWeight += weight
+            weightUnitSpace = max(weightUnitSpace, (size / weight).roundToInt())
+        }
+    }
+    return (weightUnitSpace * totalWeight).roundToInt() + fixedSpace
+}
+
+private fun intrinsicCrossAxisSize(
+    children: List<IntrinsicMeasurable>,
+    mainAxisSize: IntrinsicMeasurable.(Int) -> Int,
+    crossAxisSize: IntrinsicMeasurable.(Int) -> Int,
+    mainAxisAvailable: Int
+): Int {
+    var fixedSpace = 0
+    var crossAxisMax = 0
+    var totalWeight = 0f
+    children.fastForEach { child ->
+        val weight = child.data.weight
+        if (weight == 0f) {
+            val mainAxisSpace = child.mainAxisSize(Constraints.Infinity)
+            fixedSpace += mainAxisSpace
+            crossAxisMax = max(crossAxisMax, child.crossAxisSize(mainAxisSpace))
+        } else if (weight > 0f) {
+            totalWeight += weight
+        }
+    }
+
+    val weightUnitSpace = if (totalWeight == 0f) {
+        0
+    } else if (mainAxisAvailable == Constraints.Infinity) {
+        Constraints.Infinity
+    } else {
+        (max(mainAxisAvailable - fixedSpace, 0) / totalWeight).roundToInt()
+    }
+
+    children.fastForEach { child ->
+        val weight = child.data.weight
+        if (weight > 0f) {
+            crossAxisMax = max(
+                crossAxisMax,
+                child.crossAxisSize((weightUnitSpace * weight).roundToInt())
+            )
+        }
+    }
+    return crossAxisMax
+}
+
+internal data class LayoutWeightImpl(val weight: Float, val fill: Boolean) : ParentDataModifier {
+    override fun Density.modifyParentData(parentData: Any?) =
+        ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
+            it.weight = weight
+            it.fill = fill
+        }
+}
+
+internal sealed class SiblingsAlignedModifier : ParentDataModifier {
+    abstract override fun Density.modifyParentData(parentData: Any?): Any?
+
+    internal data class WithAlignmentLineBlock(val block: (Measured) -> Int) :
+        SiblingsAlignedModifier() {
+        override fun Density.modifyParentData(parentData: Any?): Any? {
+            return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
+                it.crossAxisAlignment =
+                    CrossAxisAlignment.Relative(AlignmentLineProvider.Block(block))
+            }
+        }
+    }
+
+    internal data class WithAlignmentLine(val line: AlignmentLine) :
+        SiblingsAlignedModifier() {
+        override fun Density.modifyParentData(parentData: Any?): Any? {
+            return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
+                it.crossAxisAlignment =
+                    CrossAxisAlignment.Relative(AlignmentLineProvider.Value(line))
+            }
+        }
+    }
+}
+
+internal data class HorizontalGravityModifier(
+    val horizontal: Alignment.Horizontal
+) : ParentDataModifier {
+    override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {
+        return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
+            it.crossAxisAlignment = CrossAxisAlignment.horizontal(horizontal)
+        }
+    }
+}
+
+internal data class VerticalGravityModifier(
+    val vertical: Alignment.Vertical
+) : ParentDataModifier {
+    override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {
+        return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
+            it.crossAxisAlignment = CrossAxisAlignment.vertical(vertical)
+        }
+    }
+}
+
+/**
+ * Parent data associated with children.
+ */
+internal data class RowColumnParentData(
+    var weight: Float = 0f,
+    var fill: Boolean = true,
+    var crossAxisAlignment: CrossAxisAlignment? = null
+)
+
+/**
+ * Provides the alignment line.
+ */
+internal sealed class AlignmentLineProvider {
+    abstract fun calculateAlignmentLinePosition(placeable: Placeable): Int?
+    data class Block(val lineProviderBlock: (Measured) -> Int) : AlignmentLineProvider() {
+        override fun calculateAlignmentLinePosition(
+            placeable: Placeable
+        ): Int? {
+            return lineProviderBlock(Measured(placeable))
+        }
+    }
+
+    data class Value(val line: AlignmentLine) : AlignmentLineProvider() {
+        override fun calculateAlignmentLinePosition(placeable: Placeable): Int? {
+            return placeable[line]
+        }
+    }
+}
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Spacer.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Spacer.kt
new file mode 100644
index 0000000..8a098b3
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Spacer.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Composable
+import androidx.compose.emptyContent
+import androidx.ui.core.Layout
+import androidx.ui.core.Modifier
+import androidx.ui.core.hasFixedHeight
+import androidx.ui.core.hasFixedWidth
+
+/**
+ * Component that represents an empty space layout, whose size can be defined using the [LayoutWidth],
+ * [LayoutHeight] and [LayoutSize] modifiers.
+ *
+ * @sample androidx.ui.layout.samples.SpacerExample
+ *
+ * @param modifier modifiers to set to this spacer
+ */
+@Composable
+fun Spacer(modifier: Modifier) {
+    Layout(emptyContent(), modifier) { _, constraints ->
+        with(constraints) {
+            val width = if (hasFixedWidth) maxWidth else 0
+            val height = if (hasFixedHeight) maxHeight else 0
+            layout(width, height) {}
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Stack.kt b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Stack.kt
new file mode 100644
index 0000000..078531b
--- /dev/null
+++ b/ui/ui-layout/src/commonMain/kotlin/androidx/ui/layout/Stack.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.layout
+
+import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
+import androidx.ui.core.Alignment
+import androidx.ui.core.Constraints
+import androidx.ui.core.Layout
+import androidx.ui.core.Measurable
+import androidx.ui.core.Modifier
+import androidx.ui.core.ParentDataModifier
+import androidx.ui.core.Placeable
+import androidx.ui.unit.Density
+import androidx.ui.unit.IntSize
+import kotlin.math.max
+
+/**
+ * A composable that positions its children relative to its edges.
+ * The component is useful for drawing children that overlap. The children will always be
+ * drawn in the order they are specified in the body of the [Stack].
+ * Use [StackScope.gravity] modifier to define the position of the target element inside the
+ * [Stack] box.
+ *
+ * Example usage:
+ *
+ * @sample androidx.ui.layout.samples.SimpleStack
+ */
+@Composable
+fun Stack(
+    modifier: Modifier = Modifier,
+    children: @Composable StackScope.() -> Unit
+) {
+    val stackChildren: @Composable () -> Unit = { StackScope().children() }
+
+    Layout(stackChildren, modifier = modifier) { measurables, constraints ->
+        val placeables = arrayOfNulls<Placeable>(measurables.size)
+        // First measure aligned children to get the size of the layout.
+        val childConstraints = constraints.copy(minWidth = 0, minHeight = 0)
+        (0 until measurables.size).filter { i -> !measurables[i].stretch }.forEach { i ->
+            placeables[i] = measurables[i].measure(childConstraints)
+        }
+        val (stackWidth, stackHeight) = with(placeables.filterNotNull()) {
+            Pair(
+                max(maxBy { it.width }?.width ?: 0, constraints.minWidth),
+                max(maxBy { it.height }?.height ?: 0, constraints.minHeight)
+            )
+        }
+
+        // Now measure stretch children.
+        (0 until measurables.size).filter { i -> measurables[i].stretch }.forEach { i ->
+            // infinity check is needed for intrinsic measurements
+            val minWidth = if (stackWidth != Constraints.Infinity) stackWidth else 0
+            val minHeight = if (stackHeight != Constraints.Infinity) stackHeight else 0
+            placeables[i] = measurables[i].measure(
+                Constraints(minWidth, stackWidth, minHeight, stackHeight)
+            )
+        }
+
+        // Position the children.
+        layout(stackWidth, stackHeight) {
+            (0 until measurables.size).forEach { i ->
+                val measurable = measurables[i]
+                val childData = measurable.stackChildData
+                val placeable = placeables[i]!!
+
+                val position = childData.alignment.align(
+                    IntSize(
+                        stackWidth - placeable.width,
+                        stackHeight - placeable.height
+                    ),
+                    layoutDirection
+                )
+                placeable.placeAbsolute(position.x, position.y)
+            }
+        }
+    }
+}
+
+/**
+ * A StackScope provides a scope for the children of a [Stack].
+ */
+@LayoutScopeMarker
+@Immutable
+class StackScope {
+    /**
+     * Pull the content element to a specific [Alignment] within the [Stack].
+     */
+    @Stable
+    fun Modifier.gravity(align: Alignment) = this + StackGravityModifier(align)
+
+    /**
+     * Size the element to match the size of the [Stack] after all other content elements have
+     * been measured.
+     *
+     * The element using this modifier does not take part in defining the size of the [Stack].
+     * Instead, it matches the size of the [Stack] after all other children (not using
+     * matchParentSize() modifier) have been measured to obtain the [Stack]'s size.
+     * In contrast, a general-purpose [Modifier.fillMaxSize] modifier, which makes an element
+     * occupy all available space, will take part in defining the size of the [Stack]. Consequently,
+     * using it for an element inside a [Stack] will make the [Stack] itself always fill the
+     * available space.
+     */
+    @Stable
+    fun Modifier.matchParentSize() = this + StretchGravityModifier
+
+    internal companion object {
+        @Stable
+        val StretchGravityModifier: ParentDataModifier =
+            StackGravityModifier(Alignment.Center, true)
+    }
+}
+
+private data class StackChildData(
+    val alignment: Alignment,
+    val stretch: Boolean = false
+)
+
+private val Measurable.stackChildData: StackChildData
+    get() = (parentData as? StackChildData) ?: StackChildData(Alignment.TopStart)
+private val Measurable.stretch: Boolean
+    get() = stackChildData.stretch
+
+private data class StackGravityModifier(
+    val alignment: Alignment,
+    val stretch: Boolean = false
+) : ParentDataModifier {
+    override fun Density.modifyParentData(parentData: Any?): StackChildData {
+        return ((parentData as? StackChildData) ?: StackChildData(alignment, stretch))
+    }
+}