Change the trailingIcon size of a TextField - android

TextField trainlingIcon has a default size inside the class it wraps the composable inside a box with a default size
TextField.kt
Box(
modifier = Modifier.layoutId(TrailingId).then(IconDefaultSizeModifier),
contentAlignment = Alignment.Center
) {
Decoration(
contentColor = trailingColor,
content = trailing
)
}
}
where the default value is defined in TextFieldImple.Kt
internal val IconDefaultSizeModifier = Modifier.defaultMinSize(48.dp, 48.dp)
this causes unwanted space for the textField
is there a way to override this?

Related

How to show semi-transparent loading overlay above full Composable

I am trying to create a Composable that wraps another content Composable and displays a CircularProgressBar as an overlay on top of it, covering the whole content Composable.
I almost got it working as wished, see the following to images:
Initial state
Loading state
But as you can see, the overlay fills the complete screen instead of just the gray LazyRow item and the text field. Thus, the button is pushed off screen.
This is my current code:
#Composable
fun LoadingBox(
modifier: Modifier = Modifier,
isLoading: Boolean = false,
loadingText: String,
content: #Composable() () -> Unit
) {
Box(modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
) {
content()
if (isLoading) {
Surface(
modifier = Modifier
.fillMaxSize()
.alpha(0.5f),
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
CircularProgressIndicator()
Text(text = loadingText)
}
}
}
}
}
On the screenshots, I am providing the gray box and the text field as the content parameter. So the overlay should only cover the gray LazyRow item and the text field.
I already stumbled across instrinsic measures, however I cannot use them as the App crashes when I provide a LazyRow as content due to following error:
java.lang.IllegalStateException: Asking for intrinsic measurements of SubcomposeLayout layouts is not supported. This includes components that are built on top of SubcomposeLayout, such as lazy lists, BoxWithConstraints, TabRow, etc. To mitigate this:
- if intrinsic measurements are used to achieve 'match parent' sizing,, consider replacing the parent of the component with a custom layout which controls the order in which children are measured, making intrinsic measurement not needed
You should:
add contentAlignment = Alignment.Center in the parent Box
Remove the Surface
remove the verticalArrangement in the Column
Add an other Box which you can fill with a translucent background
Something like:
Box(modifier = modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
content()
if (isLoading) {
Box(
Modifier
.matchParentSize()
.background(Color.Gray.copy(alpha = 0.5f))
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
androidx.compose.material.CircularProgressIndicator()
Text(text = loadingText)
}
}
}

Compose BasicTextField trailingIcon stretches the heigh of the field

I ran into an issue today where if I specify a trailing icon for a BasicTextField it forces the height of the field to be increased. Any way to override this setting?
As you can see in the image the 555g field is shorter than the Ingredient1 which became taller due to the addition of the trailingIcon.
I've tried to understand what's happening in compose and after digging I came across this code in the Google native TextField.kt
if (trailing != null) {
Box(
modifier = Modifier
.layoutId(LeadingId)
.then(IconDefaultSizeModifier),
contentAlignment = Alignment.Center
) {
trailing()
}
}
So I am inferring from this that IconDefaultSizeModifier is forcing the minHeight to be 48.dp
and below is the code that I use to create the BasicTextField
BasicTextField(
value = state,
//... more arguments
decorationBox = #Composable {
TextFieldDefaults.OutlinedTextFieldDecorationBox(
value = textVal,
visualTransformation = VisualTransformation.None,
innerTextField = it,
singleLine = singleLine,
enabled = enabled,
interactionSource = interactionSource,
trailingIcon = trailingIcon,
// keep vertical paddings but change the horizontal
contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
top = 10.dp,
bottom = 10.dp,
start = 12.dp,
end = 8.dp
),
container = {}
)
}
)
You can apply the same height to both TextFields.
Something like:
BasicTextField(
modifier = Modifier.height(48.dp),
In the same way you can reduce the height.For example
BasicTextField(
modifier = Modifier.height(32.dp),
//... more arguments
decorationBox = #Composable {
TextFieldDefaults.OutlinedTextFieldDecorationBox(
contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
top = 0.dp,
bottom = 10.dp,
start = 12.dp,
end = 0.dp
),

Jetpack Compose TextField cuts text on scroll

I have a TextField with a fixed height. When the user enters a longer text it will scroll. It will cut off any text within the padding when scrolling:
Basically something like this:
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { value -> text = value },
modifier = modifier
.fillMaxWidth()
.height(100.dp),
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
backgroundColor = Color.Transparent
)
)
It is possible to adjust/remove the padding for a TextField, by using BasicTextField directly, e.g. see this stack overflow question.
However I want to keep the padding, but without the clipping of the text when the user scrolls. A simple Text Composable has this behavior.
You can use BasicTextField and modify its decorationBox parameter.
Simply put innerTextField() inside a scrollable Column and add Spacers at the top and the bottom of it.
var text by remember {
mutableStateOf("Hello Stackoverflow")
}
val paddingSize = remember { 16.dp }
BasicTextField(
modifier = Modifier
.height(100.dp),
value = text,
onValueChange = { text = it },
decorationBox = { innerTextField ->
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(state = rememberScrollState())
) {
Spacer(modifier = Modifier.height(paddingSize))
innerTextField()
Spacer(modifier = Modifier.height(paddingSize))
}
}
)
Just use the maxLines parameter of the TextField to avoid clipping. Set it to 1 or 2, per your case, then adjust the height/font-size so the max lines you specify are accomodated correctly. It will likely start to snap to the visible portion, cutting the extra stuff entirely.

