android jetpack compose textField does not resize screen on API31 - android-jetpack-compose

On API 31
I made scrollable column(not lazyColumn) with TextField.
when i click the textfield at first, the screen is not moving(resized) but when i click again screen is resized.
first time i thought compose version was the problem but it wasn't.
val scrollState = rememberScrollState()
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Box(modifier = Modifier.height(500.dp)
TextField(text = " ", onValueChange = {})
TextField(text = " ", onValueChange = {})
TextField(text = " ", onValueChange = {})
}
I tried to a API 30, but there is not problem.
is there anyone solved this issue?

Related

Custom TopAppBarDefaults ScrollBehavior in Compose

I'm currently working on a animated TopAppBar using Material3 LargeTopAppBar and Compose.
I'm using TopAppBarDefaults.exitUntilCollapsedScrollBehavior to make the AppBar collapse when scrolled.
When flung downwards it snaps to the expanded view, but when flung upwards it doesn't snap and more often than not just stays in between the two states(collapsed & expanded).
Here's some of the code:
val topappbarstate = rememberTopAppBarState()
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
decayAnimationSpec, topappbarstate)
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
LargeHeader(
title = "Title",
subtitle = "Subtitle",
scrollBehavior
)
}, content = { innerPadding ->
LazyColumn(contentPadding = innerPadding,) {
items(count = 100) {
Box(Modifier.fillMaxWidth()) {
Text(text = "$it")
}
}
}
},
)
}
The LargeHeader is a function that contains a Motionlayout to animate the Title and Subtitle in the LargeTopAppBar to swing upwards when scrolled but has no impact on the flinging animation of the TopAppBar.
Is there a way to customize this flinging snap of the actual TopAppBar?
Sorry for my english and thanks in advance!

Jetpack Compose ConstraintLayout TextField baseline moves

I'm experimenting with Jetpack Compose and ConstraintLayout.
I have a Text()-Resource and a TextField. I want the Text to vertically align with TextFields baseline.
#Composable
fun MyComposable() {
ContraintLayout {
val (text, textfield) = createRefs()
var tfValue by remember { mutableStateOf("") }
Text(
"foo",
Modifier.constrainAs(text) {
baseline.linkTo(textfield.baseline)
}
)
TextField(
value = tfValue,
onValueChange = { tfValue = it },
label = { Text("bar") },
modifier = Modifier
.constrainAs(textfield) {
top.linkTo(parent.top, margin = 24.dp)
}
)
}
}
When I use this composable, the text "foo" is perfectly aligned with the baseline of textfield. But when i focus the textfield and the label moves to the top of the textfield, the Text()-Component moves with it. Is this intended?
How can i constrain foo's baseline to the baseline of the content of Textfield rather than to Textfield's label?

How do I fix ConstraintLayout that include Balloon(gives strange results) in jetpack compose?

I'm trying to make a help pop ups in my first app. the problem that came up after making the pop up work is that the icon which I'm using becomes a button taking up whole screen height.
I'm using the only code I found for balloon popups in jetpack compose.
the layout is fine until I add the BalloonAnchor.
this is the code:
#Composable
fun GiveHelp(helpText: String) {
Surface{
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
ConstraintLayout {
val (icon, text) = createRefs()
Icon(
modifier = Modifier
.constrainAs(icon) {
top.linkTo(parent.top)
start.linkTo(parent.start)
},
painter = painterResource(id = R.drawable.ic_help),
contentDescription = "help Icon"
)
Text(
modifier = Modifier
.constrainAs(text) {
top.linkTo(icon.top)
start.linkTo(icon.end)
bottom.linkTo(icon.bottom)
}
.padding(horizontal = 10.dp),
text = "Is your task:"
)
BalloonAnchor(
reference = icon,
modifier = Modifier
.aspectRatio(0.1f),
balloon = BalloonUtils.getTitleBalloon(
context = context,
title = helpText,
lifecycle = lifecycleOwner
),
onAnchorClick = { balloon, anchor -> balloon.showAlignTop(anchor) }
)
}
}
}
The problem here is the aspectRatio you are using in the Modifier of BalloonAnchor. Try something like Modifier.aspectRatio(0.99f). Using this, your Icon will not take the entire screen height. Or, your can use something like below code to get a desirable look.
BalloonAnchor(
reference = icon,
modifier = Modifier
.height(40.dp),
balloon = BalloonUtils.getTitleBalloon(
context = context,
title = helpText,
lifecycle = lifecycleOwner
),
onAnchorClick = { balloon, anchor -> balloon.showAlignTop(anchor) }
)

Keyboard does not open if the TextField inside LazyColumn is in the bottom of the screen

