just about Hacking the Compose Backside sheet. Working with the ModalBottomSheetLayout… | by Peter Törnhult | Feb, 2023 will cowl the most recent and most present steering roughly talking the world. entrance slowly suitably you perceive skillfully and accurately. will mass your data expertly and reliably
When migrating to Compose I found that the ModalBottomSheetLayout
it is carried out somewhat in another way in Compose than I anticipated. This brought on me some points with nested screens in modules and tabbed looking. When you have an analogous looking setup to mine, possibly this can assist 😊
TL;DR; It is a very app-specific situation which will solely apply to you in case your app makes use of nested tabs.
Scaffolds
from which you wish to management the underside sheet. The next picture reveals my downside on the left and my desired consequence on the suitable. Maintain studying to know extra 😉
Within the outdated days (earlier than Compose/BC), we might create a backside sheet like Dialog
utilizing the BottomSheetDialogFragment
class. We might hold the UI logic contained in the BottomSheetDialogFragment
aside from the Fragment
was launched from And the underside sheet and the primary fragment might share a ViewModel
when mandatory. That was a minimum of how I carried out it in my purposes. I wished to port this circulate to Compose, however I hit a bump within the highway.
Though Compose has assist for Dialogs
in a means that I might have preferred to make use of, the ModalBottomSheetLayout
as a substitute, it’s meant for use as a wrapper round your display screen (Scaffold
). You should utilize a dialog like this:
@Composable
enjoyable Screen1() {
var showDialog by bear in mind mutableStateOf(false) Scaffold
// Display screen UI
showDialog = true
)
if (showDialog)
Dialog(...)
whereas the ModalBottomSheetLayout
is carried out like this:
@Composable
enjoyable Screen1()
val scope = rememberCoroutineScope()
val bottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)ModalBottomSheetLayout(
sheetState = bottomSheetState,
sheetContent =
// Bottomsheet UI
,
content material =
Scaffold
// Display screen UI
scope.launch bottomSheetState.present()
,
)
When opened, each will cowl the Scaffold
content material with a Dialog
or a modal backside sheet.
It is easy sufficient to make use of, however what occurs when your display screen is used inside tabbed looking? Then your code turns into extra nested and will look extra like this:
@Composable
enjoyable TabsScreen()
Scaffold(
bottomBar =
NavigationBar(...) // or BottomNavigation in Material2
)
NavHost(...)
composable(...)
Screen1()
composable(...)
Screen2()
When you used a Dialog
inside Screen1/2
it might nonetheless seem overlaying all the display screen (even the tabs). However, in case you selected a backside sheet, the underside sheet is not going to cowl the entire display screen, however solely the within Scaffold
(ex. Screen1
, Screen2
and many others above)
To me this does not really feel proper as a result of I desire a modal backside sheet to cowl all the display screen. That’s, the consumer ought to solely be capable to work together with the underside sheet and never the tabs on the identical time.
The best way I solved this was to maneuver the ModalBottomSheetLayout
code across the Scaffold
containing the tabs. This would not have been a lot of an issue if my app was a single module app. However in my case, every interior display screen lives in its personal module and is related through the highest stage app module that incorporates the tab navigation scaffolding.
So, I wanted a strategy to management the underside sheet from the submodules. 🤔
:utility
│
├──► :dwelling
│
├──► :rounds
│
├──► :gamers
│
└──► :menu
I created a typealias
and put it in a typical submodule so all earlier modules might entry it.
typealias SheetContent = @Composable ColumnScope.() -> Unit
Then I added 2 features to every interior display screen. Composable
:
showBottomSheet: (SheetContent) -> Unit,
hideBottomSheet: () -> Unit,
on the prime stage Scaffold
I added the implementation for these 2 features:
val showBottomSheet: (SheetContent) -> Unit = content material: SheetContent ->
bottomSheetContent = content material
scope.launch bottomSheetState.present()
val hideBottomSheet: () -> Unit =
scope.launch
bottomSheetState.conceal()
bottomSheetContent = null
And carried out the underside sheet content material like so:
@Composable
enjoyable TabsScreen() {
val bottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
var bottomSheetContent: SheetContent? by bear in mind mutableStateOf(null) Scaffold(
sheetContent =
bottomSheetContent?.invoke(this)
,
sheetState = bottomSheetState,
bottomBar =
NavigationBar(...) // or BottomNavigation in Material2
...
With this, I can now management (present/conceal) the underside sheet of every interior display screen, whereas protecting the underside sheet Composable
logic contained subsequent to mother or father Composable
inside every submodule. The whole logic seems like this:
@Composable
enjoyable TabsScreen()
val bottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
var bottomSheetContent: SheetContent? by bear in mind mutableStateOf(null) val showBottomSheet: (SheetContent) -> Unit = content material: SheetContent ->
bottomSheetContent = content material
scope.launch bottomSheetState.present()
val hideBottomSheet: () -> Unit =
scope.launch
bottomSheetState.conceal()
bottomSheetContent = null
BackHandler(bottomSheetContent != null)
hideBottomSheet()
Scaffold(
sheetContent =
bottomSheetContent?.invoke(this)
,
sheetState = bottomSheetState,
bottomBar =
NavigationBar(...) // or BottomNavigation in Material2
)
NavHost(...)
composable(...)
Screen1(
showBottomSheet = showBottomSheet,
hideBottomSheet = hideBottomSheet,
)
composable(...)
Screen2(
showBottomSheet = showBottomSheet,
hideBottomSheet = hideBottomSheet,
)
...
@Composable
enjoyable Screen1(
showBottomSheet: (SheetContent) -> Unit,
hideBottomSheet: () -> Unit,
)
Scaffold
// Display screen UI
...
showBottomSheet
// Bottomsheet UI
To hack or to not hack
So why is that this a cheat? We spend composables on a regular basis, proper? Positive, we handed Composables
backside in nested Composables
however on this case, we’re going by it above to a different Composable
. To be sincere, I am undecided it is an issue in any respect. This works (a minimum of for me) and I have not seen any rapid issues with the answer. However on the identical time, I have not seen anybody recommend this in any examples, so I’ve a suspicion this may occasionally come again to trouble me sooner or later and may be thought-about a code scent. That stated, that is my solely caveat.
Use at your individual discretion ¯_(ツ)_/¯
I’ve come this far, I’m very comfortable. Possibly I am not the one one with this downside 😅.
Please clap and share
I hope the article very almost Hacking the Compose Backside sheet. Working with the ModalBottomSheetLayout… | by Peter Törnhult | Feb, 2023 provides notion to you and is helpful for addendum to your data