Retain EnterExiteState across navigtion. jetpack compose - android

I have a AnimatedVisibility component in my screenA. And I also have a screenB. When the component is visible, navigating to screenB and then back will sure turn the component into invisble state. I wish it can stick to visible state. I know there is a EnterExiteState.Visible. Will this help? What should I do?
Here is my set. A button is used to change the value of editable between true and false.
var editable by rememberSaveable { mutableStateOf(false) }
AnimatedVisibility(
visible = editable,
modifier = Modifier.constrainAs(layer) {
bottom.linkTo(parent.bottom, 20.dp)
start.linkTo(parent.start, 16.dp)
},
enter = slideInVertically {
// Slide in from 40 dp from the top.
with(density) { -40.dp.roundToPx() }
} + expandVertically(
// Expand from the top.
expandFrom = Alignment.Top
) + fadeIn(
// Fade in with the initial alpha of 0.3f.
initialAlpha = 0.3f
),
exit = slideOutVertically{
// Slide in from 40 dp from the top.
with(density) { 40.dp.roundToPx() }
} + shrinkVertically( shrinkTowards = Alignment.Bottom )
+ fadeOut()
) {
Card(
shape = RoundedCornerShape(20.dp),
elevation = CardDefaults.cardElevation(1.dp)
) {
InfoPanel()
}
}

Related

How can I animate between 0 and wrap content values ​using compose

I'm trying to make a swipe to reveal component using compose, but I want the width of the card that will appear after the swipe to grow to the size of the wrap content without using it, but I don't understand how to calculate the wrap content size.
var width by remember {
mutableStateOf(0.dp)
}
val lowerTransition = updateTransition(transitionState, "lowerCardTransition")
val lowerOffsetTransition by lowerTransition.animateFloat(
label = "lowerCardOffsetTransition",
transitionSpec = { tween(durationMillis = ANIMATION_DURATION) },
targetValueByState = { if (isRevealed) width.value else 0f },
)
How do I equate the width value used here to the wrap content value?
I'm trying to make the resulting delete button appear all without using a constant value
Try using AnimatedVisibility. For demo purpose I used OnClick, replace it with OnSwipe.
#Preview
#Composable
fun AnimateVisibility2() {
var visible by remember {
mutableStateOf(false)
}
Row(
modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.Center
) {
AnimatedVisibility(
visible = visible, enter = expandHorizontally(), exit = shrinkHorizontally()
) {
IconButton(onClick = { /*TODO*/ }) {
Icon(Icons.Default.Phone, contentDescription = null)
}
}
Button(onClick = { visible = !visible }, Modifier.weight(1f)) {
Text("Click Me")
}
}
}

Jetpack Compose UI - Button width changes on click inside AlertDialog