If you focus a TextField near the bottom of the screen, keyboard appears for a moment and then immediately hides. But if you focus some TextField from above, keyboard opens as usual and you can scroll to the bottom. This happens if windowSoftInputMode is set to adjustResize. If none is set, then the behaviour goes to i would say random (keyboard may overlap content, also it may push toolbar and etc.). Perhaps i am simply doing all this wrong, i dunno. I wonder if anyone faced this issue before and could give me a hand.
Sample code (for simplicity i hold mutable states right here, not using ViewModel):
#Composable
fun Greeting() {
Scaffold(topBar = {
TopAppBar(title = { Text(text = "Some title") })
}) {
val focusManager = LocalFocusManager.current
LazyColumn(
contentPadding = PaddingValues(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
items(count = 20) { index ->
val (value, onValueChange) = rememberSaveable { mutableStateOf("Some value $index") }
TextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier.fillMaxWidth(),
label = { Text(text = "Some label $index") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(onNext = {
if (!focusManager.moveFocus(FocusDirection.Down))
focusManager.clearFocus()
}),
singleLine = true
)
}
}
}
}
Compose version 1.0.5

How to make ClickableText accessible to screen readers

This code creates a ClickableText element in Jetpack Compose Composable:
ClickableText(
text = forgotPasswordAnnotatedString,
onClick = {
context.startActivity(intent)
},
modifier = Modifier
.padding(top = mediumPadding)
)
The annotated string is defined here to make the text look like a link:
val forgotPasswordAnnotatedString = buildAnnotatedString {
append(stringResource(R.string.forgot_password))
addStyle(
style = SpanStyle(
textDecoration = TextDecoration.Underline,
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
),
start = 0,
end = 21,
)
}
When I encounter this text using the TalkBalk screen reader in Android, the screenreader does not make it clear that this is clickable text that will do something which tapped on. The reader just reads the text.
Is there a way to make it clear to the screen reader that this text is interactive? Otherwise should I just use a button and style it to look like a link?
It looks like your intention is for the whole text to be clickable? In which you best option is probably a TextButton as suggested by
Gabriele Mariotti.
But if you wan't only part of the link to be clickable, or to have multiple clickable sections, the best I've been able to land on is to draw an invisible box overtop of the Text. It means that I can control the touch target of the clickable area to be at least 48.dp and can use the semantics{} modifier to control how a screen reader interprets it.
Would welcome any suggestions.
// remember variables to hold the start and end position of the clickable text
val startX = remember { mutableStateOf(0f) }
val endX = remember { mutableStateOf(0f) }
// convert to Dp and work out width of button
val buttonPaddingX = with(LocalDensity.current) { startX.value.toDp() }
val buttonWidth = with(LocalDensity.current) { (endX.value - startX.value).toDp() }
Text(
text = forgotPasswordAnnotatedString,
onTextLayout = {
startX.value = it.getBoundingBox(0).left // where 0 is the start index of the range you want to be clickable
endX.value = it.getBoundingBox(21 - 1).right // where 21 is the end index of the range you want to be clickable
}
)
Note that buttonPaddingX is relative to the Text position not the Window, so you may have to surround both in a Box{} or use ConstraintLayout.
Then to draw the invisible box
Box(modifier = Modifier
.sizeIn(minWidth = 48.dp, minHeight = 48.dp) // minimum touch target size
.padding(start = buttonPaddingX)
.width(buttonWidth)
// .background(Color.Magenta.copy(alpha = 0.5f)) // uncomment this to debug where the box is drawn
.clickable(onClick = { context.startActivity(intent) })
.semantics {
// tell TalkBack whatever you need to here
role = Role.Button
contentDescription = "Insert button description here"
}
)
In my code I'm using pushStringAnnotation(TAG, annotation) rather than reference string indexes directly. That way I can get the start and end index of the clickable area with annotatedString.getStringAnnotations(TAG,0,annotatedString.length).first(). Useful if there a multiple links within the text.
It's disappointing that ClickableText doesn't have accessibility in mind from the get go, hopefully we'll be able to use it again in a future update.
Adding .semantics.contentDescription to the Modifier changes what is read by the screen reader. I had to word contentDescription to make it clear that this was a link to reset the your password.
The screen reader still doesn't recognize the element a clickable but hopefully the description will be useful to convey to the user that this element is interactive.
ClickableText(
text = forgotPasswordAnnotatedString,
onClick = {
context.startActivity(intent)
},
modifier = Modifier
.padding(top = mediumPadding)
// new code here:
.semantics {
contentDescription = "Forgot your password? link"
}
)

Resources