0
# Layout System
1
2
Flexible layout engine with built-in layouts and support for custom layout implementations with precise measurement and positioning control for creating sophisticated user interfaces.
3
4
## Capabilities
5
6
### Custom Layout Foundation
7
8
Core layout system providing the building blocks for creating custom layout composables with precise measurement and positioning control.
9
10
```kotlin { .api }
11
/**
12
* A composable that lays out and draws its children according to a custom measurement and placement policy.
13
*/
14
@Composable
15
fun Layout(
16
content: @Composable () -> Unit,
17
modifier: Modifier = Modifier,
18
measurePolicy: MeasurePolicy
19
)
20
21
/**
22
* A composable that lays out multiple sets of children according to a custom measurement and placement policy.
23
*/
24
@Composable
25
fun MultiMeasureLayout(
26
modifier: Modifier = Modifier,
27
measurePolicy: MultiMeasurePolicy,
28
content: @Composable () -> Unit
29
)
30
31
/**
32
* Policy for measuring and placing children in a Layout composable.
33
*/
34
fun interface MeasurePolicy {
35
/**
36
* Measure and position child composables.
37
*/
38
fun MeasureScope.measure(
39
measurables: List<Measurable>,
40
constraints: Constraints
41
): MeasureResult
42
43
/**
44
* Calculate the minimum intrinsic width.
45
*/
46
fun IntrinsicMeasureScope.minIntrinsicWidth(
47
measurables: List<IntrinsicMeasurable>,
48
height: Int
49
): Int = 0
50
51
/**
52
* Calculate the minimum intrinsic height.
53
*/
54
fun IntrinsicMeasureScope.minIntrinsicHeight(
55
measurables: List<IntrinsicMeasurable>,
56
width: Int
57
): Int = 0
58
59
/**
60
* Calculate the maximum intrinsic width.
61
*/
62
fun IntrinsicMeasureScope.maxIntrinsicWidth(
63
measurables: List<IntrinsicMeasurable>,
64
height: Int
65
): Int = 0
66
67
/**
68
* Calculate the maximum intrinsic height.
69
*/
70
fun IntrinsicMeasureScope.maxIntrinsicHeight(
71
measurables: List<IntrinsicMeasurable>,
72
width: Int
73
): Int = 0
74
}
75
76
/**
77
* The measurement scope used by a Layout's MeasurePolicy.
78
*/
79
interface MeasureScope : IntrinsicMeasureScope {
80
/**
81
* Create a MeasureResult with the specified size and placement block.
82
*/
83
fun layout(
84
width: Int,
85
height: Int,
86
alignmentLines: Map<AlignmentLine, Int> = emptyMap(),
87
placementBlock: Placeable.PlacementScope.() -> Unit
88
): MeasureResult
89
}
90
91
/**
92
* Result of measuring a layout.
93
*/
94
interface MeasureResult {
95
/**
96
* Width of the measured layout.
97
*/
98
val width: Int
99
100
/**
101
* Height of the measured layout.
102
*/
103
val height: Int
104
105
/**
106
* Map of alignment lines to their positions.
107
*/
108
val alignmentLines: Map<AlignmentLine, Int>
109
110
/**
111
* Place children of the layout.
112
*/
113
fun placeChildren()
114
}
115
116
/**
117
* Represents a child composable that can be measured.
118
*/
119
interface Measurable : IntrinsicMeasurable {
120
/**
121
* Measure the child with the given constraints.
122
*/
123
fun measure(constraints: Constraints): Placeable
124
125
/**
126
* Data associated with this measurable.
127
*/
128
val parentData: Any?
129
}
130
131
/**
132
* Represents a measured child that can be placed.
133
*/
134
abstract class Placeable : Measured {
135
/**
136
* Width of the placeable after measurement.
137
*/
138
abstract val width: Int
139
140
/**
141
* Height of the placeable after measurement.
142
*/
143
abstract val height: Int
144
145
/**
146
* Map of alignment lines for this placeable.
147
*/
148
open val alignmentLines: Map<AlignmentLine, Int> = emptyMap()
149
150
/**
151
* Measured size as IntSize.
152
*/
153
val measuredSize: IntSize
154
155
/**
156
* Place the child at the given position.
157
*/
158
protected abstract fun placeAt(
159
position: IntOffset,
160
zIndex: Float,
161
layerBlock: (GraphicsLayerScope.() -> Unit)?
162
)
163
164
/**
165
* Place the child at the given coordinates.
166
*/
167
fun place(x: Int, y: Int) {
168
place(IntOffset(x, y))
169
}
170
171
/**
172
* Place the child at the given offset.
173
*/
174
fun place(position: IntOffset) {
175
placeAt(position, 0f, null)
176
}
177
178
/**
179
* Place the child with a z-index for layering.
180
*/
181
fun placeWithLayer(
182
position: IntOffset,
183
zIndex: Float = 0f,
184
layerBlock: GraphicsLayerScope.() -> Unit = {}
185
) {
186
placeAt(position, zIndex, layerBlock)
187
}
188
189
/**
190
* Place the child relative to the layout direction.
191
*/
192
fun placeRelative(x: Int, y: Int) {
193
placeRelative(IntOffset(x, y))
194
}
195
196
/**
197
* Place the child relative to the layout direction.
198
*/
199
fun placeRelative(position: IntOffset) {
200
placeRelativeAt(position, 0f, null)
201
}
202
203
/**
204
* The scope for placing children.
205
*/
206
abstract class PlacementScope {
207
/**
208
* The layout direction for placing children.
209
*/
210
protected abstract val parentLayoutDirection: LayoutDirection
211
212
/**
213
* The width of the parent layout.
214
*/
215
protected abstract val parentWidth: Int
216
217
/**
218
* Convert coordinates based on layout direction.
219
*/
220
protected fun Placeable.placeRelativeAt(
221
position: IntOffset,
222
zIndex: Float,
223
layerBlock: (GraphicsLayerScope.() -> Unit)?
224
)
225
}
226
}
227
```
228
229
**Usage Examples:**
230
231
```kotlin
232
// Simple custom layout that arranges children vertically
233
@Composable
234
fun SimpleColumn(
235
modifier: Modifier = Modifier,
236
content: @Composable () -> Unit
237
) {
238
Layout(
239
content = content,
240
modifier = modifier
241
) { measurables, constraints ->
242
// Measure all children with available width
243
val placeables = measurables.map { measurable ->
244
measurable.measure(constraints.copy(minHeight = 0))
245
}
246
247
// Calculate total height
248
val totalHeight = placeables.sumOf { it.height }
249
val maxWidth = placeables.maxOfOrNull { it.width } ?: 0
250
251
// Return layout result
252
layout(maxWidth, totalHeight) {
253
var yPosition = 0
254
placeables.forEach { placeable ->
255
placeable.place(x = 0, y = yPosition)
256
yPosition += placeable.height
257
}
258
}
259
}
260
}
261
262
// Custom layout with alignment
263
@Composable
264
fun AlignedLayout(
265
alignment: Alignment.Horizontal,
266
modifier: Modifier = Modifier,
267
content: @Composable () -> Unit
268
) {
269
Layout(
270
content = content,
271
modifier = modifier
272
) { measurables, constraints ->
273
val placeables = measurables.map { it.measure(constraints) }
274
275
val maxWidth = placeables.maxOfOrNull { it.width } ?: 0
276
val totalHeight = placeables.sumOf { it.height }
277
278
layout(maxWidth, totalHeight) {
279
var yPosition = 0
280
placeables.forEach { placeable ->
281
val xPosition = alignment.align(
282
size = placeable.width,
283
space = maxWidth,
284
layoutDirection = layoutDirection
285
)
286
placeable.place(x = xPosition, y = yPosition)
287
yPosition += placeable.height
288
}
289
}
290
}
291
}
292
```
293
294
### Layout Constraints
295
296
Constraint system for defining size requirements and limitations during layout measurement.
297
298
```kotlin { .api }
299
/**
300
* Immutable constraints for measuring child layouts.
301
*/
302
@Immutable
303
data class Constraints(
304
val minWidth: Int = 0,
305
val maxWidth: Int = Infinity,
306
val minHeight: Int = 0,
307
val maxHeight: Int = Infinity
308
) {
309
init {
310
require(minWidth >= 0 && minHeight >= 0) { "minWidth and minHeight must be >= 0" }
311
require(maxWidth >= minWidth) { "maxWidth must be >= minWidth" }
312
require(maxHeight >= minHeight) { "maxHeight must be >= minHeight" }
313
}
314
315
companion object {
316
/**
317
* A value that represents positive infinity for constraints.
318
*/
319
const val Infinity = Int.MAX_VALUE
320
321
/**
322
* Constraints with fixed width and height.
323
*/
324
fun fixed(width: Int, height: Int): Constraints
325
326
/**
327
* Constraints with fixed width.
328
*/
329
fun fixedWidth(width: Int): Constraints
330
331
/**
332
* Constraints with fixed height.
333
*/
334
fun fixedHeight(height: Int): Constraints
335
}
336
337
/**
338
* Whether the constraints allow only a single width value.
339
*/
340
val hasBoundedWidth: Boolean
341
342
/**
343
* Whether the constraints allow only a single height value.
344
*/
345
val hasBoundedHeight: Boolean
346
347
/**
348
* Whether there is exactly one width value that satisfies the constraints.
349
*/
350
val hasFixedWidth: Boolean
351
352
/**
353
* Whether there is exactly one height value that satisfies the constraints.
354
*/
355
val hasFixedHeight: Boolean
356
357
/**
358
* Whether the constraints specify bounded dimensions.
359
*/
360
val isZero: Boolean
361
362
/**
363
* Coerces width within the constraints.
364
*/
365
fun constrainWidth(width: Int): Int
366
367
/**
368
* Coerces height within the constraints.
369
*/
370
fun constrainHeight(height: Int): Int
371
372
/**
373
* Create new constraints by changing individual values.
374
*/
375
fun copy(
376
minWidth: Int = this.minWidth,
377
maxWidth: Int = this.maxWidth,
378
minHeight: Int = this.minHeight,
379
maxHeight: Int = this.maxHeight
380
): Constraints
381
382
/**
383
* Returns the result of coercing the given size within the constraints.
384
*/
385
fun constrain(size: IntSize): IntSize
386
387
/**
388
* Returns constraints with the width constrained between the given values.
389
*/
390
fun constrainWidth(minWidth: Int = this.minWidth, maxWidth: Int = this.maxWidth): Constraints
391
392
/**
393
* Returns constraints with the height constrained between the given values.
394
*/
395
fun constrainHeight(minHeight: Int = this.minHeight, maxHeight: Int = this.maxHeight): Constraints
396
397
/**
398
* Returns the maximum size that fits these constraints.
399
*/
400
val maxSize: IntSize
401
402
/**
403
* Returns the minimum size that fits these constraints.
404
*/
405
val minSize: IntSize
406
}
407
```
408
409
**Usage Examples:**
410
411
```kotlin
412
// Working with constraints in custom layouts
413
@Composable
414
fun ConstraintAwareLayout(
415
modifier: Modifier = Modifier,
416
content: @Composable () -> Unit
417
) {
418
Layout(
419
content = content,
420
modifier = modifier
421
) { measurables, constraints ->
422
// Check constraint properties
423
val hasFixedWidth = constraints.hasFixedWidth
424
val hasBoundedHeight = constraints.hasBoundedHeight
425
426
// Create modified constraints for children
427
val childConstraints = constraints.copy(
428
minWidth = 0,
429
minHeight = 0,
430
maxWidth = if (constraints.hasBoundedWidth) constraints.maxWidth / 2 else Constraints.Infinity
431
)
432
433
// Measure children with modified constraints
434
val placeables = measurables.map { measurable ->
435
measurable.measure(childConstraints)
436
}
437
438
// Calculate layout size respecting constraints
439
val totalWidth = placeables.sumOf { it.width }
440
val maxHeight = placeables.maxOfOrNull { it.height } ?: 0
441
442
val layoutWidth = constraints.constrainWidth(totalWidth)
443
val layoutHeight = constraints.constrainHeight(maxHeight)
444
445
layout(layoutWidth, layoutHeight) {
446
var xPosition = 0
447
placeables.forEach { placeable ->
448
placeable.place(x = xPosition, y = 0)
449
xPosition += placeable.width
450
}
451
}
452
}
453
}
454
455
// Fixed size constraints
456
val fixedConstraints = Constraints.fixed(width = 200, height = 100)
457
458
// Flexible constraints with limits
459
val flexibleConstraints = Constraints(
460
minWidth = 100,
461
maxWidth = 300,
462
minHeight = 50,
463
maxHeight = Constraints.Infinity
464
)
465
```
466
467
### Intrinsic Measurements
468
469
System for querying preferred dimensions of composables before final measurement and layout.
470
471
```kotlin { .api }
472
/**
473
* Interface for calculating intrinsic measurements.
474
*/
475
interface IntrinsicMeasurable {
476
/**
477
* Data from parent layout.
478
*/
479
val parentData: Any?
480
481
/**
482
* Calculate the minimum width for the given height.
483
*/
484
fun minIntrinsicWidth(height: Int): Int
485
486
/**
487
* Calculate the maximum width for the given height.
488
*/
489
fun maxIntrinsicWidth(height: Int): Int
490
491
/**
492
* Calculate the minimum height for the given width.
493
*/
494
fun minIntrinsicHeight(width: Int): Int
495
496
/**
497
* Calculate the maximum height for the given width.
498
*/
499
fun maxIntrinsicHeight(width: Int): Int
500
}
501
502
/**
503
* Scope for intrinsic measurement calculations.
504
*/
505
interface IntrinsicMeasureScope : Density {
506
/**
507
* The layout direction for intrinsic calculations.
508
*/
509
val layoutDirection: LayoutDirection
510
}
511
512
/**
513
* Marker interface for measured layouts.
514
*/
515
interface Measured {
516
/**
517
* The measured width.
518
*/
519
val measuredWidth: Int
520
521
/**
522
* The measured height.
523
*/
524
val measuredHeight: Int
525
526
/**
527
* Get the position of an alignment line, or AlignmentLine.Unspecified.
528
*/
529
operator fun get(alignmentLine: AlignmentLine): Int
530
}
531
```
532
533
**Usage Examples:**
534
535
```kotlin
536
// Layout using intrinsic measurements
537
@Composable
538
fun IntrinsicAwareLayout(
539
modifier: Modifier = Modifier,
540
content: @Composable () -> Unit
541
) {
542
Layout(
543
content = content,
544
modifier = modifier,
545
measurePolicy = object : MeasurePolicy {
546
override fun MeasureScope.measure(
547
measurables: List<Measurable>,
548
constraints: Constraints
549
): MeasureResult {
550
// Query intrinsic sizes before measuring
551
val minIntrinsicWidth = measurables.sumOf {
552
it.minIntrinsicWidth(Constraints.Infinity)
553
}
554
val maxIntrinsicHeight = measurables.maxOfOrNull {
555
it.maxIntrinsicHeight(constraints.maxWidth)
556
} ?: 0
557
558
// Use intrinsic information to make measurement decisions
559
val childConstraints = if (minIntrinsicWidth <= constraints.maxWidth) {
560
constraints.copy(minWidth = 0)
561
} else {
562
constraints.copy(maxWidth = constraints.maxWidth / measurables.size)
563
}
564
565
val placeables = measurables.map { measurable ->
566
measurable.measure(childConstraints)
567
}
568
569
val layoutWidth = constraints.constrainWidth(
570
placeables.sumOf { it.width }
571
)
572
val layoutHeight = constraints.constrainHeight(
573
placeables.maxOfOrNull { it.height } ?: 0
574
)
575
576
layout(layoutWidth, layoutHeight) {
577
var xPosition = 0
578
placeables.forEach { placeable ->
579
placeable.place(x = xPosition, y = 0)
580
xPosition += placeable.width
581
}
582
}
583
}
584
585
override fun IntrinsicMeasureScope.minIntrinsicWidth(
586
measurables: List<IntrinsicMeasurable>,
587
height: Int
588
): Int {
589
return measurables.sumOf { it.minIntrinsicWidth(height) }
590
}
591
592
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
593
measurables: List<IntrinsicMeasurable>,
594
height: Int
595
): Int {
596
return measurables.sumOf { it.maxIntrinsicWidth(height) }
597
}
598
}
599
)
600
}
601
602
// Using intrinsic size modifiers
603
@Composable
604
fun IntrinsicExample() {
605
Column {
606
// Width based on intrinsic measurements
607
Row(
608
modifier = Modifier.width(IntrinsicSize.Max)
609
) {
610
Text("Short", modifier = Modifier.fillMaxHeight())
611
Text("Much longer text", modifier = Modifier.fillMaxHeight())
612
}
613
614
// Height based on intrinsic measurements
615
Column(
616
modifier = Modifier.height(IntrinsicSize.Min)
617
) {
618
Text("Line 1")
619
Text("Line 2 with more content")
620
}
621
}
622
}
623
```
624
625
### Layout Events and Callbacks
626
627
System for responding to layout changes, positioning updates, and size modifications.
628
629
```kotlin { .api }
630
/**
631
* Modifier that provides callback for global position changes.
632
*/
633
fun Modifier.onGloballyPositioned(
634
onGloballyPositioned: (LayoutCoordinates) -> Unit
635
): Modifier
636
637
/**
638
* Modifier that provides callback for size changes.
639
*/
640
fun Modifier.onSizeChanged(
641
onSizeChanged: (IntSize) -> Unit
642
): Modifier
643
644
/**
645
* Modifier that provides callback for placement changes.
646
*/
647
fun Modifier.onPlaced(
648
onPlaced: (LayoutCoordinates) -> Unit
649
): Modifier
650
651
/**
652
* A coordinate system for a layout.
653
*/
654
interface LayoutCoordinates {
655
/**
656
* The size of this layout in the local coordinate system.
657
*/
658
val size: IntSize
659
660
/**
661
* Whether this coordinate system is attached to the root of the composition.
662
*/
663
val isAttached: Boolean
664
665
/**
666
* The LayoutCoordinates of the parent layout modifier or parent layout.
667
*/
668
val parentLayoutCoordinates: LayoutCoordinates?
669
670
/**
671
* The LayoutCoordinates of the parent composable.
672
*/
673
val parentCoordinates: LayoutCoordinates?
674
675
/**
676
* Converts a local position to an ancestor coordinate system.
677
*/
678
fun localToWindow(relativeToLocal: Offset): Offset
679
680
/**
681
* Converts a local position to the root coordinate system.
682
*/
683
fun localToRoot(relativeToLocal: Offset): Offset
684
685
/**
686
* Converts a position in the ancestor coordinate system to local.
687
*/
688
fun windowToLocal(relativeToWindow: Offset): Offset
689
690
/**
691
* Converts a position in the root coordinate system to local.
692
*/
693
fun rootToLocal(relativeToRoot: Offset): Offset
694
695
/**
696
* Converts a local position to the coordinate system of another layout.
697
*/
698
fun localPositionOf(
699
sourceCoordinates: LayoutCoordinates,
700
relativeToSource: Offset
701
): Offset
702
703
/**
704
* Converts a local bounding box to the coordinate system of another layout.
705
*/
706
fun localBoundingBoxOf(
707
sourceCoordinates: LayoutCoordinates,
708
clipBounds: Boolean = true
709
): Rect
710
711
/**
712
* Gets the position of an alignment line in the local coordinate system.
713
*/
714
operator fun get(alignmentLine: AlignmentLine): Int
715
}
716
717
/**
718
* Intrinsic size values for width and height constraints.
719
*/
720
enum class IntrinsicSize { Min, Max }
721
722
/**
723
* Modifier for setting intrinsic size constraints.
724
*/
725
fun Modifier.width(intrinsicSize: IntrinsicSize): Modifier
726
fun Modifier.height(intrinsicSize: IntrinsicSize): Modifier
727
fun Modifier.requiredWidth(intrinsicSize: IntrinsicSize): Modifier
728
fun Modifier.requiredHeight(intrinsicSize: IntrinsicSize): Modifier
729
```
730
731
**Usage Examples:**
732
733
```kotlin
734
// Responding to layout position changes
735
@Composable
736
fun PositionAwareBox() {
737
var position by remember { mutableStateOf(Offset.Zero) }
738
var size by remember { mutableStateOf(IntSize.Zero) }
739
740
Box(
741
modifier = Modifier
742
.size(200.dp)
743
.background(Color.Blue)
744
.onGloballyPositioned { coordinates ->
745
position = coordinates.localToRoot(Offset.Zero)
746
size = coordinates.size
747
}
748
.onSizeChanged { newSize ->
749
// React to size changes
750
println("Size changed to: $newSize")
751
}
752
) {
753
Text(
754
text = "Position: (${position.x.toInt()}, ${position.y.toInt()})\nSize: ${size.width} x ${size.height}",
755
color = Color.White,
756
modifier = Modifier.align(Alignment.Center)
757
)
758
}
759
}
760
761
// Coordinate system conversions
762
@Composable
763
fun CoordinateExample() {
764
var clickPosition by remember { mutableStateOf(Offset.Zero) }
765
766
Box(
767
modifier = Modifier
768
.fillMaxSize()
769
.onGloballyPositioned { rootCoordinates ->
770
// Store root coordinates for conversions
771
}
772
.pointerInput(Unit) {
773
detectTapGestures { tapOffset ->
774
clickPosition = tapOffset
775
}
776
}
777
) {
778
Box(
779
modifier = Modifier
780
.size(100.dp)
781
.background(Color.Red)
782
.align(Alignment.Center)
783
.onGloballyPositioned { childCoordinates ->
784
// Convert click position to child coordinate system
785
val localPosition = childCoordinates.windowToLocal(clickPosition)
786
if (childCoordinates.size.toRect().contains(localPosition)) {
787
println("Clicked inside child at local position: $localPosition")
788
}
789
}
790
)
791
}
792
}
793
```
794
795
### Parent Data and Layout Parameters
796
797
System for passing layout-specific data from child to parent composables during layout.
798
799
```kotlin { .api }
800
/**
801
* Modifier for attaching parent data to a layout.
802
*/
803
fun Modifier.parentData(parentData: Any?): Modifier
804
805
/**
806
* Marker interface for parent data classes.
807
*/
808
interface ParentDataModifier : Modifier.Element {
809
/**
810
* Modify the parent data.
811
*/
812
fun Density.modifyParentData(parentData: Any?): Any?
813
}
814
815
/**
816
* Alignment lines for coordinating layout alignment.
817
*/
818
abstract class AlignmentLine(internal val merger: (Int, Int) -> Int) {
819
companion object {
820
/**
821
* Unspecified alignment line position.
822
*/
823
const val Unspecified: Int = Int.MIN_VALUE
824
}
825
}
826
827
/**
828
* Horizontal alignment line.
829
*/
830
abstract class HorizontalAlignmentLine(merger: (Int, Int) -> Int) : AlignmentLine(merger)
831
832
/**
833
* Vertical alignment line.
834
*/
835
abstract class VerticalAlignmentLine(merger: (Int, Int) -> Int) : AlignmentLine(merger)
836
837
/**
838
* Common alignment lines.
839
*/
840
object AlignmentLineOffset {
841
/**
842
* First baseline of text.
843
*/
844
val FirstBaseline: HorizontalAlignmentLine
845
846
/**
847
* Last baseline of text.
848
*/
849
val LastBaseline: HorizontalAlignmentLine
850
}
851
852
/**
853
* Modifier for providing alignment lines.
854
*/
855
fun Modifier.alignmentLineOffset(
856
alignmentLine: AlignmentLine,
857
before: Dp = 0.dp,
858
after: Dp = 0.dp
859
): Modifier
860
861
/**
862
* Modifier for aligning by a baseline.
863
*/
864
fun Modifier.alignBy(alignmentLine: HorizontalAlignmentLine): Modifier
865
fun Modifier.alignBy(alignmentLineBlock: (Measured) -> Int): Modifier
866
```
867
868
**Usage Examples:**
869
870
```kotlin
871
// Custom parent data for layout parameters
872
data class FlexChildData(
873
val flex: Float,
874
val alignment: Alignment.Vertical? = null
875
)
876
877
fun Modifier.flex(flex: Float, alignment: Alignment.Vertical? = null) = this.then(
878
object : ParentDataModifier {
879
override fun Density.modifyParentData(parentData: Any?): FlexChildData {
880
return FlexChildData(flex, alignment)
881
}
882
}
883
)
884
885
@Composable
886
fun FlexLayout(
887
modifier: Modifier = Modifier,
888
content: @Composable () -> Unit
889
) {
890
Layout(
891
content = content,
892
modifier = modifier
893
) { measurables, constraints ->
894
// Access parent data from children
895
val flexData = measurables.map { measurable ->
896
(measurable.parentData as? FlexChildData) ?: FlexChildData(flex = 1f)
897
}
898
899
val totalFlex = flexData.sumOf { it.flex.toDouble() }.toFloat()
900
val availableWidth = constraints.maxWidth
901
902
val placeables = measurables.mapIndexed { index, measurable ->
903
val flex = flexData[index].flex
904
val childWidth = ((availableWidth * flex) / totalFlex).toInt()
905
906
measurable.measure(
907
constraints.copy(
908
minWidth = childWidth,
909
maxWidth = childWidth
910
)
911
)
912
}
913
914
val maxHeight = placeables.maxOfOrNull { it.height } ?: 0
915
916
layout(availableWidth, maxHeight) {
917
var xPosition = 0
918
placeables.forEachIndexed { index, placeable ->
919
val alignment = flexData[index].alignment ?: Alignment.CenterVertically
920
val yPosition = alignment.align(placeable.height, maxHeight)
921
922
placeable.place(x = xPosition, y = yPosition)
923
xPosition += placeable.width
924
}
925
}
926
}
927
}
928
929
// Using the flex layout
930
@Composable
931
fun FlexExample() {
932
FlexLayout(
933
modifier = Modifier.fillMaxWidth()
934
) {
935
Box(
936
modifier = Modifier
937
.flex(1f, Alignment.Top)
938
.height(50.dp)
939
.background(Color.Red)
940
)
941
Box(
942
modifier = Modifier
943
.flex(2f, Alignment.CenterVertically)
944
.height(100.dp)
945
.background(Color.Green)
946
)
947
Box(
948
modifier = Modifier
949
.flex(1f, Alignment.Bottom)
950
.height(75.dp)
951
.background(Color.Blue)
952
)
953
}
954
}
955
956
// Alignment line example
957
@Composable
958
fun BaselineAlignedRow() {
959
Row {
960
Text(
961
text = "Small",
962
fontSize = 12.sp,
963
modifier = Modifier.alignBy(FirstBaseline)
964
)
965
Text(
966
text = "Large",
967
fontSize = 24.sp,
968
modifier = Modifier.alignBy(FirstBaseline)
969
)
970
Text(
971
text = "Medium",
972
fontSize = 18.sp,
973
modifier = Modifier.alignBy(FirstBaseline)
974
)
975
}
976
}
977
```