Skip to content

Instantly share code, notes, and snippets.

@bmc08gt
Last active December 6, 2023 17:45
Show Gist options
  • Save bmc08gt/8668f228de4b8d4055993b0c73f72374 to your computer and use it in GitHub Desktop.
Save bmc08gt/8668f228de4b8d4055993b0c73f72374 to your computer and use it in GitHub Desktop.
FKScaffold with accessory view triggers
FKScaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
FKLargeTopAppBar(title = name)
},
) { padding, triggerAccessoryView ->
ItemForm(
modifier = Modifier.padding(top = padding.calculateTopPadding()),
state = state,
dispatch = viewModel::dispatchEvent
)
LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<ItemViewModel.Event.ChooseExpiration>()
.map { state.builder.expiration }
.onEach { selectedDate ->
triggerAccessoryView(AccessoryViewTrigger.DatePicker(
selectedDate = selectedDate ?: Clock.System.now().plus(3.days),
minDate = Clock.System.now(),
onResult = {
viewModel.dispatchEvent(ItemViewModel.Event.OnExpirationSet(it))
}
))
}.launchIn(this)
}
}
val LocalAccessoryViewHeight: ProvidableCompositionLocal<Dp> =
staticCompositionLocalOf { error("AccessoryViewHeight not initialized") }
val LocalAccessoryVisible: ProvidableCompositionLocal<Boolean> =
staticCompositionLocalOf { error("AccessoryVisible not initialized") }
enum class AccessoryViewType {
DatePicker
}
sealed interface AccessoryViewTrigger<T> {
val type: AccessoryViewType
val onResult: (T) -> Unit
data class DatePicker(
val selectedDate: Instant,
val minDate: Instant?,
override val onResult: (Instant) -> Unit
) : AccessoryViewTrigger<Instant> {
override val type: AccessoryViewType = AccessoryViewType.DatePicker
}
}
@Composable
fun FKScaffold(
modifier: Modifier = Modifier,
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
content: @Composable BoxScope.(PaddingValues, (AccessoryViewTrigger<*>) -> Unit) -> Unit
) {
var accessoryView by remember {
mutableStateOf<AccessoryViewTrigger<*>?>(null)
}
val accessoryHeights = remember {
mutableStateMapOf(AccessoryViewType.DatePicker to 0.dp)
}
val animatedHeight by animateDpAsState(
if (accessoryView != null) accessoryHeights[accessoryView!!.type]!! else 0.dp
)
val scrim by animateColorAsState(
if (accessoryView != null) MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f) else Color.Transparent
)
@Composable
fun bottomBar(bottomBar: @Composable () -> Unit, trigger: AccessoryViewTrigger<*>?) {
val dateTrigger = trigger as? AccessoryViewTrigger.DatePicker
DatePicker(
modifier = Modifier.fillMaxWidth()
.measured {
val size = accessoryHeights[AccessoryViewType.DatePicker] ?: 0.dp
if (size == 0.dp) {
accessoryHeights[AccessoryViewType.DatePicker] = it.height
}
},
value = dateTrigger?.selectedDate ?: Clock.System.now(),
minValue = dateTrigger?.minDate,
mode = DatePickerMode.Date,
isVisible = trigger is AccessoryViewTrigger.DatePicker,
onValueChanged = {
dateTrigger?.onResult?.invoke(it)
accessoryView = null
}
)
if (trigger == null) {
bottomBar()
}
}
Scaffold(
modifier = modifier,
topBar = {
Box {
topBar()
if (accessoryView != null) {
Scrim(modifier = Modifier.matchParentSize(), color = scrim)
}
}
},
bottomBar = { bottomBar(bottomBar, accessoryView) },
snackbarHost = snackbarHost,
floatingActionButton = floatingActionButton,
floatingActionButtonPosition = floatingActionButtonPosition,
containerColor = containerColor,
contentColor = contentColor,
contentWindowInsets = contentWindowInsets,
content = { padding ->
CompositionLocalProvider(
LocalAccessoryViewHeight provides animatedHeight,
LocalAccessoryVisible provides (accessoryView != null),
) {
Box(Modifier.fillMaxSize()) {
content(padding) { accView ->
accessoryView = accView
}
if (accessoryView != null) {
Scrim(modifier = Modifier.matchParentSize(), color = scrim)
}
}
}
}
)
}
@Composable
private fun Scrim(modifier: Modifier, color: Color) {
Box(
modifier = modifier
.addIf(Platform.showScrimWithDatePicker) {
Modifier.background(color)
}.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { /* swallow clicks */ }
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment