Im seeing some strange behavior with a Composition...
# compose
z
Im seeing some strange behavior with a CompositionLocal: if my top level composable function accepts a value of X, and defaults to LocalX.current; the value is null, even though it can never be null. If I specify LocalX.current inside the composable I get the proper value. Bug in compose?
Copy code
val LocalStep = compositionLocalOf<Step> {
    throw NullPointerException()
}

@Composable
fun <T> Property(
    modifier: Modifier = Modifier,
    step: Step = LocalStep.current, //crashes
    input: Input<T>,
    onUpdate: (Input<T>) -> Unit,
    label: String? = null,
    style: InputStyle? = null,
    fill: Boolean = false
) {
    val step = LocalStep.current //works
}
a
Strange. I use parameters which default to composition local values all the time. So I'm sure it works. I have, however, never issued
throw NullPOinterException()
as a value of the composition local, so perhaps that's the difference.
z
Same, it works elsewhere for me too. If I specify a different default Step value (instead of throwing); it still crashes.
It reminds me of not being able to specify default parameters for composable functions inside interfaces, although Im not sure if theyre related.
c
You also can't use default parameters inside expect/actual functions
z
This is just a regular function, albeit in a different module than the one its used in, but other functions (even in the same file) work with default parameters.
t
@Zoltan Demant I’ve tried accessing
compositionLocal
from parent scope (using default argument) as well as composable scope. It works for me.
Copy code
data class User(val name: String)

val LocalUser = compositionLocalOf<User> { throw NullPointerException() }

@Preview
@Composable
fun A() {
    CompositionLocalProvider(LocalUser provides User(name = "Adam")) {
        Column {
            B()
            C()
        }
    }
}

@Composable
fun B(
    user: User = LocalUser.current
) {
    Text(text = "B says user is ${user.name}")
}

@Composable
fun C(){
    val user = LocalUser.current
    Text(text = "C says user is ${user.name}")
}
Can you share a reproducible code like the above?
z
Thanks, my value class was the issue - if you replace data class with value class in your example, it crashes just the same.
Copy code
@JvmInline
value class User(val name: String)
👍 1
z
@Zoltan Demant It’s not clear from your original message – is your
Property
function being called from somewhere that
LocalStep
is actually provided? If so, what’s the exception thrown in the default parameter case?
t
@Zoltan Demant I see. I haven’t tried
value
class with
compositionLocal
before. I am curious why it didn’t work with the inline class 🤔
z
@Zach Klippenstein (he/him) [MOD]: Yes, its provided and just to verify, I can access it using LocalStep.current before/inside/after the composable function completely fine. I get
Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter step
and everything works if I just make it a data class instead of inline. Im using inline classes in other cases, the issue seems to only come up when using them as default values with compositionLocal.current.
z
Could you file a bug?
z
Id love to, but today it works. Im on the exact same commit as I was yesterday. I left it as a data class, since that worked last night; and when writing the bug report this morning, I changed it back to a value class just to verify that it was still happening - and now it works. Could it be an issue with the build? What happened yesterday was after a full day of coding. Let me know if theres any more info I can provide!
😐 1
z
Sounds like it could be a caching or daemon issue, yea
That is probably a bug in itself but prolly much harder to repro
🥲 1