Jetpack Compose: No background in OutlinedTextField - android

I have two layers in my Compose layout: One for the actual content and the one above is a Box-wrapped OutlinedTextField for search queries.
Here's the sample code:
// Placeholder for layout content
Box(modifier = Modifier.fillMaxSize()) {
Text(
text = stringResource(id = R.string.lorem_ipsum),
color = Color.Gray
)
}
// Overlaying Box with OutlinedTextField
Box(modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp, top = 64.dp)
) {
OutlinedTextField(
value = viewModel.query.value,
onValueChange = { viewModel.updateQuery(it) },
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.navigationBarsWithImePadding(),
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Characters,
autoCorrect = false,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.None
),
shape = CircleShape,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.White
),
placeholder = { Text(stringResource(id = R.string.search_input_placeholder)) },
maxLines = 1,
singleLine = true
)
}
Even though I'm setting
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.White
)
in the OutlinedTextField, the background stays transparent as you can see in the following screenshot:
Adding a background color on the OutlinedTextField's modifier is giving the whole row a rectangled background, which is also not the desired result.
The OutlinedTextField with it's CircleShape should only have a background inside of the shape. How can I achieve that?
I'm using Jetpack Compose version 1.0.4.

Reason
Unfortunately, in version 1.0.4, OutlinedTextField ignores the backgroundColor even if you specify it, so you can remove the colors parameter.
Support for it will be added in 1.1.0:
https://android-review.googlesource.com/c/platform/frameworks/support/+/1741240
It's already present in 1.1.0-alpha06 so you can play with it if you want. Your original code should produce desired outcome on that version.
Solution
To achieve what you want (before 1.1.0 is released) you can simply add a background modifier with the same shape:
.background(Color.White, CircleShape)
Since the order of modifiers is important, you should add it after you apply all the paddings (navigationBarsWithImePadding in your case). Like that:
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.navigationBarsWithImePadding()
.background(Color.White, CircleShape),
Note: Also be aware that you cannot use the label parameter with this approach, because the TextField with label will be higher than just the outline shape and these two shaped wouldn't match in size anymore.
Result
Entire code:
// Placeholder for layout content
Box(modifier = Modifier.fillMaxSize()) {
Text(
text = stringResource(id = R.string.lorem_ipsum),
color = Color.Gray
)
}
// Overlaying Box with OutlinedTextField
Box(modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp, top = 64.dp)
) {
OutlinedTextField(
value = viewModel.query.value,
onValueChange = { viewModel.updateQuery(it) },
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.navigationBarsWithImePadding()
.background(Color.White, CircleShape),
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Characters,
autoCorrect = false,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.None
),
shape = CircleShape,
placeholder = { Text(stringResource(id = R.string.search_input_placeholder)) },
maxLines = 1,
singleLine = true
)
}
How the final result looks like:

Related

Android Compose scroll up after focus on BasicTextField

I'm using BasicTextField to stylize my text input field. I'm having a problem where when I focus on said text input on the lower part of the screen, keyboard overlaps my input a bit. Is there a way to fix it ? Here's my BasicTextField
Row(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 68.dp)
.border(width = 1.dp, color = Color.Blue)
.background(color = Color.White),
verticalAlignment = Alignment.Top
) {
BasicTextField(
modifier = Modifier
.offset(y = (8).dp)
.padding(start = 16.dp, top = 20.dp, bottom = 16.dp)
.weight(1f),
textStyle = TextStyle(fontSize = 24.sp, lineHeight = 37.sp),
maxLines = 5,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
value = input,
onValueChange = {
input = it
},
decorationBox = {
Text(
modifier = Modifier.offset(y = (-16).dp),
text = "amount",
fontSize = 12.sp,
color = Color.Gray,
fontWeight = FontWeight.Bold
)
it()
},
)
Row(modifier = Modifier.align(Alignment.CenterVertically)) {
Image(
modifier = Modifier.padding(start = 4.dp, end = 16.dp),
painter = painterResource(id = R.drawable.arrow_down),
contentDescription = null,
colorFilter = ColorFilter.tint(color = Color.Blue)
)
}
}
I tried adding specific height on BasicTextField, removing border and background from Row and adding to BasicTextField, but end result wasn't the text field looking like I want it to. I also need the text field to be able to expand on break line and that's where a lot of times it went bad when trying to adjust it. Maybe I'm missing something.

How to get OutlinedTextField with gradient boarder in Jetpack Compose?

