Hi, how can I make a button shake when I click on ...
# compose
a
Hi, how can I make a button shake when I click on it ?
l
I don’t think there is anything out of the box for it, but take a look at animateFloatAsState https://developer.android.com/jetpack/compose/animation#animate-as-state
You could have a animateAsState for a padding value, and then have a button with that padding
If you use a spring, you might be able to get it to shake https://developer.android.com/jetpack/compose/animation#animationspec
Might have to code a custom Animation spec for it though
a
okay I created the animation, now how to I execute it when I click on my button ?
l
Have a mutable state property change state
a
yes but which state ?
I searched everything and I've don't found how to get the state of the button
l
Could you paste some code? You have to do something like the examples
Copy code
val alpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f)
Box(
    Modifier.fillMaxSize()
        .graphicsLayer(alpha = alpha)
        .background(Color.Red)
)
This example has a property
enabled
var enabled by remember { mutableStateOf(false) }
declare something like this, and then make your animated property change based on that variable
a
enabled is for enabling the button or not, letting you click on it or not, I don't want this, I want to animate it when I click on the button
for now I have this
Copy code
@Composable
fun AddTaskButton(todos: MutableState<List<Todo>>, text: MutableState<TextFieldValue>, size: MutableState<Int>) {
	Box(modifier = Modifier) {}
	Button(
		modifier = Modifier.fillMaxHeight(),
		onClick = { addTask(text, todos, size) },
		colors = ButtonDefaults.buttonColors(Color.LightGray)
	) {
		Text("Add task")
	}
}
and in my main()
Copy code
fun main() = Window(title = "Test", size = IntSize(1600, 900)) {
	val size = remember { mutableStateOf(1) }
	val todos = remember { mutableStateOf(listOf(Todo("test #${size.value}"))) }
	val text = remember { mutableStateOf(TextFieldValue()) }
	
	MaterialTheme {
		LocalAppWindow.current.keyboard.onKeyEvent = {
			if (it.key == Key.Enter) {
				addTask(text, todos, size)
				true
			} else false
		}

		AddTaskButton(todos, text, size)

		todos.value.forEach {
			Task(it, todos) // this is another composable, not important here
		}
	}
}
l
I meant you need a property you can toggle to trigger the animation, doesn’t need to be called enabled, can be called anything. Where is the animation code?
a
I have this
Copy code
@Composable
fun shake() = spring<IntOffset>(
	dampingRatio = Spring.DampingRatioHighBouncy,
	stiffness = Spring.StiffnessVeryLow
)
but I don't understand where and how I should put it
l
Use a
offset
modifier on the button you want to animate. on the offset property, instead of passing a regular distance, pass a
animated*
like in the example
That spring can be used in one of the parameters of the animated call I think
a
so
Copy code
val offset: IntOffset by animateIntOffsetAsState(
	targetValue = IntOffset(10, 10),
	animationSpec = shake(),
)
	
Button(
	modifier = Modifier.fillMaxHeight().offset { offset },
	// [...]
I don't know what to put for
targetValue
for now the button is just displaced
l
Ah, you can use
.offset(x = offset)
instead, if you only need to shake horizontally
To trigger the animation, you need some state
var enabled by remember { mutableStateOf(false) }
and then the target value depends on that state
targetValue = if (enabled) 10 else 0
so when you click the button, you toggle that state on and off, and it should trigger the animation
… I think 😛
a
no, again
enabled
is a property to define if you can click on the button, not when it is clicked
l
Yes, that is correct but thats not what I’m saying
Declare
var potatoes by remember { mutableStateOf(false) }
Copy code
val offset: IntOffset by animateIntOffsetAsState(
	targetValue = if (potatoes) IntOffset(10, 10) else IntOffset(0,0),
	animationSpec = shake(),
)
Button(
	modifier = Modifier.fillMaxHeight().offset { offset },
	// [...]
a
but how do I define the value of
potatoes
? this is what I'm asking
l
I mean
the same way you are defining the offset, I pasted it for you aboce
Copy code
var potatoes by remember { mutableStateOf(false) }
val offset: IntOffset by animateIntOffsetAsState(
	targetValue = if (potatoes) IntOffset(10, 10) else IntOffset(0,0),
	animationSpec = shake(),
)
Button(
	modifier = Modifier.fillMaxHeight().offset { offset },
	// [...]
a
no you still doesn't understand, I don't mean that I don't understand how to declare the variable but how to set the value, I know that I can use the
onClick()
function of my button but doesn't this will activate the animation for ever ? or just one time ?
j
Reset the flag using the `finishedListener`:
Copy code
val shouldAnimate by remember { mutableStateOf(false) }
val offset: IntOffset by animateIntOffsetAsState(
    targetValue = if (shouldAnimate) IntOffset(10, 10) else IntOffset(0,0),
    animationSpec = shake(),
    finishedListener = { shouldAnimate = false }
)
Button(onClick = {shouldAnimate = true})
👍 2
a
oh nice it works !
Now I have to resolve the final problem of this, the shake animation is not right, for now it oscillates between two
IntOffset
which is logic because it's what we defined, but I want a
shake
effect so how should I do it ?
okay someone literally have made exactly what I want on this slack https://kotlinlang.slack.com/archives/CJLTWPH7S/p1621869799278100 thanks for the help everyone x)
👍 1
l
Nice! This is great for once off things!
153 Views