I'm facing this weird issue with fillMaxWidth(fraction = ...) with an AlertDialog and Button, where the Button shows up initially at one size, and on click it shrinks to wrapping its content. Here is the most basic example I can create. I've tried with multiple versions of Compose but they all do the same thing. Any ideas?
AlertDialog(
modifier = modifier,
onDismissRequest = {},
text = { },
buttons = {
Button(
onClick = { },
modifier = Modifier
.fillMaxWidth(0.75f)
.padding(start = 12.dp, end = 12.dp, bottom = 8.dp)
) {
Text(text = "Done")
}
}
)
Before click:
After click:
I think you have to define a specific width for the AlertDialog, any child it has may not be able to calculate a 75% of unknown width .
either you fill the max width of the dialog
AlertDialog(
modifier = Modifier.fillMaxWidth(),
...
or specify an actual dp width
AlertDialog(
modifier = Modifier.width(150.dp),
...
I don't wanna throw jargons I can't explain, but I suspect when AlertDialog doesn't have any specific width, it cannot provide any incoming measurements for its children, which is in this case the Button cannot compute a 75% of unknown width on initial display, it looks like all size computation is happening only after an action has been made to the button, maybe a recomposition is happening under-the-hood having correct measurements.
I've used the sample composable from here and added your modifier to it for each button so it looks like
#Composable
fun AlertDialogSample() {
MaterialTheme {
Column {
val openDialog = remember { mutableStateOf(false) }
Button(onClick = {
openDialog.value = true
}) {
Text("Click me")
}
if (openDialog.value) {
AlertDialog(
onDismissRequest = {
// Dismiss the dialog when the user clicks outside the dialog or on the back
// button. If you want to disable that functionality, simply use an empty
// onCloseRequest.
openDialog.value = false
},
title = {
Text(text = "Dialog Title")
},
text = {
Text("Here is a text ")
},
confirmButton = {
Button(
modifier = Modifier
.fillMaxWidth(0.75f)
.padding(start = 12.dp, end = 12.dp, bottom = 8.dp),
onClick = {
openDialog.value = false
}) {
Text("This is the Confirm Button")
}
},
dismissButton = {
Button(
modifier = Modifier
.fillMaxWidth(0.75f)
.padding(start = 12.dp, end = 12.dp, bottom = 8.dp),
onClick = {
openDialog.value = false
}) {
Text("This is the dismiss Button")
}
}
)
}
}
}
}
and I see the buttons at 75% width.
Doesn't explain why you see the described issue, but does give you a solution.

Compose - AnimatedVisibility and offset

When adding an enter and exit transition to AnimatedVisibility, if the composable animated has an offset modifier, the transition is not visible:
AnimatedVisibility(
visible = visible,
enter = fadeIn(animationSpec = tween(1000)),
exit = fadeOut(animationSpec = tween(1000))
) {
Text(
text = "Hello, world!", modifier = Modifier
// This will make the transition not working
//.offset(100.dp, 0.dp)
)
}
Button(onClick = { visible = !visible }) {
Text("Click Me")
}
This is the animation without the offset:
and this is the animation with the offset:
Is there a way to make it work?
Don't out the offset modifier onto the Text. Put it onto AnimatedVisibility instead:
AnimatedVisibility(
visible = visible,
enter = fadeIn(animationSpec = tween(1000)),
exit = fadeOut(animationSpec = tween(1000)),
modifier = Modifier.offset(100.dp, 0.dp)
) {
Text(text = "Hello, world!")
}

How to show Drop down Menu where ever I touch in a box Jetpack Compose in android studio

I want to show my DropdownMenu where ever I touch but it doesn't work. I used offset to set the location of my drop down menu but it doesn't work properly. It looks like it only sets the x value to the location of my drop down menu.
var expanded by remember { mutableStateOf(false) }
var touchPoint: Offset by remember { mutableStateOf(Offset.Zero) }
val density = LocalDensity.current
Box(
Modifier
.fillMaxSize()
.background(Color.Cyan)
.pointerInput(Unit) {
detectTapGestures {
Log.d(TAG, "onCreate: ${it}")
touchPoint = it
expanded = true
}
}
) {
val (xDp, yDp) = with(density) {
(touchPoint.x.toDp()) to (touchPoint.y.toDp())
}
DropdownMenu(
modifier = Modifier.align(Alignment.Center),
expanded = expanded,
offset = DpOffset(xDp, yDp),
onDismissRequest = {
expanded = false
},
) {
DropdownMenuItem(onClick = {
expanded = false
}) {
Text("Copy")
}
DropdownMenuItem(onClick = { expanded = false }) {
Text("Get Balance")
}
}
}
What you actually should be doing is offseting upward by giving a negative Offset.
You need get height of your parent you can get it via Modifier.onSizeChanged or using BoxWithConstraints if it covers whole parent.
I used BoxWithConstraints for demonstration but both can be used.
If you offset by maxHeight or height of the Composable Dropdown menu appears at the top so we need another positive offset which is y axis of touch position to move it down
#Composable
private fun DropDownSample() {
var expanded by remember { mutableStateOf(false) }
var touchPoint: Offset by remember { mutableStateOf(Offset.Zero) }
val density = LocalDensity.current
BoxWithConstraints(
Modifier
.fillMaxSize()
.background(Color.Cyan)
.pointerInput(Unit) {
detectTapGestures {
Log.d("TAG", "onCreate: ${it}")
touchPoint = it
expanded = true
}
}
) {
val (xDp, yDp) = with(density) {
(touchPoint.x.toDp()) to (touchPoint.y.toDp())
}
DropdownMenu(
expanded = expanded,
offset = DpOffset(xDp, -maxHeight + yDp),
onDismissRequest = {
expanded = false
}
) {
DropdownMenuItem(
onClick = {
expanded = false
},
interactionSource = MutableInteractionSource(),
text = {
Text("Copy")
}
)
DropdownMenuItem(
onClick = {
expanded = false
},
interactionSource = MutableInteractionSource(),
text = {
Text("Get Balance")
}
)
}
}
}
As can be seen in gif if you touch area where Dropdown appears if you touch the bottom of the screen it is moved up to top of the Composable. It's at the end of the gif. I don't know what's causing this. Other than this issue it works as expected.

How to handle visibility of a Text in Jetpack Compose?

I have this Text:
Text(
text = stringResource(id = R.string.hello)
)
How can I show and hide this component?
I'm using Jetpack Compose version '1.0.0-alpha03'
As CommonsWare stated, compose being a declarative toolkit you tie your component to a state (for ex: isVisible), then compose will intelligently decide which composables depend on that state and recompose them. For ex:
#Composable
fun MyText(isVisible: Boolean){
if(isVisible){
Text(text = stringResource(id = R.string.hello))
}
}
Also you could use the AnimatedVisibility() composable for animations.
You can simply add a condition like:
if(isVisible){
Text("....")
}
Something like:
var visible by remember { mutableStateOf(true) }
Column() {
if (visible) {
Text("Text")
}
Button(onClick = { visible = !visible }) { Text("Toggle") }
}
If you want to animate the appearance and disappearance of its content you can use the AnimatedVisibility
var visible by remember { mutableStateOf(true) }
Column() {
AnimatedVisibility(
visible = visible,
enter = fadeIn(
// Overwrites the initial value of alpha to 0.4f for fade in, 0 by default
initialAlpha = 0.4f
),
exit = fadeOut(
// Overwrites the default animation with tween
animationSpec = tween(durationMillis = 250)
)
) {
// Content that needs to appear/disappear goes here:
Text("....")
}
Button(onClick = { visible = !visible }) { Text("Toggle") }
}
As stated above, you could use AnimatedVisibility like:
AnimatedVisibility(visible = yourCondition) { Text(text = getString(R.string.yourString)) }
/**
* #param visible if false content is invisible ie. space is still occupied
*/
#Composable
fun Visibility(
visible: Boolean,
content: #Composable () -> Unit
) {
val contentSize = remember { mutableStateOf(IntSize.Zero) }
Box(modifier = Modifier
.onSizeChanged { size -> contentSize.value = size }) {
if (visible || contentSize.value == IntSize.Zero) {
content()
} else {
Spacer(modifier = Modifier.size(contentSize.value.width.pxToDp(), contentSize.value.height.pxToDp()))
}
}
}
fun Int.pxToDp(): Dp {
return (this / getSystem().displayMetrics.density).dp
}
usage:
Visibility(text.value.isNotEmpty()) {
IconButton(
onClick = { text.value = "" },
modifier = Modifier
.padding(bottom = 8.dp)
.height(30.dp),
) {
Icon(Icons.Filled.Close, contentDescription = "Clear text")
}
}

Categories

Resources