how to change text automatically in compose when button click - android

#Composable
fun Counter(name: Int) {
Text(
text = "Hello ${name}!2" + name,
fontSize = 18.sp,
)
}
When the increase button is clicked name value increase +1 however, text ui doesn't change.
How can I make this ui change automatically.

You need a variable somewhere to track the count and have it incremented when a button is clicked.
#Composable
fun Counter(name: Int) {
var counter by state { name }
Column {
Text(
text = "Hello ${counter}!",
fontSize = 18.sp,
)
Button(onClick = { counter++ }) {
Text("Increase")
}
}
}

Related

Animate visibility in compose

I have a text which need to be animated to show and hide with the value is null or not. it would have been straight forward if the visibility is separately handle, but this is what I got.
In the bellow code the enter animation works but the exit animation dont as the text value is null.
I can think of something with remembering the old value but not sure how.
#Composable
fun ShowAnimatedText(
text : String?
) {
Column(
modifier = Modifier.fillMaxWidth()
) {
AnimatedVisibility(
visible = text != null,
enter = fadeIn(animationSpec = tween(2000)),
exit = fadeOut(animationSpec = tween(2000))
) {
text?.let {
Text(text = it)
}
}
}
}
I think the fade-out animation is actually working "per-se".
I suspect the parameter text: String? is a value coming from a hoisted "state" somewhere up above ShowAnimatedText, and since you are directly observing it inside the animating scope, when you change it to null it instantly removes the Text composable, and your'e not witnessing a slow fade out.
AnimatedVisibility(
...
) {
text?.let { // your'e directly observing a state over here
Text(text = it)
}
}
This is my attempt completing your snippet based on my assumption and making it work, the fade-in works, but the desired fade-out is instantly happening.
#Composable
fun SomeScreen() {
var text by remember {
mutableStateOf<String?>("Initial Value")
}
Row(
modifier = Modifier.fillMaxWidth()
) {
Button(onClick = {
text = "New Value"
}) {
Text("Set New Value")
}
Button(onClick = {
text = null
}) {
Text("Remove Value")
}
AnimatedText(text = text)
}
}
#Composable
fun ShowAnimatedText(
text : String?
) {
Column(
modifier = Modifier.fillMaxWidth()
) {
AnimatedVisibility(
visible = text != null,
enter = fadeIn(animationSpec = tween(2000)),
exit = fadeOut(animationSpec = tween(2000))
) {
text?.let {
Text(text = it)
}
}
}
}
You can solve it by modifying the text to a non-state value and change your visibility logic from using a nullability check to some "business logic" that would require it to be visible or hidden, modifying the codes above like this.
#Composable
fun SomeScreen() {
var show by remember {
mutableStateOf(true)
}
Row(
modifier = Modifier.fillMaxWidth()
) {
Button(onClick = {
show = !show
}) {
Text("Set New Value")
}
AnimatedText(text = "Just A Value", show)
}
}
#Composable
fun ShowAnimatedText(
text : String?,
show: Boolean
) {
Column(
modifier = Modifier.fillMaxWidth()
) {
AnimatedVisibility(
visible = show,
enter = fadeIn(animationSpec = tween(2000)),
exit = fadeOut(animationSpec = tween(2000))
) {
text?.let {
Text(text = it)
}
}
}
}
I fixed it by remembering the previous state (Or don't set the null value) until the exit animation is finished, if the text is null.
Thank you z.y for your suggestion.
#Composable
fun ShowAnimatedText(
text : String?,
show: Boolean
) {
var localText by remember {
mutableStateOf<String?>(null)
}
AnimatedContent(show, localText)
LaunchedEffect(key1 = text, block = {
if(text == null){
delay(2000)
}
localText = text
})
}
#Composable
private fun AnimatedContent(show: Boolean, localText: String?) {
Column(
modifier = Modifier.fillMaxWidth()
) {
AnimatedVisibility(
visible = show,
enter = fadeIn(animationSpec = tween(2000)),
exit = fadeOut(animationSpec = tween(2000))
) {
localText?.let {
Text(text = it)
}
}
}
}

Show trailing icon after text input from user in TextField Jetpack Compose

I am trying to:
make the trailingIcon of TextField composable visible only if the user enters some text other than white spaces.
Later when the user clicks the trailingIcon the text in the TextField should get cleared and the trailingIcon should disappear.
Again when the user enters a text other than space, the trailingIcon should appear and enable this text clearing feature.
and so on...
I tried searching for solutions to this problem but mostly they were focused on "visible trailingIcons" and not what I was trying to implement.
Depending on text state you can specify null or actual view for trailingIcon parameter:
var text by remember { mutableStateOf("") }
val trailingIconView = #Composable {
IconButton(
onClick = {
text = ""
},
) {
Icon(
Icons.Default.Clear,
contentDescription = "",
tint = Color.Black
)
}
}
TextField(
value = text,
onValueChange = { text = it },
trailingIcon = if (text.isNotBlank()) trailingIconView else null,
)
You can add a condition to make visible the trailingIcon.
Something like:
var text by remember { mutableStateOf("") }
val isVisible by remember {
derivedStateOf {
text.isNotBlank()
}
}
OutlinedTextField(
value = text,
onValueChange = {
text = it
},
trailingIcon = {
if (isVisible) {
IconButton(
onClick = { text = "" }
) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = "Clear"
)
}
}
}
)

dynamically change textDecoration on clickableText android compose

I have a large number of texts in a row, and I would like to make every one of them change text decoration on press
(so the user can notice which text/tag is already selected)
(unselected: TextDecoration.None, selected: TextDecoration: Underlined)
(user can press selected text to unselect it)
var tagsSelected = mutableListOf<String>()
...
Text(text = "tech",
Modifier.clickable {
if (tagsSelected.contains("tech")) {
tagsSelected.remove("tech")
// RemoveTextDecoration ?
} else {
tagsSelected.add("tech")
// AddTextDecoration ?
}
}.padding(5.dp))
...
I've tried using variables (not a good idea cause it would require a lot of them), using an mutable array of boolean values (later observed as states) and none of that has brought results for me,
any amount of help will be appreciated,
thanks :)
You're creating a new mutableListOf on each recomposition. That's why new values are not getting saved. Check out how you should store state in compose.
rememberSaveable will save your state even after screen rotation(unlike remember), and mutableStateListOf is a variation of mutable list which will notify Compose about updates. I you need to save state even when you leave the screen and come back, check out about view models.
Also you can move your add/remove logic into extension so your code will look cleaner:
fun <E> MutableList<E>.addOrRemove(element: E) {
if (!add(element)) {
remove(element)
}
}
Final variant:
val tagsSelected = rememberSaveable { mutableStateListOf<String>() }
Text(
text = "tech",
modifier = Modifier
.clickable {
tagsSelected.addOrRemove("tech")
}
.padding(5.dp)
)
If you have many Text items which looks the same, you can repeat them using forEach:
val tagsSelected = rememberSaveable { mutableStateListOf<String>() }
val items = listOf(
"tech1",
"tech2",
"tech3"
)
items.forEach { item ->
Text(
text = item,
modifier = Modifier
.clickable {
tagsSelected.addOrRemove(item)
}
.padding(5.dp)
)
}
If you need to use selection state only to change text decoration, you can easily move it to an other composable and create a local variable:
#Composable
fun ClickableDecorationText(
text: String,
) {
var selected by rememberSaveable { mutableStateOf(false) }
Text(
text = text,
textDecoration = if(selected) TextDecoration.Underline else TextDecoration.None,
modifier = Modifier
.clickable {
selected = !selected
}
.padding(5.dp)
)
}

AnimatedVisibility Doesn't Expand Height In Dialog in Jetpack Compose

So I have this composable in my project ...
#Composable
private fun ShowDialog() {
var showText by remember { mutableStateOf(false) }
val text = if (showText) {
"Hide Text"
} else {
"Show Text"
}
Dialog(onDismissRequest = { }) {
Card(modifier = Modifier.padding(15.dp)) {
Column(modifier = Modifier.padding(15.dp)) {
AnimatedVisibility(visible = showText) {
Text(
text = "Here is the show text sample",
modifier = Modifier.padding(5.dp),
style = MaterialTheme.typography.body1,
color= Color.Black
)
}
Button(onClick = { showText = !showText }) {
Text(text = text)
}
}
}
}
}
If you have gone through the code, you might get what it is supposed to do. i.e it is basically a dialog with one text and a button below it. When the user clicks on a button the text above the button will toggle its visibility.
But the problem with the code is, When I click on the button, the text appears but the button gets invisible in other words the text takes the space and pushes a button to below. But yet the container in this case card or the column doesn't expand its height.
Is it supposed to work like that ? Or is this a bug?
I tried animateContentSize() on Column and Card but it didn't work. And checked similar questions on StackOverflow but didn't found any useful information.
Luckily, I found a temporary working answer for this problem,
What we need to use is just pass DialogProperties(usePlatformDefaultWidth = false) as properties parameter for dialog. This will make the dialog to resizable like this
#Composable
private fun ShowDialog() {
var showText by remember { mutableStateOf(false) }
val text = if (showText) {
"Hide Text"
} else {
"Show Text"
}
Dialog(
onDismissRequest = { },
properties = DialogProperties(usePlatformDefaultWidth = false)
) {
Card(
modifier = Modifier
.padding(15.dp)
.wrapContentWidth()
.animateContentSize()
) {
Column(modifier = Modifier.padding(15.dp).fillMaxWidth(1f)) {
AnimatedVisibility(visible = showText) {
Text(
text = "Sample",
modifier = Modifier
.padding(5.dp)
.fillMaxWidth(1f),
style = MaterialTheme.typography.body1,
color = Color.Black
)
}
Button(onClick = { showText = !showText }) {
Text(text = text)
}
}
}
}
}
Caution: It uses #ExperimentalComposeUiApi
This API is experimental and is likely to change in the future.

Get Button Text

I have a Button composable that I need to get the text value when clicked.
Button(
onClick = {// Get the "TheText" from below },
) {
Text(
modifier = Modifier.padding(8.dp),
text = "TheText",
style = TextStyle(fontSize = 15.sp)
)
}
I am creating a type of quiz where the buttons text matches the correct answer.
I thin I may need to create a custom compposabe that takes the text as a parameter and also a callback function that will pass that text back up to my main program where I can Check for a correct answer.
I assume you want to input text instead of just displaying one.
#Composable
fun example() {
var text by remember { mutableStateOf("TheText") }
Column {
Button(
onClick = {
val useThisString = text
},
) { Text(text = text) } // Probably wanna put "Copy" here
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Optinal Label") }
)
}
}
Otherwise, if you just want the label of the button:
val buttonText = "TheText"
Button(
onClick = {
// Use variable here
},
) {
Text(
modifier = Modifier.padding(8.dp),
text = buttonText,
style = TextStyle(fontSize = 15.sp)
)
}
Ok here's how to store it in a variable
var retrievedValue by mutableStateOf("") //Assuming you must use it somewhere else as state
var currentValue by rememberSaveable { mutableStateOf("The Text") }
Button(
onClick = { retrievedValue = currentValue },
) {
Text(
modifier = Modifier.padding(8.dp),
text = currentValue,
style = TextStyle(fontSize = 15.sp)
)
}
Now you can use it anywhere you want. Changing the retrievedValue would trigger recompositions in any composable that reads it.
This should solve your problem. Use the retrievedValue as the value returned by a callback. In compose, for such stuff, you do not need callbacks. MutableState objects, whenever are modified, trigger a recomposition on the composables reading them.
Anyway, here when you click the button, you will get the value of the text, whatever it may be, you will always get the current value.

Categories

Resources