6

I am working in textfield in jetpack compose. I want to build something like this

enter image description here

TextField(
            value = value,
            onValueChange = {
                value = it
            },
            modifier = Modifier
                .requiredWidth(56.dp)
                .padding(10.dp),
            colors = TextFieldDefaults.textFieldColors(
                backgroundColor = OffWhite,
                focusedIndicatorColor = TealLight,
                cursorColor = TealLight
            )
        )

Scenario 1

When I am trying this

 .requiredWidth(56.dp)

or

.width(56.dp)

It looks like this in the design

enter image description here

Scenario 2

When I try this

.widthIn(min = 56.dp)

It look like this

enter image description here

Scenario 3

When I try this

.fillMaxWidth()

It takes the whole screen width.

So my question is which one is used for which case? and what would be best attribute in my case?

Kotlin Learner
  • 3,995
  • 6
  • 47
  • 127

3 Answers3

13

In jetpack Compose dimensions of Composable or child Composables are set using Constraints which is set of dimensions and flags for being bounded or finite. And be Modifier.requiredX are used for forcing size but it can easily break your layout if the constraints are not in limit of parents.

Difference between widthIn and requiredWidthIn is first one abides size modifiers before it or max parent dimensions while latter can force constraints.

/**
 * Create a [Constraints]. [minWidth] and [minHeight] must be positive and
 * [maxWidth] and [maxHeight] must be greater than or equal to [minWidth] and [minHeight],
 * respectively, or [Infinity][Constraints.Infinity].
 */
@Stable
fun Constraints(
    minWidth: Int = 0,
    maxWidth: Int = Constraints.Infinity,
    minHeight: Int = 0,
    maxHeight: Int = Constraints.Infinity
): Constraints {
    require(maxWidth >= minWidth) {
        "maxWidth($maxWidth) must be >= than minWidth($minWidth)"
    }
    require(maxHeight >= minHeight) {
        "maxHeight($maxHeight) must be >= than minHeight($minHeight)"
    }
    require(minWidth >= 0 && minHeight >= 0) {
        "minWidth($minWidth) and minHeight($minHeight) must be >= 0"
    }
    return Constraints.createConstraints(minWidth, maxWidth, minHeight, maxHeight)
}

based on Constraints during measurement width and height are assigned to Composables as Placeables.

In the sample below when TextField doesn't have parent with fixed size it grows till max width. If fixed size that is smaller than TextField is set when Modifier.widthIn() is used TextField is set to parent width while Modifier.requiredWidthIn() jumps out of parent as (TextField size - parent size)/2

enter image description here

@Composable
private fun TextFieldSamples() {

    Column(
        modifier = Modifier
            .padding(20.dp).border(2.dp, Color.Cyan)
    ) {

        var text1 by remember { mutableStateOf("") }

        Column(modifier = Modifier) {
            TextField(
                modifier = Modifier
                    .border(2.dp, Color.Green)
                    .widthIn(56.dp),
                value = text1,
                onValueChange = { text1 = it }
            )

            TextField(
                modifier = Modifier
                    .border(2.dp, Color.Red)
                    .requiredWidthIn(56.dp),
                value = text1, onValueChange = { text1 = it })
        }

        Spacer(modifier = Modifier.height(30.dp))

        var text2 by remember { mutableStateOf("") }

        Column(modifier = Modifier.width(50.dp)) {
            TextField(
                modifier = Modifier
                    .border(2.dp, Color.Green)
                    .widthIn(56.dp),
                value = text2, onValueChange = { text2 = it })

            TextField(
                modifier = Modifier
                    .border(2.dp, Color.Red)
                    .requiredWidthIn(56.dp),
                value = text2, onValueChange = { text2 = it })
        }
    }
}

Size Modifiers

Modifier.width/height()

Returns fixed Constraints or one with equal minWidth/Height and maxWidth/Height. So Child Composables are limited to this Constraints

Modifier.fillmaxWidth/Height(fraction)

Covers fraction of space that is available from parent's max constraints

Modifier.width/heightIn(min)

Reserves minimum width/height as this value so child Composables can be measured using this dimension. Children can be lower than this value or greater until max width/height

Modifier.width/heightIn(max)

This is the max width/height children can be measured with. Children Composables cannot be measured with dimension bigger than max width/height.

