galex
07/17/2023, 3:49 AMinit
?
I would like to build a state object that triggers a callback only when the keyboard is closed (opening a bottom sheet in this case)
I've used this useful piece of code to know when the keyboard is closed by mutable state and came up with the following (in thread)galex
07/17/2023, 3:50 AM@Composable
fun rememberOnKeyboardClosedState(
whenClosed: () -> Unit,
): OnKeyboardClosedState {
val keyboardController = LocalSoftwareKeyboardController.current
val keyboardStatus by keyboardStatusAsState()
val runOpen = remember { mutableStateOf(false) }
return remember(keyboardStatus, whenClosed, runOpen) {
OnKeyboardClosedState(
keyboardController = keyboardController,
keyboardStatus = keyboardStatus,
runOpen = runOpen,
whenClosed = whenClosed
)
}
}
/**
* Runs the callback of whenClosed only when the keyboard is actually closed
*/
@OptIn(ExperimentalComposeUiApi::class)
@Stable
data class OnKeyboardClosedState internal constructor(
private val keyboardController: SoftwareKeyboardController?,
private val keyboardStatus: KeyboardStatus,
private val runOpen: MutableState<Boolean>,
private val whenClosed: () -> Unit,
) {
private val rememberScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
init {
if (runOpen.value) {
rememberScope.launch {
delay(200) // not necessary but makes it fancier
open()
}
runOpen.value = false
}
}
fun open() {
if (keyboardStatus == KeyboardStatus.Closed) {
rememberScope.launch {
whenClosed()
}
} else {
runOpen.value = true
keyboardController?.hide()
}
}
}
galex
07/17/2023, 3:51 AMval onKeyboardClosedState = rememberOnKeyboardClosedState {
handleEvent(ChatUiEvent.OnMoreOptionsClicked)
}
// in some onClick function
onKeyboardClosedState.open()
galex
07/17/2023, 3:51 AMStylianos Gakis
07/17/2023, 7:05 AMval keyboardStatus by keyboardStatusAsState()
LaunchedEffect(Unit) {
snapshotFlow { keyboardStatus }
.drop(1)
.filter { it == true }
.collect {
Do something
}
}
Stylianos Gakis
07/17/2023, 7:06 AMgalex
07/18/2023, 6:27 AMinit
:
@Composable
fun rememberOnKeyboardClosedState(
whenClosed: () -> Unit,
): OnKeyboardClosedState {
val keyboardController = LocalSoftwareKeyboardController.current
val keyboardStatus by keyboardStatusAsState()
val shouldRunOpen = remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
snapshotFlow { keyboardStatus }
.drop(1)
.filter { it == KeyboardStatus.Closed && shouldRunOpen.value }
.collect {
delay(200) // not necessary per say but makes the UI change a little calmer
whenClosed()
shouldRunOpen.value = false
}
}
return remember(keyboardStatus) {
OnKeyboardClosedState(
keyboardController = keyboardController,
keyboardStatus = keyboardStatus,
shouldRunOpen = shouldRunOpen,
whenClosed = whenClosed
)
}
}
/**
* Runs the callback of whenClosed only when the keyboard is actually closed
*/
@OptIn(ExperimentalComposeUiApi::class)
@Stable
data class OnKeyboardClosedState internal constructor(
private val keyboardController: SoftwareKeyboardController?,
private val keyboardStatus: KeyboardStatus,
private val shouldRunOpen: MutableState<Boolean>,
private val whenClosed: () -> Unit,
) {
/**
* Triggers the [whenClosed] callback the keyboard is closed or trigger closing it and
* setting a mutable state to run [whenClosed] when the keyboard will be closed
*/
fun open() {
if (keyboardStatus == KeyboardStatus.Closed) {
whenClosed()
} else {
// To indeed call whenClosed after the keyboard is closed
shouldRunOpen.value = true
// triggers changes of keyboardStatusState
keyboardController?.hide()
}
}
}
I still need to pass a mutable state to the remembered object, to enable the callback when I really intend to open a bottom sheet, but overall it looks indeed better!
I'm open for more improvements if you see any 😇Stylianos Gakis
07/18/2023, 7:09 AMopen()
on it while it's still closed? And it doesn't even trigger the callback in that situationStylianos Gakis
07/18/2023, 7:10 AMgalex
07/18/2023, 7:17 AMStylianos Gakis
07/18/2023, 7:20 AMopen()
means open sheet, not open keyboard reallygalex
07/18/2023, 7:24 AM