I have a 3rd party library which provides their st...
# compose
u
I have a 3rd party library which provides their stuff as a `Fragment`s Is it possible to embed these into composables? Sort of like SwiftUI has
UIViewControllerRepresentable
for embedding view controllers as swiftui views
I have seen the
AndroidViewBinding
thing, but that's only for fragments inflatable from xml, i.e. fragment needs to have no arguments, no?
s
Does this https://developer.android.com/jetpack/compose/migrate/interoperability-apis cover your use case perhaps? Also what 3rd party library exposes things as fragments 👀
u
Basically all libs which expose ui and dont have native compose support
what are you reffering to exactly? clicking the link gets me to root of the docs
s
Don't they usually expose Views instead? But regardless, the interop apis should be able to get you what you need.
Yeah it's the root, from there go to what you're looking for. In your case using views in compose.
u
But my case its fragments in compose, not views
s
Okay, did you actually try to go through the docs?
well, the first comment anyways, thats for paramless fragments, am I right? i.e. unusable for one which take a
Bundle
s
How do your fragments with parameters look like? Can you try and use the lower level API which gives you access to the "factory" lambda which lets you call the constructor in whatever way you please perhaps?
u
the usual old way
Copy code
val fragment = ...
fragment.arguments = Bundle..
fragmentManager.begin...add(containerId, fragment).commit
idiomatic fragments, nothing special the xml inflation was mostly a gimmick, everybody needed parameterized fragments
there are answers and samples in that thread. Pay attention what Ian says.
u
so am I correct in saying that the xml cannot have arguments?
s
yep
u
so the solution is what Muthuraj says?
s
just add fragment programmatically
But check if it's properly destroyed after your composable function leaves the composition tree.
u
like this?
Copy code
AndroidView(modifier = Modifier.padding(it), factory = { context ->
            FragmentContainerView(context).apply {
                id = fragmentId

                activity.supportFragmentManager
                    .beginTransaction()
                    .add(fragmentId, YourFragment())
                    .commit()
            }
        }, update = {
        })

        // To remove the fragment when this composable is removed.
        DisposableEffect(Unit) {
            onDispose {
                activity.supportFragmentManager
                    .findFragmentById(fragmentId)
                    ?.let { fragment ->
                        activity.supportFragmentManager
                            .commit {
                                remove(fragment)
                            }
                    }
            }
        }
    }
s
sort of
u
what would be wrong?
s
What do you mean? If you don't dispose of the fragment once the composable function exits, it could lead to memory leaks, potential crashes, or visual bugs.
u
i am doing that, am I not?
s
I dont get your question than.
is this not correct?
s
rephrase your question, what is not correct?
u
I posted the sample, and then you said its sort of right, I asked how is it wrong, you said I need to remove the fragment on composable disposal - but the sample already does that so I'm confused
s
I mean, this code will allow you to add a fragmant to a compose widget, but don't forget to check in debugger or in memory analyzer if it correctly destroys after.
u
yea sure but why would it not, onDispose is hopefully guaranteed
s
Everything will depend on your final code, so I just suggest to doublecheck it.
u
but looking at the sample, see any issues?
s
at first glance it is fine
u
k thanks!
👍 1
btw it works mostly fine, but crashed on screen rotation
Copy code
FATAL EXCEPTION: main
                 Process: sk.o2.mojeo2.mock, PID: 9574
                 java.lang.RuntimeException: Unable to destroy activity {sk.o2.mojeo2.mock/sk.o2.mojeo2.MainActivity}: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
                 	at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5654)
                 	at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5686)
                 	at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5964)
                 	at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:5883)
                 	at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:76)
                 	at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
                 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
                 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
                 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2468)
s
looks like you have leaked your activity context or. hard to say something not seeing the code
u
is the sample from above, no changes
not sure why would I leak context, it says I cannot perform destroy after onSaveInstanceState i.e. onSaveInstanceState was called and then compose wants to remove my wrapper AndroidView, which then wants to remove the fragment
s
try to do it next:
Copy code
.commitAllowingStateLoss {
    remove(fragment)
}
or .commitNowAllowingStateLoss
u
that obviously wont crash, but state wont be saved