@Composable
private fun ConstraintsSample1() {
    Text(text = "Fixed Size")
    BoxWithConstraints(modifier = Modifier
        .size(100.dp)
        .border(3.dp, Color.Green)) {
        Box(modifier = Modifier
            .size(50.dp)
            .background(Color.Red))
    }

    Spacer(modifier=Modifier.height(10.dp))
    BoxWithConstraints(modifier = Modifier
        .size(100.dp)
        .border(3.dp, Color.Green)) {
        Box(modifier = Modifier
            .size(150.dp)
            .background(Color.Red))
    }

    Text(text = "widthIn(min)")

    BoxWithConstraints(modifier = Modifier
        .widthIn(min = 100.dp)
        .border(3.dp, Color.Green)) {
        Box(modifier = Modifier
            .size(50.dp)
            .background(Color.Red))
    }

    Spacer(modifier=Modifier.height(10.dp))
    BoxWithConstraints(modifier = Modifier
        .widthIn(min = 100.dp)
        .border(3.dp, Color.Green)) {
        Box(modifier = Modifier
            .size(150.dp)
            .background(Color.Red))
    }


    Text(text = "widthIn(max)")

    BoxWithConstraints(modifier = Modifier
        .widthIn(max = 100.dp)
        .border(3.dp, Color.Green)) {
        Box(modifier = Modifier
            .size(50.dp)
            .background(Color.Red))
    }

    Spacer(modifier=Modifier.height(10.dp))
    BoxWithConstraints(modifier = Modifier
        .widthIn(max = 100.dp)
        .border(3.dp, Color.Green)) {
        Box(modifier = Modifier
            .size(150.dp)
            .background(Color.Red))
    }
}

Modifier.requiredWidth/Height()

Constrain the width/height of the content to be between `minDp` and `maxDp`. 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.

For instance when you set Modifier.size(50.dp).size(100.dp) first size modifier is applied however if you user requiredIn

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

100.dp is forced but layout is placed as half of difference between 2 dimensions

@Composable
private fun ConstraintsSample2() {

    Column(modifier = Modifier
        .fillMaxSize()
        .padding(30.dp)
        .border(4.dp, Color.Cyan)) {

        Text(text = "Chaining size modifiers")

        Box(modifier = Modifier
            .size(50.dp)
            .background(Color.Yellow))

        Box(modifier = Modifier
            .size(50.dp)
            .size(100.dp)
            .background(Color.Red))

        Box(modifier = Modifier
            .size(50.dp)
            .requiredSizeIn(100.dp)
            .background(Color.Green))


        Text(text = "widthIn(max)")

        BoxWithConstraints(
            modifier = Modifier
                .width(100.dp)
                .border(3.dp, Color.Green)
        ) {
            Box(
                modifier = Modifier
                    .requiredWidthIn(min = 20.dp, max = 50.dp)
                    .height(50.dp)
                    .background(Color.Red)
            )
        }

        Spacer(modifier = Modifier.height(10.dp))
        BoxWithConstraints(
            modifier = Modifier
                .width(100.dp)
                .border(3.dp, Color.Green)
        ) {
            Box(
                modifier = Modifier
                    .requiredWidthIn(min = 150.dp, max = 200.dp)
                    .height(50.dp)
                    .background(Color.Red)
            )
        }
    }
}

Constraints

Constraints change based on size modifiers, vertical/horizontal scroll or how parent chooses to limit or want to use min or max dimension.

Max device on my device width is 1080px, and 200.dp is 525px in the samples below. And used verticalScroll that's why height is measured with Constraints.Infinity

@Composable
fun ConstraintsSample3() {
    Column(modifier = Modifier) {

        Text(text = "No Dimension Modifier")

        BoxWithConstraints(modifier = Modifier.background(Brown400)) {
            val hasBoundedWidth = constraints.hasBoundedWidth
            val hasFixedWidth = constraints.hasFixedWidth
            val minWidth = constraints.minWidth
            val maxWidth = constraints.maxWidth

            val hasBoundedHeight = constraints.hasBoundedHeight
            val hasFixedHeight = constraints.hasFixedHeight
            val minHeight = constraints.minHeight
            val maxHeight = constraints.maxHeight
            Text(
                "minWidth: $minWidth, maxWidth: $maxWidth\n" +
                        "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\n" +
                        "minHeight: $minHeight, maxHeight: $maxHeight\n" +
                        "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",
                color = Color.White
            )
        }

        Spacer(modifier = Modifier.height(10.dp))
        Text(text = "FillMaxWidth and 200.dp Height")
        BoxWithConstraints(
            modifier = Modifier
                .fillMaxWidth()
                .height(200.dp)
                .background(Red400)
        ) {
            val hasBoundedWidth = constraints.hasBoundedWidth
            val hasFixedWidth = constraints.hasFixedWidth
            val minWidth = constraints.minWidth
            val maxWidth = constraints.maxWidth

            val hasBoundedHeight = constraints.hasBoundedHeight
            val hasFixedHeight = constraints.hasFixedHeight
            val minHeight = constraints.minHeight
            val maxHeight = constraints.maxHeight
            Text(
                "minWidth: $minWidth, maxWidth: $maxWidth\n" +
                        "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\n" +
                        "minHeight: $minHeight, maxHeight: $maxHeight\n" +
                        "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",
                color = Color.White
            )
        }

        Spacer(modifier = Modifier.height(10.dp))
        Text(text = "wrapContentSize()")
        BoxWithConstraints(
            modifier = Modifier
                .wrapContentSize()
                .background(Orange400)
        ) {

            val hasBoundedWidth = constraints.hasBoundedWidth
            val hasFixedWidth = constraints.hasFixedWidth
            val minWidth = constraints.minWidth
            val maxWidth = constraints.maxWidth

            val hasBoundedHeight = constraints.hasBoundedHeight
            val hasFixedHeight = constraints.hasFixedHeight
            val minHeight = constraints.minHeight
            val maxHeight = constraints.maxHeight
            Text(
                "minWidth: $minWidth, maxWidth: $maxWidth\n" +
                        "hasBoundedWidth: $hasBoundedWidth\n" +
                        "hasFixedWidth: $hasFixedWidth\n" +
                        "minHeight: $minHeight\n" +
                        "maxHeight: $maxHeight\n" +
                        "hasBoundedHeight: $hasBoundedHeight\n" +
                        "hasFixedHeight: $hasFixedHeight",
                color = Color.White
            )
        }

        Spacer(modifier = Modifier.height(10.dp))
        Text(text = "200.dp Width and Height")
        BoxWithConstraints(
            modifier = Modifier
                .width(200.dp)
                .height(200.dp)
                .background(Green400)
        ) {

            val hasBoundedWidth = constraints.hasBoundedWidth
            val hasFixedWidth = constraints.hasFixedWidth
            val minWidth = constraints.minWidth
            val maxWidth = constraints.maxWidth

            val hasBoundedHeight = constraints.hasBoundedHeight
            val hasFixedHeight = constraints.hasFixedHeight
            val minHeight = constraints.minHeight
            val maxHeight = constraints.maxHeight
            Text(
                "minWidth: $minWidth, maxWidth: $maxWidth\n" +
                        "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\n" +
                        "minHeight: $minHeight, maxHeight: $maxHeight\n" +
                        "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",
                color = Color.White
            )
        }
    }
}
@Composable
private fun BoxWithConstraintsSample4() {
    Text(text = "200.dp WidthIn(min) and HeightIn(min)")
    BoxWithConstraints(
        modifier = Modifier
            .widthIn(min = 200.dp)
            .heightIn(200.dp)
            .background(Blue400)
    ) {

        val hasBoundedWidth = constraints.hasBoundedWidth
        val hasFixedWidth = constraints.hasFixedWidth
        val minWidth = constraints.minWidth
        val maxWidth = constraints.maxWidth

        val hasBoundedHeight = constraints.hasBoundedHeight
        val hasFixedHeight = constraints.hasFixedHeight
        val minHeight = constraints.minHeight
        val maxHeight = constraints.maxHeight
        Text(
            "minWidth: $minWidth, maxWidth: $maxWidth\n" +
                    "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\n" +
                    "minHeight: $minHeight, maxHeight: $maxHeight\n" +
                    "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",
            color = Color.White
        )
    }

    Text(text = "200.dp requiredWidth(min) and requiredHeight(min)")
    BoxWithConstraints(
        modifier = Modifier
            .requiredWidthIn(min = 200.dp)
            .requiredHeightIn(200.dp)
            .background(Pink400)
    ) {

        val hasBoundedWidth = constraints.hasBoundedWidth
        val hasFixedWidth = constraints.hasFixedWidth
        val minWidth = constraints.minWidth
        val maxWidth = constraints.maxWidth

        val hasBoundedHeight = constraints.hasBoundedHeight
        val hasFixedHeight = constraints.hasFixedHeight
        val minHeight = constraints.minHeight
        val maxHeight = constraints.maxHeight
        Text(
            "minWidth: $minWidth, maxWidth: $maxWidth\n" +
                    "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\n" +
                    "minHeight: $minHeight, maxHeight: $maxHeight\n" +
                    "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",
            color = Color.White
        )
    }

    Text(text = "200.dp defaultMinSize()")
    BoxWithConstraints(
        modifier = Modifier
            .defaultMinSize(200.dp)
            .background(Pink400)
    ) {

        val hasBoundedWidth = constraints.hasBoundedWidth
        val hasFixedWidth = constraints.hasFixedWidth
        val minWidth = constraints.minWidth
        val maxWidth = constraints.maxWidth

        val hasBoundedHeight = constraints.hasBoundedHeight
        val hasFixedHeight = constraints.hasFixedHeight
        val minHeight = constraints.minHeight
        val maxHeight = constraints.maxHeight
        Text(
            "minWidth: $minWidth, maxWidth: $maxWidth\n" +
                    "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\n" +
                    "minHeight: $minHeight, maxHeight: $maxHeight\n" +
                    "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",
            color = Color.White
        )
    }

    Text(text = "200.dp WidthIn(max)")
    BoxWithConstraints(
        modifier = Modifier
            .widthIn(max = 200.dp)
            .background(Purple400)
    ) {

        val hasBoundedWidth = constraints.hasBoundedWidth
        val hasFixedWidth = constraints.hasFixedWidth
        val minWidth = constraints.minWidth
        val maxWidth = constraints.maxWidth

        val hasBoundedHeight = constraints.hasBoundedHeight
        val hasFixedHeight = constraints.hasFixedHeight
        val minHeight = constraints.minHeight
        val maxHeight = constraints.maxHeight
        Text(
            "minWidth: $minWidth, maxWidth: $maxWidth\n" +
                    "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\n" +
                    "minHeight: $minHeight, maxHeight: $maxHeight\n" +
                    "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",
            color = Color.White
        )
    }
}
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • I really appreciate it. – Kotlin Learner Aug 06 '22 at 12:15
  • So If I want to make my above screenshot I need to use `requireWidth` with height? – Kotlin Learner Aug 06 '22 at 12:16
  • If you don't want your TextFieldSize to change when String changes use Modifier.width(56.dp), if you want it have minimum width of 56.dp and grow when String grows use Modifier.widthIn(min=56.dp) or Modifier.requiredWidthIn(min=56.dp). However with requiredWidthIn be careful to not have a parent with less than 56.dp width – Thracian Aug 06 '22 at 12:35
  • Updated answer. Try TextFields to see for yourself. See in last TextField with requiredWidthIn if parent has smaller width TextField overflows to left. This is what you should be careful about. Purpose of required is to force your Modifier if there is another but it should be in bounds of parent to not break or jump your composable out of parent – Thracian Aug 06 '22 at 12:43
  • I think I need to open another question because my textfield inside text is not visible clearly with 56dp. I need to customise the text. – Kotlin Learner Aug 06 '22 at 13:43
