https://kotlinlang.org logo
#compose
Title
# compose
t

Tash

06/07/2020, 7:27 PM
Is there a Compose construct for handling back button presses? So far I've just been trying to use the Activity's
OnBackPressedDispatcher
, but wondering if there's a better way...
a

Adam Powell

06/07/2020, 7:29 PM
I've done it this way and it's worked well https://gist.github.com/adamp/62d13fe5bf0d6ddf9fcf58f8a6769523
👍🏽 1
💯 1
🙏🏼 2
t

Tash

06/07/2020, 7:42 PM
Wow, this is great. Didn't think to use a static Ambient in this way. Thanks so much!!
z

Zach Klippenstein (he/him) [MOD]

06/07/2020, 7:47 PM
Curious why you’re updating the
onBackPressed
property in an
onCommit
– I’ve seen a lot of internal Compose code update model properties like directly from the composable function, with no
onCommit
wrapper. It looks like an optimization, but I would think
onCommit
is actually more expensive than just setting a property. I’m guessing it has something to do with handling frames/threading correctly?
💡 4
a

Adam Powell

06/07/2020, 9:33 PM
Yep, that's why. If you come across code that is updating non-
MutableState
properties of things that live beyond the current composition pass during composition, it's a bug. And yeah, we've got them here and there.
🙏🏼 2
And that includes objects that you
remember {}
across compositions
Even in the absence of threading, a composition can fail and its changes won't be applied to the resulting UI tree. By using onCommit for side effects of composition you ensure you never have to try to roll back when that happens
👍🏼 2
p

pavi2410

06/08/2020, 4:41 PM
I hacked up a back press handler like this
Copy code
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {

            val backPressed = state { false }

            onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                    backPressed.value = true
                }
            })

            MaterialTheme {
                Navigator(backPressed)
            }
        }
    }
}
a

Adam Powell

06/08/2020, 5:43 PM
that doesn't really work since it only handles one transition from false to true, and the ownership of who or what should set it back to false again is unclear. It's likely to lead to composition loops or non-idempotent operations
p

pavi2410

06/08/2020, 5:46 PM
I sensed it was not right, but wanted a back press handler anyway. I think this is why AlertDialog wasn't closing https://kotlinlang.slack.com/archives/CJLTWPH7S/p1591295610338600?thread_ts=1591295610.338600&cid=CJLTWPH7S
a

Adam Powell

06/08/2020, 5:48 PM
constructs like that AlertDialog example should be fine since, "is the dialog showing?" is a state. The back button being pressed is an event; it happens instantaneously, it might mutate other state, but then it is done. It shouldn't persist as a, "the back button was pressed at some point in the past" state on its own.
p

pavi2410

06/08/2020, 5:58 PM
But I am resetting the backpressed state when it is consumed, and it worked well for navigation between screens but not Exit dialog. I am yet to learn Ambient and onCommit stuff though. 😁
a

Adam Powell

06/08/2020, 6:03 PM
I expect that the consumption code for that back pressed state either has bugs, is error-prone, or both 🙂
😁 1
v

Vinay Gaba

06/09/2020, 4:25 AM
I’m curious about how
AmbientBackPressedDispatcher
gets any value at all. Where is the value being implicitly being passed from given that we initialized it to null in the code example.
Is there a default
Provider
that uses it and provides a value (like the current activity in this case)
my guess is that it’s coming from here?
Copy code
Wrapper.kt

.
.
.
LifecycleOwnerAmbient provides lifecycleOwner
hmm it only worked when I provided my own Provider. Curious to know if there was a better way to handle it and there was indeed an implicity way where this value was being provided.
2 Views