I'm trying to get an OutlinedTextField with gradient border like this one:
But I can add only a single color to the outline,
OutlinedTextField(
value = email,
onValueChange = {email = it},
label = { Text(text = "Email Address") },
modifier = Modifier
.fillMaxWidth(.8f)
.padding(4.dp),
colors = TextFieldDefaults.outlinedTextFieldColors(unfocusedBorderColor = Color.Green)
)
If I add a border with modifier then the gradient is applied to the border, not to the outline:
Modifier.border(width = 1.dp, brush = gradient, shape = RoundedCornerShape(12.dp))
How can I add gradient color to the outline?
so far that i know OutlinedTextField does't support gradient border. But if really want to use gradient border in text field you can try BasicTextField
var name by remember {
mutableStateOf("")
}
BasicTextField(
value = name,
onValueChange = { name = it },
cursorBrush = Brush.horizontalGradient(listOf(Color.Red, Color.Green)),
modifier = Modifier
.fillMaxWidth(),
decorationBox = { innerTextField ->
Row(
modifier = Modifier
.fillMaxWidth()
.border(
brush = Brush.horizontalGradient(listOf(Color.Green, Color.Blue)),
width = 1.dp,
shape = CircleShape
)
.padding(16.dp)
) {
Icon(Icons.Default.Email, contentDescription = "")
Spacer(modifier = Modifier.padding(3.dp))
innerTextField()
}
}
)
and this is the final result
you can learn more about basic text field by looking the sample
[BasicTextField Samples][2]

Jetpack Compose Textfield Label not staying in center

I'm trying to make a textfield thats quite small (32.dp). When doing this, the label gets pushed to the bottom and I can't find a way to keep it centered? I've tried using TextAlign.center but no luck
TextField(
modifier = Modifier
.height(32.dp)
.padding(horizontal = 8.dp)
.border(1.dp, colorResource(id = R.color.mono4), RoundedCornerShape(32.dp))
.constrainAs(commentBox) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(profilePhoto.end)
end.linkTo(postText.start)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
},
label = {
Text(
textAlign = TextAlign.Center,
text = "Comment on workout...",
style = TextStyle(
fontSize = 14.sp,
color = colorResource(id = R.color.mono4),
fontFamily = FontFamily(Font(R.font.josefin_san_regular))
)
)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
value = commentText,
onValueChange = { commentText = it }
)
This is internal behaviour, you will need to alter the source coe to fix it, or implement your own composable, something like:-
#OptIn(ExperimentalAnimationApi::class)
#Composable
fun TinyTextCapture(){
var value by remember { mutableStateOf("") }
Box(contentAlignment = Alignment.Center){
TextField(
modifier = Modifier.height(32.dp),
value = value, onValueChange = { value = it }
)
AnimatedVisibility(visible = value.isEmpty()) {
Text("Label")
}
}
}
The default label parameter has a default padding from top start, that is why it was seen as being pushed downwards. If I were you, I would not go about editing the source of something as basic as a TextField since it has so many internal dependencies that it is all a mess.
You could also do without the AnimatedVisibility, and also apply a ConstraintLayout to get it at the start of the TextField in contrast to its absolute centered position right now.

How to remove or reduce padding in Jetpack compose OutlinedButton

Unable to reduce the huge padding in OutlinedButton. Tried contentPadding, modifier padding, etc. Cannot reduce padding for text "apple". Any idea? Should I use any other type of compose component for this?
OutlinedButton(
onClick = {},
border = BorderStroke(1.dp, Color.White),
shape = RoundedCornerShape(12.dp),
contentPadding = PaddingValues(0.dp),
modifier = Modifier
.background(bgColor)
.height(24.dp)
.padding(all = 0.dp),
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = bgColor)) {
Text("apple",
color = Color.White,
style = MaterialTheme.typography.body2,
modifier = Modifier.height(10.dp).padding(vertical = 0.dp), //.background(bgColor),
)
}
Updated after #liveAnyway's answer (thanks!) which appeared to help. After that I removed height from OutlinedButton too - ideally I wanted it like "wrap-content". Once I made that change, I still see the padding. Bottomline I don't want any absolute height specified so that it can work with different font size from system settings.
Row(modifier = Modifier.padding(vertical = 12.dp)) {
OutlinedButton(
onClick = {},
border = BorderStroke(1.dp, Color.White),
shape = RoundedCornerShape(18.dp),
contentPadding = PaddingValues(0.dp),
modifier = Modifier
.background(bgColor)
.padding(all = 0.dp),
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = bgColor)
) {
Text("apple",
color = Color.White,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(vertical = 0.dp),
)
}
}
Button has default min size modifier. This is done according to Material guidelines, so that the button is easy to hit. If the control size is too small, the user may have problems hitting it, take this into account when changing this parameter.
You can override it by applying defaultMinSize modifier. The 0.dp will be ignored, but starting from 1.dp you will get the desired result:
OutlinedButton(
onClick = { /*TODO*/ },
contentPadding = PaddingValues(),
modifier = Modifier
.defaultMinSize(minWidth = 1.dp, minHeight = 1.dp)
) {
Text(
"Apple",
)
}
Alternatively, you can design your own button without these restrictions:
Surface(
onClick = {
},
shape = MaterialTheme.shapes.small,
color = bgColor,
contentColor = MaterialTheme.colors.primary,
border = ButtonDefaults.outlinedBorder,
role = Role.Button,
) {
Text(
"Apple",
)
}
You have to change the minHeight (default size are MinWidth = 64.dp and MinHeight = 36.dp) and remove the contentPadding with contentPadding = PaddingValues(0.dp):
OutlinedButton(
onClick = {},
border = BorderStroke(1.dp, Color.White),
shape = RoundedCornerShape(12.dp),
contentPadding = PaddingValues(0.dp),
modifier = Modifier.defaultMinSize(
minWidth = ButtonDefaults.MinWidth,
minHeight = 10.dp
)
) {
Text(
"apple",
style = MaterialTheme.typography.body2
)
}

