When using Jetpack Compose, there are some scenarios where there is a piece of code that does a specific job but since it is calling lambda parameters of the composable, it is not possible to fully extract an independent function from it.
In this situation, a method that comes to mind is having some nested functions (as provided in the following example) that each do a specific job.
@Composable
fun MyComposable(
onUpdate: () -> Unit,
) {
// ... other composable code ...
// Local function
fun nestedFunction() {
// logic
...
onUpdate()
}
LaunchedEffect(key = x) {
// Call the local function
nestedFunction()
}
// ... other composable code ...
}
I was wondering if this solution is an antipattern or not. Or maybe are there any other solutions?
It's not an anti-pattern it's implemented in Slider code as almost as in your question. Nested function is called from callback but you can do it as in your question as well.
Some code omitted it's like this in Slider
@Composable
fun Slider(
value: Float,
onValueChange: (Float) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
/*@IntRange(from = 0)*/
steps: Int = 0,
onValueChangeFinished: (() -> Unit)? = null,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
colors: SliderColors = SliderDefaults.colors()
) {
val onValueChangeState = rememberUpdatedState(onValueChange)
BoxWithConstraints(
modifier
.minimumInteractiveComponentSize()
.requiredSizeIn(minWidth = ThumbRadius * 2, minHeight = ThumbRadius * 2)
.sliderSemantics(
value,
enabled,
onValueChange,
onValueChangeFinished,
valueRange,
steps
)
.focusable(enabled, interactionSource)
) {
fun scaleToUserValue(offset: Float) =
scale(minPx, maxPx, offset, valueRange.start, valueRange.endInclusive)
fun scaleToOffset(userValue: Float) =
scale(valueRange.start, valueRange.endInclusive, userValue, minPx, maxPx)
val scope = rememberCoroutineScope()
val rawOffset = remember { mutableFloatStateOf(scaleToOffset(value)) }
val pressOffset = remember { mutableFloatStateOf(0f) }
val draggableState = remember(minPx, maxPx, valueRange) {
SliderDraggableState {
rawOffset.floatValue = (rawOffset.floatValue + it + pressOffset.floatValue)
pressOffset.floatValue = 0f
val offsetInTrack = rawOffset.floatValue.coerceIn(minPx, maxPx)
onValueChangeState.value.invoke(scaleToUserValue(offsetInTrack))
}
}
}
}