Hi <@UHAJKUSTU> I do have question regarding the "...
# decompose
j
Hi @Arkadii Ivanov I do have question regarding the "decompose"-architecture. E.g. I have the following: • StackNavigationComponent/StackNavigationContent ◦ Consists of Scaffold with hamburger-button, back-button, topBar and modalDrawer ◦ Within its content of Scaffold List & Detail are displayed • ListComponent/ListContent ◦ Not important for my question • DetailComponent/DetailContent ◦ Detail with BottomSheetScaffold which can be expanded/collapsed by user via drag or toggle-button When the user presses the toggle DetailComponent.setIsBottomSheetExpanded(isBottomSheetExpanded) is executed which bubbles all the way up to StackNavigationComponent. Within StackNavigationComponent I have a UiState data class with a property isBottomSheetExpanded. UiState is then in StackNavigationContent observed and passes isBottomSheetExpanded via StackNavigationContent back to DetailContent. In DetailContent I then expand() or collapse() my bottomSheet via a rememberCoroutineScope depending on the value of isBottomSheetExpanded. I am wondering if passing isBottomSheetExpanded back to DetailContent via composables is the right approach or if I should instead pass it back via components. I think using the later would be better because all logic is decoupled from my ui which then would be completely pluggable. If the later is recommended then I am wondering how I can pass something as an observable to my DetailComponent. In the decompose-example within ArticleListComponent/ArticleDetailsComponent I saw something like isToolbarVisible: Observable<Boolean>. Is there an alternative with flows available? I have not found anything related to that in the documentation.
a
Good question! I recommend placing a Scaffold on each screen. This allows managing bottom sheets on each screen, also displaying proper title, appbar actions, etc. And it also pays nicely with the new predictive back gesture. But if you really want to host Scaffold in the parent, then it depends on whether you want to manage it via Decompose or just with Compose. In the latter case you can add the callback directly to the DetailsContent function. No need to pass via components. The former case is if you want to utilize e.g. ChildSlot for this. In this case, DetailsContent should call a function on its DetailsComponent, and bubble this up to the parent which activates ChildSlot.
Re question about Observable. You can use SharedFlow instead.
j
Currently I think I am using a hybrid approach. My DetailContent calls my DetailComponent to change the isBottomSheetExpanded flag. This is bubbling up all the way to my StackNavigationComponent which then triggers an UIState change for isBottomSheetExpanded. This state change is then bubbling down via StackNavigationContent to my DetailContent to expand/collapse my sheet (composable approach). I am just wondering if I should use components to bubble the change of isBottomSheetExpanded to my DetailComponent and let DetailContent observe that via Value. I mean its working as expected, but just want to know your opinion about this and I am not 100% sure if my ui is completely decoupled. Sorry if I repeat myself, just want to make it more clear.
a
It's not clear how do you use expand/callapse in your DetailsContent together with the state flag.
j
Currently bei DetailsContent is like this:
Copy code
@Composable
fun DetailsContent(
    component: IOrderMenuComponent,
    isBottomSheetExpanded: Boolean,
    modifier: Modifier = Modifier
){
    val scope = rememberCoroutineScope()
    val scaffoldState = rememberBottomSheetScaffoldState()
    BottomSheetScaffold(
        scaffoldState = scaffoldState,
        sheetPeekHeight = 56.dp,
        sheetContent = {...}
    )
    ...
    // expand/collapse depending on isBottomSheetExpanded
    if (isBottomSheetExpanded) {
        scope.launch { scaffoldState.bottomSheetState.expand() }
    } else {
        scope.launch { scaffoldState.bottomSheetState.collapse() }
    }
    ...
    // Listen to bottomSheetState e.g. if user dragged sheet to   
       expanded/collapsed state
    LaunchedEffect(Unit) {
        snapshotFlow { 
            scaffoldState.bottomSheetState.currentValue 
        }.collect {
            component.setIsBottomSheetExpanded(
                isExpanded = it == BottomSheetValue.Expanded
            )
        }
    }
}
hard to write alls this stuff, so if you do not have a clue about what I am trying than its fine and I'll investigate on my own how to do it or keep it as is ...
but basically I am wondering if I should pass my state flag (isBottomSheetExpanded) to the composable or if I should use a Value<Boolean> in my DetailComponent and subscribe to that in my DetailContent.
Maybe it does not matter, but my feeling is that with this approach (code example) my UI is not completely decoupled.
a
Ok, so you are hosting your scaffold state in DetailsContent, which is good. Then why do you need to pass this through the parent? You could just have this part of the DetailsComponent state. You could even manipulate
scaffoldState.bottomSheetState
directly in your
LaunchedEffect
, if this works for you.
j
Currently I bubble this state up to check if my sheet is expanded and enable backCallback to collapse my sheet if the user presses back-button in topBar or hardware-back
a
Ok, I think you can't tap the topbar when the bottom sheet is expanded. For the back button handling, you can register a callback in your DetailsComponent and dismiss the sheet right there. It doesn't look like the parent should be aware of those details.
j
Hm maybe we are talking about different thinks. Back-Button is working. And I can tap on my topBar without any problems.
image.png
image.png
But nevermind. This is going to far ...
a
The main point is to host the bottom sheet state in the details component, where it belongs to.