Jetpack Compose: Custom TextField design

In general, most components in Jetpack Compose seem to be very easy to customize.
However, the same cannot be said for the TextField. For example, say that I wanted to make something like this:
One would think that simply wrapping the BaseTextField would work. However, it appears that there has been a bug in the BaseTextField component, and I have opened an issue. This bug will not permit the user to focus the text field after focusing-away from it once already, until the component is re-rendered.
Citing this, I attempted to customize the OutlinedTextField and TextField components, but am not able to customize them to look like the image above. Were it not for the fact that the cursor color uses the activeColor property, I could make it work.
What would be a proper work-around to create a usable text field that looks like the above?
You can use the TextField:
removing the label with label = null
applying custom color with the TextFieldDefaults.textFieldColors parameter to hide the indicator.
adding in the onValueChange a function to fix the max number of characters as described here
Finally use a Column to add 2 Text composables to complete the external label and counter text.
Something like:
var text by remember { mutableStateOf("") }
val maxChar = 5
Column(){
//External label
Text(
text = "Label",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Start,
color = Blue
)
TextField(
value = text,
onValueChange = {
if (it.length <= maxChar) text = it
},
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
shape = RoundedCornerShape(8.dp),
trailingIcon = {
Icon(Icons.Filled.Add, "", tint = Blue)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = ....,
focusedIndicatorColor = Color.Transparent, //hide the indicator
unfocusedIndicatorColor = .....)
)
//counter message
Text(
text = "${text.length} / $maxChar",
textAlign = TextAlign.End,
color = Blue,
style = MaterialTheme.typography.caption, //use the caption text style
modifier = Modifier.fillMaxWidth()
)
By this exemple you can learn a lot. With 1.0.0 you can do like this:
Column {
var textState by remember { mutableStateOf("") }
val maxLength = 110
val lightBlue = Color(0xffd8e6ff)
val blue = Color(0xff76a9ff)
Text(
text = "Caption",
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 4.dp),
textAlign = TextAlign.Start,
color = blue
)
TextField(
modifier = Modifier.fillMaxWidth(),
value = textState,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = lightBlue,
cursorColor = Color.Black,
disabledLabelColor = lightBlue,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
onValueChange = {
if (it.length <= maxLength) textState = it
},
shape = RoundedCornerShape(8.dp),
singleLine = true,
trailingIcon = {
if (textState.isNotEmpty()) {
IconButton(onClick = { textState = "" }) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = null
)
}
}
}
)
Text(
text = "${textState.length} / $maxLength",
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
textAlign = TextAlign.End,
color = blue
)
}
Well, until the issue I mentioned is resolved, the choices are:
Roll back to Compose version 1.0.0-alpha04 (issue was introduced in alpha05)
Add a border to a TextField or OutlinedTextField as so:
TextField(
value = myValue,
onValueChange = myOnChange,
modifier = Modifier.clip(myShape).border(5.dp, myColor)
)
In addition to Gabriele Mariotti's Answer
If you like to have custom color for your cursor, you can achieve it using:
Column(){
//External label
Text(
...
...
)
TextField(
...
...
colors = TextFieldDefaults.textFieldColors(
backgroundColor = ...,
focusedIndicatorColor = ...,
unfocusedIndicatorColor = ...,
cursorColor = Color.Black)
)
//counter message
Text(
...
...
)

Categories

Resources