Skip to content

Commit d3f7e68

Browse files
committed
fix back navigation in the bottomsheet
1 parent 857a8b5 commit d3f7e68

File tree

9 files changed

+111
-71
lines changed

9 files changed

+111
-71
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,4 @@ app/google-services.json
9090
.idea/sonarlint*
9191
.idea/androidTestResultsUserPreferences.xml
9292
.idea/deploymentTargetDropDown.xml
93+
.idea/deploymentTargetSelector.xml

Diff for: .idea/deploymentTargetSelector.xml

-18
This file was deleted.

Diff for: app/src/main/kotlin/br/com/mob1st/bet/features/launch/presentation/MainActivity.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ internal fun NavigationGraph() {
6262
val navController = rememberNavController(bottomSheetNavigator)
6363
val financesNavGraph = koinInject<FinancesNavGraph>(
6464
parameters = {
65-
parametersOf(navController)
65+
parametersOf(navController, bottomSheetNavigator)
6666
},
6767
)
6868
ModalBottomSheetLayout(

Diff for: core/androidx/src/main/kotlin/br/com/mob1st/core/androidx/navigation/BottomSheetNavigator.kt

+35-41
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
package br.com.mob1st.core.androidx.navigation
44

5-
import androidx.activity.compose.BackHandler
5+
import androidx.compose.foundation.layout.ColumnScope
66
import androidx.compose.material3.ExperimentalMaterial3Api
77
import androidx.compose.material3.SheetState
88
import androidx.compose.runtime.Composable
@@ -11,7 +11,6 @@ import androidx.compose.runtime.collectAsState
1111
import androidx.compose.runtime.getValue
1212
import androidx.compose.runtime.mutableStateOf
1313
import androidx.compose.runtime.produceState
14-
import androidx.compose.runtime.rememberCoroutineScope
1514
import androidx.compose.runtime.rememberUpdatedState
1615
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
1716
import androidx.compose.runtime.setValue
@@ -29,7 +28,6 @@ import kotlinx.coroutines.flow.StateFlow
2928
import kotlinx.coroutines.flow.distinctUntilChanged
3029
import kotlinx.coroutines.flow.drop
3130
import kotlinx.coroutines.flow.transform
32-
import kotlinx.coroutines.launch
3331
import kotlin.coroutines.cancellation.CancellationException
3432

3533
@Navigator.Name("bottomSheet")
@@ -70,7 +68,7 @@ class BottomSheetNavigator(
7068
* sheetContent of your [ModalBottomSheetLayout].
7169
*/
7270

73-
internal var sheetContent: @Composable () -> Unit = {}
71+
internal var sheetContent: @Composable ColumnScope.() -> Unit = {}
7472
internal var onDismissRequest: () -> Unit = {}
7573

7674
internal val sheetInitializer: @Composable () -> Unit = {
@@ -90,7 +88,7 @@ class BottomSheetNavigator(
9088
// Regardless of whether we're popping or pushing, we always want to hide
9189
// the sheet first before deciding whether to re-show it or keep it hidden
9290
try {
93-
sheetEnabled = false
91+
sheetState.hide()
9492
} catch (_: CancellationException) {
9593
// We catch but ignore possible cancellation exceptions as we don't want
9694
// them to bubble up and cancel the whole produceState coroutine
@@ -102,11 +100,26 @@ class BottomSheetNavigator(
102100
value = it
103101
}
104102
}
105-
106-
if (retainedEntry != null) {
107-
val currentOnSheetShown by rememberUpdatedState {
108-
transitionsInProgressEntries.forEach(state::markTransitionComplete)
103+
val show = {
104+
transitionsInProgressEntries.forEach(state::markTransitionComplete)
105+
}
106+
val dismiss = {
107+
sheetEnabled = false
108+
if (transitionsInProgressEntries.contains(retainedEntry)) {
109+
state.markTransitionComplete(retainedEntry!!)
110+
} else {
111+
// If there is no transition in progress, the sheet has been dimissed by the
112+
// user (for example by tapping on the scrim or through an accessibility action)
113+
// In this case, we will immediately pop without a transition as the sheet has
114+
// already been hidden
115+
retainedEntry?.let {
116+
state.pop(popUpTo = it, saveState = false)
117+
}
109118
}
119+
}
120+
if (retainedEntry != null) {
121+
val currentOnSheetShown by rememberUpdatedState(show)
122+
val currentOnSheetDismissed by rememberUpdatedState(dismiss)
110123
LaunchedEffect(sheetState, retainedEntry) {
111124
snapshotFlow { sheetState.isVisible }
112125
// We are only interested in changes in the sheet's visibility
@@ -116,47 +129,28 @@ class BottomSheetNavigator(
116129
.collect { visible ->
117130
if (visible) {
118131
currentOnSheetShown()
132+
} else {
133+
currentOnSheetDismissed()
119134
}
120135
}
121136
}
122137

123138
LaunchedEffect(key1 = retainedEntry) {
124139
sheetEnabled = true
125-
126140
sheetContent = {
127-
retainedEntry!!.LocalOwnersProvider(saveableStateHolder) {
128-
val content =
129-
(retainedEntry!!.destination as Destination).content
130-
content(retainedEntry!!)
141+
retainedEntry?.let { entry ->
142+
entry.LocalOwnersProvider(saveableStateHolder) {
143+
val content =
144+
(entry.destination as Destination).content
145+
146+
content(entry)
147+
}
131148
}
132149
}
133150
onDismissRequest = {
134-
sheetEnabled = false
135-
136-
if (transitionsInProgressEntries.contains(retainedEntry)) {
137-
// Sheet dismissal can be started through popBackStack in which case we have a
138-
// transition that we'll want to complete
139-
state.markTransitionComplete(retainedEntry!!)
140-
} else {
141-
// If there is no transition in progress, the sheet has been dimissed by the
142-
// user (for example by tapping on the scrim or through an accessibility action)
143-
// In this case, we will immediately pop without a transition as the sheet has
144-
// already been hidden
145-
state.pop(popUpTo = retainedEntry!!, saveState = false)
146-
}
151+
currentOnSheetDismissed()
147152
}
148153
}
149-
150-
val scope = rememberCoroutineScope()
151-
BackHandler {
152-
scope
153-
.launch { sheetState.hide() }
154-
.invokeOnCompletion {
155-
if (!sheetState.isVisible) {
156-
onDismissRequest()
157-
}
158-
}
159-
}
160154
} else {
161155
LaunchedEffect(key1 = Unit) {
162156
sheetContent = {}
@@ -181,12 +175,12 @@ class BottomSheetNavigator(
181175
navigatorExtras: Extras?,
182176
) {
183177
entries.fastForEach { entry ->
184-
state.push(entry)
178+
state.pushWithTransition(entry)
185179
}
186180
}
187181

188182
override fun popBackStack(popUpTo: NavBackStackEntry, savedState: Boolean) {
189-
state.pop(popUpTo, savedState)
183+
state.popWithTransition(popUpTo, savedState)
190184
}
191185

192186
/**
@@ -195,6 +189,6 @@ class BottomSheetNavigator(
195189
@NavDestination.ClassType(Composable::class)
196190
class Destination(
197191
navigator: BottomSheetNavigator,
198-
internal val content: @Composable (NavBackStackEntry) -> Unit,
192+
internal val content: @Composable ColumnScope.(NavBackStackEntry) -> Unit,
199193
) : NavDestination(navigator), FloatingWindow
200194
}

Diff for: core/androidx/src/main/kotlin/br/com/mob1st/core/androidx/navigation/BottomSheetNavigatorDestinationBuilder.kt

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package br.com.mob1st.core.androidx.navigation
22

3+
import androidx.compose.foundation.layout.ColumnScope
34
import androidx.compose.runtime.Composable
45
import androidx.navigation.NavBackStackEntry
56
import androidx.navigation.NavDeepLink
@@ -21,12 +22,12 @@ import kotlin.reflect.KType
2122
@NavDestinationDsl
2223
class BottomSheetNavigatorDestinationBuilder : NavDestinationBuilder<BottomSheetNavigator.Destination> {
2324
private val bottomSheetNavigator: BottomSheetNavigator
24-
private val content: @Composable (NavBackStackEntry) -> Unit
25+
private val content: @Composable ColumnScope.(NavBackStackEntry) -> Unit
2526

2627
constructor(
2728
navigator: BottomSheetNavigator,
2829
route: String,
29-
content: @Composable (NavBackStackEntry) -> Unit,
30+
content: @Composable ColumnScope.(NavBackStackEntry) -> Unit,
3031
) : super(navigator, route) {
3132
this.bottomSheetNavigator = navigator
3233
this.content = content
@@ -36,7 +37,7 @@ class BottomSheetNavigatorDestinationBuilder : NavDestinationBuilder<BottomSheet
3637
navigator: BottomSheetNavigator,
3738
route: KClass<*>,
3839
typeMap: Map<KType, @JvmSuppressWildcards NavType<*>>,
39-
content: @Composable (NavBackStackEntry) -> Unit,
40+
content: @Composable ColumnScope.(NavBackStackEntry) -> Unit,
4041
) : super(navigator, route, typeMap) {
4142
this.bottomSheetNavigator = navigator
4243
this.content = content
@@ -56,7 +57,7 @@ class BottomSheetNavigatorDestinationBuilder : NavDestinationBuilder<BottomSheet
5657
inline fun <reified T : Any> NavGraphBuilder.bottomSheet(
5758
typeMap: Map<KType, @JvmSuppressWildcards NavType<*>> = emptyMap(),
5859
deepLinks: List<NavDeepLink> = emptyList(),
59-
noinline content: @Composable (backstackEntry: NavBackStackEntry) -> Unit,
60+
noinline content: @Composable ColumnScope.(backstackEntry: NavBackStackEntry) -> Unit,
6061
) {
6162
destination(
6263
BottomSheetNavigatorDestinationBuilder(

Diff for: core/androidx/src/main/kotlin/br/com/mob1st/core/androidx/navigation/ModalBottomSheetLayout.kt

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package br.com.mob1st.core.androidx.navigation
22

3-
import androidx.compose.foundation.layout.Box
43
import androidx.compose.foundation.layout.Column
5-
import androidx.compose.foundation.layout.fillMaxHeight
4+
import androidx.compose.foundation.layout.fillMaxSize
65
import androidx.compose.material3.ExperimentalMaterial3Api
76
import androidx.compose.material3.ModalBottomSheet
87
import androidx.compose.runtime.Composable
@@ -23,10 +22,13 @@ fun ModalBottomSheetLayout(
2322
ModalBottomSheet(
2423
onDismissRequest = bottomSheetNavigator.onDismissRequest,
2524
sheetState = bottomSheetNavigator.sheetState,
26-
) {
27-
Box(modifier = Modifier.fillMaxHeight(0.95f)) {
28-
bottomSheetNavigator.sheetContent()
29-
}
30-
}
25+
content = {
26+
Column(
27+
modifier = Modifier.fillMaxSize(0.95f),
28+
) {
29+
bottomSheetNavigator.sheetContent(this)
30+
}
31+
},
32+
)
3133
}
3234
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package br.com.mob1st.core.androidx.navigation
2+
3+
import androidx.compose.foundation.layout.ColumnScope
4+
import androidx.compose.material3.ExperimentalMaterial3Api
5+
import androidx.compose.material3.SheetState
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.runtime.LaunchedEffect
8+
import androidx.compose.runtime.getValue
9+
import androidx.compose.runtime.rememberUpdatedState
10+
import androidx.compose.runtime.saveable.SaveableStateHolder
11+
import androidx.compose.runtime.snapshotFlow
12+
import androidx.navigation.NavBackStackEntry
13+
import androidx.navigation.compose.LocalOwnersProvider
14+
import kotlinx.coroutines.flow.distinctUntilChanged
15+
import kotlinx.coroutines.flow.drop
16+
17+
@OptIn(ExperimentalMaterial3Api::class)
18+
@Composable
19+
internal fun ColumnScope.SheetContentHost(
20+
backStackEntry: NavBackStackEntry?,
21+
sheetState: SheetState,
22+
saveableStateHolder: SaveableStateHolder,
23+
onSheetShown: (entry: NavBackStackEntry) -> Unit,
24+
onSheetDismissed: (entry: NavBackStackEntry) -> Unit,
25+
) {
26+
if (backStackEntry != null) {
27+
val currentOnSheetShown by rememberUpdatedState(onSheetShown)
28+
val currentOnSheetDismissed by rememberUpdatedState(onSheetDismissed)
29+
LaunchedEffect(sheetState, backStackEntry) {
30+
snapshotFlow { sheetState.isVisible }
31+
// We are only interested in changes in the sheet's visibility
32+
.distinctUntilChanged()
33+
// distinctUntilChanged emits the initial value which we don't need
34+
.drop(1)
35+
.collect { visible ->
36+
if (visible) {
37+
currentOnSheetShown(backStackEntry)
38+
} else {
39+
currentOnSheetDismissed(backStackEntry)
40+
}
41+
}
42+
}
43+
backStackEntry.LocalOwnersProvider(saveableStateHolder) {
44+
val content =
45+
(backStackEntry.destination as BottomSheetNavigator.Destination).content
46+
content(backStackEntry)
47+
}
48+
}
49+
}

Diff for: features/finances/impl/src/main/kotlin/br/com/mob1st/features/finances/impl/ui/category/detail/CategoryViewModel.kt

+3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ internal class CategoryViewModel(
114114
fun submit() = launchIn(default + errorHandler) {
115115
uiState.value.ifIs<CategoryDetailUiState.Loaded> { state ->
116116
setCategory(state.merge())
117+
consumableDelegate.update {
118+
it.copy(isSubmitted = true)
119+
}
117120
}
118121
}
119122

Diff for: features/finances/impl/src/main/kotlin/br/com/mob1st/features/finances/impl/ui/category/navigation/CategoryCoordinator.kt

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package br.com.mob1st.features.finances.impl.ui.category.navigation
22

3+
import androidx.compose.material3.ExperimentalMaterial3Api
34
import br.com.mob1st.features.finances.impl.ui.category.detail.CategoryDetailArgs
45
import br.com.mob1st.twocents.core.navigation.Coordinator
56
import br.com.mob1st.twocents.core.navigation.NativeNavigationApi
@@ -10,6 +11,7 @@ import br.com.mob1st.twocents.core.navigation.NativeNavigationApi
1011
*/
1112
internal class CategoryCoordinator(
1213
navigationApi: NativeNavigationApi,
14+
// private val bottomSheetNavigator: BottomSheetNavigator,
1315
) : Coordinator<CategoryNavRoute>(navigationApi) {
1416
/**
1517
* Navigates to the category detail screen.
@@ -19,4 +21,10 @@ internal class CategoryCoordinator(
1921
val route = CategoryNavRoute.Detail(args = args)
2022
navigate(route)
2123
}
24+
25+
@OptIn(ExperimentalMaterial3Api::class)
26+
suspend fun dismiss() {
27+
// bottomSheetNavigator.sheetState.hide()
28+
back()
29+
}
2230
}

0 commit comments

Comments
 (0)