What is the difference between `fillMaxSize()` and `matchParentSize()` in a component inside a Box in Jetpack Compose?

I'm creating a component in Jetpack Compose and realized that when I'm making a Composable inside a Box it's possible that this component assumes 2 maximum fill possibilities: Modifier.fillMaxSize() and Modifier.matchParentSize(). As shown below:
Box(
modifier = modifier // This modifier is received by parameter of another composable function
) {
Canvas(modifier = Modifier.matchParentSize()) {
// Using match parent size
}
}
and
Box(
modifier = modifier // This modifier is received by parameter of another composable function
) {
Canvas(modifier = Modifier.fillMaxSize()) {
// Using fill max size
}
}
What is the practical difference between these 2 modes? And why can't I set Modifier.matchParentSize() to a Column or a Row?
From official doc
Modifier.fillMaxSize modifier, which makes an element occupy all available space, will take part in defining the size of the Box.
So it specifies the size of the element.
But if you use Modifier.matchParentSize() in an element inside of a box it has nothing to do with specifying the size of the box.
The size of the box will be measured by other children element of the box. Then the element with the Modifier.matchParentSize() will match and occupy that size.
You can't use .matchParentSize() in Row or Column because this modifier is part of the BoxScope interface. So it works only with boxscope.
For example, if you use fillMaxSize with something like the below.
Box() {
Text(
modifier = Modifier
.fillMaxSize()
.background(Color.Green),
text = ""
)
Text(
modifier = Modifier
.size(100.dp)
.background(Color.Blue),
text = "Hello",
)
}
You will get this. It will fill the entire screen because of that .fillMaxSize() modifier in the first child.
But if you use this
Box() {
Text(
modifier = Modifier
.matchParentSize()
.background(Green),
text = ""
)
Text(
modifier = Modifier
.size(100.dp),
text = "Hello",
)
}
It will take only 100.dp for the Hello text and then the green background will fill that 100.dp because of that .matchParentSize() modifier in the first child.
I could have used Box instead of Text but more Box can make it confused.

Resizeable BasicTextField in Jetpack Compose

Is there a way to produce a resizeable BasicTextField in Jetpack Compose, so that its width would wrap text size when a user types in or deletes characters? They've solved a similar problem for flutter, but I didn't find out how to solve this for Compose.
Flutter - how to make TextField width fit its text ("wrap content")
var text: String by rememberSaveable { mutableStateOf("") }
BasicTextField(
value = text,
onValueChange = {
text = it
},
modifier = Modifier.wrapContentWidth()
)
unfortunately, wrapContentWidth() doesn't work here.
Well, looks like width(IntrinsicSize.Min) solves this problem:
var text: String by rememberSaveable { mutableStateOf("") }
BasicTextField(
value = text,
onValueChange = {
text = it
},
modifier = Modifier.width(IntrinsicSize.Min)
)
We can specify the width range ( min and max width that the textField can span )
for width:-
modifier = Modifier.widthIn(1.dp, Dp.Infinity) // specified the min width as 1.dp
for height:-
modifier = Modifier.heightIn(1.dp, Dp.Infinity)
The composable code:-
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
OutlinedTextField(
value = text,
onValueChange = {
text = it
},
modifier = Modifier.widthIn(1.dp, Dp.Infinity)
)
}
The TextField would grow when we type more till Dp.Infinity. ( used OutlinedTextField for demo but we can use BasicTextField )

Categories

Resources