Hey Composers! I have a question about <https://de...
# compose
a
Hey Composers! I have a question about Material 3 ModalBottomSheet and handling the Back button I want to forbid closing current bottom sheet depending on some logical state - for instance, if a critical action is in progress, don't allow closing the sheet. I was able to make it work in Material 2 implementation, and now I'm struggling with Material 3.
ModalBottomSheet
has SheetState, which has confirmValueChange - a callback that I use to forbid
ModalBottomSheet
dismiss depending on its logical state. This works fine with "tap outside sheet" and "drag sheet down" dismiss actions. However, a system Back button press does not respect
confirmValueChange
- it just dismisses the sheet without any hesitation. There are ModalBottomSheetProperties, such as
shouldDismissOnBackPress
. Unfortunately, setting
shouldDismissOnBackPress=false
just disables the back button completely - the sheet's Window intercepts it, but does nothing.
BackHandler
also doesn't work, as it requires underlying support in the implementation of Window.
Dialog
composable has this support, but not
ModalBottomSheet
. Basically, I want everything to work exaclty as it does, except make back button press respect
confirmValueChange
. I even tried copying MaterialBottomSheet sources to modify this behavior, but it's a banana-monkey-jungle problem, too much stuff is
internal
. Any advice on how to make this work? Thanks in advance!
j
You need to override the back press dispatcher API as well, to ignore back press. Or using BackHandler. I can also throw in I strongly advice using this UX pattern, never block user from pressing back to close. If critical action, just re-open it again next time same action need to happen imo or something else πŸ™‚
a
You're absolutely right about the UX side, but the technical question still stands) BackHandler does not work in Material 3 Bottom Sheet - it opens as a Window (ModalBottomSheetWindow to be precise), and it lacks support for back handlers
j
Dont use the bottomsheet at all for back handling is what I am saying πŸ™‚ Ignore whatever Google doing.
a
I open Material 3 bottom sheet, and there is no way to override it's back button handling. I can either disable it completely (then nothing happens, as back press is handled by sheets Window anyway), or go with the default implementation
j
Just do like:
Box {
BackHandler()
ModalBottomSheet()
}
a
this back handler will be ignored becasue Window inside ModalBottomSheet intercepts back presses
j
I am using like this without any issues:
Copy code
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
        val coroutineScope = rememberCoroutineScope()
        BackHandler(enabled = sheetState.isVisible) {
            coroutineScope
                .launch { sheetState.hide() } // Ignore in your case
                .invokeOnCompletion {
                    if (!sheetState.isVisible) {
                        // do something
                    }
                }
        }

        ModalBottomSheet(
            modifier = Modifier.fillMaxWidth(),
            content = {
                content(model)
            },
            sheetState = sheetState,
            onDismissRequest = { navigator.finish(onDismiss()) },
        )
Not sure what you mean with WIndow inside intercept, why would it do that?
a
are you sure your backhandler works? because by default ModalBottomSheet will intercept back press and will close internally.
j
Yeah it works
a
Not sure what you mean with WIndow
Material 3 Bottom Sheet is implemented internally as a ModalBottomSheetWindow. It handles back press, because if you have more than one open Window, the back press is dispatched to the topmost one, which is ModalBottomSheetWindow
j
One major difference in my case I am using Circuit Overlay to display the bottomsheet, if that impacts maybe πŸ™‚
a
Yeah it works
try removing everything inside your BackHandler - I bet back button will still close your bottom sheet
what is Circle Overlay?
"try removing everything inside your BackHandler - I bet back button will still close your bottom sheet" Yeah ofc as I want it to be dismissed πŸ˜› But I will try what you say, to check, sounds interesting.
a
I mean it just as a scientific experiment) If you think your BackHandler works - try removing it. If back press still works, then maybe it wasn't working due to BackHandler after all)
j
Yeah its interesting, seems still work.
🦜 1
a
> I am using Circuit Overlay yeah, this could be it as well, there is some under-the-hood magic in Circuit for sure
j
Circuit πŸ˜„
πŸ’― 1
Adding
properties = ModalBottomSheetDefaults.properties(shouldDismissOnBackPress = false)
ignores back press on bottomsheet. WIll now try combine everything to see how behaves.
Right, yeah I get same problem as you now, sorry πŸ˜„
a
yes it does, but it does NOT make backhandler work!
Thanks, friend, at least now I know I'm not insane)
TL;DR of the thread for future brave heroes: the problem in the original post is reproducible and not solved yet)
j
I got interested and will check source code of what the heck Google did here, seems insane.
a
You're in for a wondrous journey)
j
Haha yeah kill me, are we going back in time with WIndowManager now πŸ˜„ I will stop using Googles things if this is the new thing
Feels like DIalogFragment but for Compose AbstractView now πŸ˜„
a
Yeah, looks not ideal, to say the least
j
Copy code
override fun onAttachedToWindow() {
        super.onAttachedToWindow()

        maybeRegisterBackCallback()
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()

        maybeUnregisterBackCallback()
    }

    private fun maybeRegisterBackCallback() {
        if (!properties.shouldDismissOnBackPress || Build.VERSION.SDK_INT < 33) {
            return
        }
        if (backCallback == null) {
            backCallback = Api33Impl.createBackCallback(onDismissRequest)
        }
        Api33Impl.maybeRegisterBackCallback(this, backCallback)
    }

    private fun maybeUnregisterBackCallback() {
        if (Build.VERSION.SDK_INT >= 33) {
            Api33Impl.maybeUnregisterBackCallback(this, backCallback)
        }
        backCallback = null
    }
This wants me go hide and never look at this code again ...
πŸ’― 1
s
Is it possible that you are using
enableOnBackInvokedCallback
on your manifest and not the latest material3 library and are hitting this https://issuetracker.google.com/issues/281967264?
a
Thanks, but sadly no( dismiss works fine, and the code is the same, there just isn't enough customization options, and I have to copy-paste the entire thing to tweak it. Open/closed principle is too closed in this case)
πŸ‘ 1
βž• 1
a
Which material3 version are you reproducing the issue with?
j
Not sure if @alaershov using same, but I use: https://github.com/JetBrains/compose-multiplatform/releases/tag/v1.6.0-rc03 Which refers to version Material3 1.2.0 Thats reproduce the issue for me as well. I think there was some kind of bug fix for this in M2 1.3.0.alpha01 or? I refer to https://issuetracker.google.com/issues/281967264#comment7
a
1.2.0 for me as well, using BOM 2024.02.01
I don't thinkk this is the fix @Joel Denke, looks more like additional support for predictive back on the newest API
@Alex Vanyo can you tell us if ignoring confirmValueChange is a deliberate design decision for back button handling in Material 3 ModalBottomSheet? It's a reasonable default for sure, but would be nice to allow the user to customize it.
j
I am the brave hero. Thanks for confirming I'm not crazy. Anyone have a workaround?
a
@Jacob Rhoda I ended up copying the whole Material 3 ModalBottomSheet implementation to my project just to fix this one issue. There is a probable fix in https://developer.android.com/jetpack/androidx/releases/compose-material3#1.3.0-alpha06 ModalBottomSheet is now written as a Dialog instead of popup, so maybe back handler works now
K 1