Skip to content

Commit b0a0026

Browse files
committed
feat: add global scrim controller
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 31189b2 commit b0a0026

3 files changed

Lines changed: 117 additions & 1 deletion

File tree

apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/App.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ import com.getcode.ui.components.OnLifecycleEvent
6565
import com.getcode.ui.components.bars.rememberBarManager
6666
import com.getcode.ui.core.RestrictionType
6767
import com.flipcash.app.core.extensions.navigateTo
68+
import com.getcode.navigation.scrim.LocalScrimController
69+
import com.getcode.navigation.scrim.ScrimController
70+
import com.getcode.navigation.scrim.ScrimOverlay
6871
import dev.bmcreations.tipkit.TipScaffold
6972
import dev.bmcreations.tipkit.engines.TipsEngine
7073
import dev.theolm.rinku.DeepLink
@@ -136,10 +139,13 @@ internal fun App(
136139
Modifier.semantics { testTagsAsResourceId = true }
137140
} else Modifier
138141

142+
val scrimController = remember { ScrimController() }
143+
139144
Box(modifier = semanticsModifier) {
140145
CompositionLocalProvider(
141146
LocalCodeNavigator provides codeNavigator,
142147
LocalBiometricsState provides biometricsState,
148+
LocalScrimController provides scrimController,
143149
) {
144150
ExternalWalletOnRampHandler(
145151
state = externalWalletOnRamp,
@@ -206,6 +212,8 @@ internal fun App(
206212
deepLink = { deepLink },
207213
),
208214
)
215+
216+
ScrimOverlay(scrimController)
209217
}
210218

211219
LaunchedEffect(deepLink) {

ui/navigation/src/main/kotlin/com/getcode/navigation/scenes/ModalBottomSheetSceneStrategy.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import com.getcode.navigation.results.NavResultOrCanceled
4141
import com.getcode.navigation.results.NavResultStore
4242
import com.getcode.navigation.results.NavigationRetVal
4343
import com.getcode.navigation.scenes.ModalBottomSheetSceneStrategy.Companion.modalBottomSheet
44+
import com.getcode.navigation.scrim.LocalScrimController
45+
import com.getcode.navigation.scrim.ScrimOverlay
4446
import com.getcode.theme.CodeTheme
4547
import com.getcode.ui.utils.LocalSheetGesturesState
4648
import kotlinx.coroutines.launch
@@ -140,17 +142,20 @@ internal class ModalBottomSheetScene<T : Any> @OptIn(ExperimentalMaterial3Api::c
140142
}
141143
}
142144

145+
val scrim = LocalScrimController.current
146+
143147
CompositionLocalProvider(
144148
LocalSheetGesturesState provides { enabled ->
145149
allowDismiss = enabled && !navigator.sheetDragDisabled
146150
},
151+
LocalScrimController provides scrim,
147152
) {
148153
// Remove inset padding. Default adds nav bar padding.
149154
// Remove grab bar for bleed to top edge of sheet
150155
ModalBottomSheet(
151156
sheetState = sheetState,
152157
onDismissRequest = { if (!isNonDismissable) dismiss(false) },
153-
sheetGesturesEnabled = !navigator.sheetDragDisabled,
158+
sheetGesturesEnabled = !navigator.sheetDragDisabled && !scrim.visible,
154159
scrimColor = CodeTheme.colors.scrim,
155160
properties = if (isNonDismissable) {
156161
ModalBottomSheetProperties(
@@ -184,6 +189,7 @@ internal class ModalBottomSheetScene<T : Any> @OptIn(ExperimentalMaterial3Api::c
184189
) {
185190
entry.Content()
186191
}
192+
ScrimOverlay(scrim)
187193
}
188194
}
189195
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.getcode.navigation.scrim
2+
3+
import androidx.compose.animation.AnimatedContent
4+
import androidx.compose.animation.AnimatedVisibility
5+
import androidx.compose.animation.EnterTransition
6+
import androidx.compose.animation.ExitTransition
7+
import androidx.compose.animation.fadeIn
8+
import androidx.compose.animation.fadeOut
9+
import androidx.compose.animation.togetherWith
10+
import androidx.compose.foundation.background
11+
import androidx.compose.foundation.layout.Box
12+
import androidx.compose.foundation.layout.Spacer
13+
import androidx.compose.foundation.layout.fillMaxSize
14+
import androidx.compose.foundation.layout.fillMaxWidth
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.getValue
17+
import androidx.compose.runtime.mutableStateOf
18+
import androidx.compose.runtime.setValue
19+
import androidx.compose.runtime.staticCompositionLocalOf
20+
import androidx.compose.ui.Alignment
21+
import androidx.compose.ui.Modifier
22+
import com.getcode.theme.CodeTheme
23+
import com.getcode.ui.core.noRippleClickable
24+
25+
class ScrimController {
26+
var visible by mutableStateOf(false)
27+
private set
28+
29+
private var onDismiss: (() -> Unit)? = null
30+
var overlayAlignment: Alignment by mutableStateOf(Alignment.Center)
31+
private set
32+
var overlayContent: (@Composable () -> Unit)? by mutableStateOf(null)
33+
private set
34+
var enterTransition: EnterTransition by mutableStateOf(fadeIn())
35+
private set
36+
var exitTransition: ExitTransition by mutableStateOf(fadeOut())
37+
private set
38+
39+
40+
fun show(
41+
content: (@Composable () -> Unit)? = null,
42+
enterTransition: EnterTransition = fadeIn(),
43+
exitTransition: ExitTransition = fadeOut(),
44+
alignment: Alignment = Alignment.Center,
45+
onDismiss: () -> Unit,
46+
) {
47+
this.onDismiss = onDismiss
48+
this.overlayContent = content
49+
this.overlayAlignment = alignment
50+
this.enterTransition = enterTransition
51+
this.exitTransition = exitTransition
52+
visible = true
53+
}
54+
55+
private fun hide() {
56+
visible = false
57+
onDismiss = null
58+
overlayContent = null
59+
60+
}
61+
62+
fun dismiss() {
63+
onDismiss?.invoke()
64+
hide()
65+
}
66+
}
67+
68+
val LocalScrimController = staticCompositionLocalOf<ScrimController> {
69+
error("No ScrimController provided")
70+
}
71+
72+
@Composable
73+
fun ScrimOverlay(controller: ScrimController) {
74+
Box(modifier = Modifier.fillMaxSize()) {
75+
AnimatedVisibility(
76+
visible = controller.visible,
77+
enter = fadeIn(),
78+
exit = fadeOut(),
79+
) {
80+
Box(
81+
modifier = Modifier
82+
.fillMaxSize()
83+
.background(CodeTheme.colors.scrim)
84+
.noRippleClickable { controller.dismiss() },
85+
)
86+
}
87+
88+
AnimatedContent(
89+
modifier = Modifier.align(controller.overlayAlignment),
90+
targetState = controller.overlayContent,
91+
transitionSpec = {
92+
controller.enterTransition togetherWith controller.exitTransition
93+
}
94+
) { content ->
95+
if (content != null) {
96+
content()
97+
} else {
98+
Spacer(Modifier.fillMaxWidth())
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)