2

For your use-case requiredWidth() is to be used.

1. fillMaxWidth

Use to fill the available width.
It takes a fraction as parameter, and defaults to 1.0F if not provided.

2. widthIn

Use widthIn to provide a size range.
It takes min width and max width as parameters

3. requiredWidth

Use requiredWidth to force to a specific width.
(This is for your use-case)

4. width

Use width to specify a preferred width. The given preferred width is used if there are no constraints overriding the given preference.

Sample code

@Composable
fun WidthTypes() {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.SpaceEvenly,
    ) {
        Box(
            modifier = Modifier
                .background(Red)
                .height(40.dp)
                .fillMaxWidth(0.9F),
        ) {
            Box(
                modifier = Modifier
                    .background(Blue)
                    .height(10.dp)
                    .width(80.dp),
            )
        }
        Box(
            modifier = Modifier
                .background(Red)
                .height(40.dp)
                .widthIn(40.dp, 100.dp),
        ) {
            Box(
                modifier = Modifier
                    .background(Blue)
                    .height(10.dp)
                    .width(80.dp),
            )
        }
        Box(
            modifier = Modifier
                .background(Red)
                .height(40.dp)
                .requiredWidth(40.dp),
        ) {
            Box(
                modifier = Modifier
                    .background(Blue)
                    .height(10.dp)
                    .width(80.dp),
            )
        }
        Box(
            modifier = Modifier
                .background(Red)
                .height(40.dp)
                .widthIn(70.dp, 100.dp)
                .width(40.dp),
        ) {
            Box(
                modifier = Modifier
                    .background(Blue)
                    .height(10.dp)
                    .width(80.dp),
            )
        }
    }
}

Note, how the 4th red box is 70.dp even though width is specified as 40.dp. This is because the widthIn is used which overrides the width value.

This does not happen when we use requiredWidth.

Abhimanyu
  • 11,351
  • 7
  • 51
  • 121
0

I don't know if i got your question right

If you were asking help to make textfield just like the reference image: the first scenario fixed the width and you want to add height adjustment too using .height

In 2nd Scenario: when you give a min value it is allowed to make the size anything more than 56.dp. it not a option for your case since you want a fixed size

Dilshad
  • 101
  • 6