Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple example to handle back button INSIDE fragment #258

Closed
omkar-tenkale opened this issue May 13, 2022 · 5 comments
Closed

Simple example to handle back button INSIDE fragment #258

omkar-tenkale opened this issue May 13, 2022 · 5 comments

Comments

@omkar-tenkale
Copy link

i know its possible with ScopedServices.HandlesBack but donno how

@omkar-tenkale
Copy link
Author

Used this approach

class BackHandler():ScopedServices.HandlesBack{
    var callback : Callable<Boolean>? = null
    override fun onBackEvent(): Boolean {
        callback?.let { return it.call() } ?: return false
    }
}
@Parcelize
class ABCKey() : DefaultFragmentKey(), DefaultServiceProvider.HasServices {
    override fun instantiateFragment(): Fragment = ABCFragment()

    override fun getScopeTag(): String = javaClass.name

    override fun bindServices(serviceBinder: ServiceBinder) {
        with(serviceBinder) {
            add(BackHandler(),"BackHandler")
        }
    }
}

Inside fragment

    private val backHandler by lazy {  lookup<BackHandler>("BackHandler") }
backHandler.callback = object : Callable<Boolean>{
            override fun call(): Boolean {
                if(shouldHandle()){
                    //handleBackPress
                    return true
                }
                return false
            }
        }

Not sure if this can be improved 🤔 @Zhuinden

@Zhuinden
Copy link
Owner

Zhuinden commented May 13, 2022

This is my current code for this

    private val backPressedCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            val topKey = backstack.top<FragmentKey>()

            val fragment = supportFragmentManager.findFragmentByTag(topKey.fragmentTag)

            if (fragment is BackHandler) {
                val handled = fragment.onBackPressed()

                if (handled) {
                    return
                }
            }

            val handled = Navigator.onBackPressed(this@MainActivity)

            if (handled) {
                return
            }

            // default fallback
            this.remove() // this is the only safe way to invoke onBackPressed while cancelling the execution of this callback
            onBackPressed() // this is the only safe way to invoke onBackPressed while cancelling the execution of this callback
            this@MainActivity.onBackPressedDispatcher.addCallback(this) // this is the only safe way to invoke onBackPressed while cancelling the execution of this callback
        }
    }

// ...


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)

        onBackPressedDispatcher.addCallback(backPressedCallback)
        ...
    }

    @Suppress("RedundantModalityModifier")
    override final fun onBackPressed() {
        super.onBackPressed() // onBackPressed() was killed by Google (thanks Google)
    }

And

interface BackHandler {
    fun onBackPressed(): Boolean
}

Then in my Fragment I can do this:

    override fun onBackPressed(): Boolean {
        if(...) {
             ...
             return true
        }

        return false
    }

But when using this hacky-looking approach using OnBackPressedDispatcher, it's actually possible to do this in the fragment (i relied on this in 1 fragment, which is why I'm using back like this in this project)

// Fragment onViewCreated
        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, backPressedCallback)

        backPressedCallback.isEnabled = isOverlayShowing
    }

@omkar-tenkale
Copy link
Author

omkar-tenkale commented May 13, 2022

onBackPressed() was killed by Google (thanks Google)
when?

(readme section not very clear)

@Zhuinden
Copy link
Owner

@omkar-tenkale if you want to use OnBackPressedDispatcher (which is internally used by BackHandler in Compose), then you cannot use onBackPressed because the order is wrong. You would execute your logic in onBackPressed before super.onBackPressed can try to delegate the back press to the Fragment's onBackPressedDispatcher.

It is a major design oversight from Google, actually. It took me a while to be convinced that yes, removing the callback and re-adding the callback is the correct way to do it. In fact, it is the only correct way to do it. 🤨

But it also comes with that targetSdkVersion 34 and Android Tiramisu deprecates onBackPressed() entirely, so I guess they also noticed that AndroidX broke the expected behaviors. I have an issue tracking this #259 but can't do much about it until Tiramisu is out.

@omkar-tenkale
Copy link
Author

oh, got it now, thx!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants