91

I'm referring to the example in https://developer.android.com/jetpack/compose/state. When I code

var expanded by remember { mutableStateOf(false) }

It errors stating

Type 'TypeVariable(T)' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate

The below works though

val expanded = remember { mutableStateOf(false) }

// OR

val (expanded, setExpanded) = remember { mutableStateOf(false) }
Elye
  • 53,639
  • 54
  • 212
  • 474

2 Answers2

211

Apparently, I have to include these imports

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

The auto imports don't automatically recommend it in the beta Android Studio 4.2

If you use livedata, then consider the below import

import androidx.compose.runtime.livedata.observeAsState
Elye
  • 53,639
  • 54
  • 212
  • 474
  • Similar one here, but slightly different. https://stackoverflow.com/questions/63875411/type-statelistuser-has-no-method-getvaluenothing-kproperty-and-t – Elye Nov 22 '20 at 07:53
5

It produces error because delegation functions of MutableState are extension functions because of that they need to be imported.

/**
 * Permits property delegation of `val`s using `by` for [State].
 *
 * @sample androidx.compose.runtime.samples.DelegatedReadOnlyStateSample
 */
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value

/**
 * Permits property delegation of `var`s using `by` for [MutableState].
 *
 * @sample androidx.compose.runtime.samples.DelegatedStateSample
 */
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> MutableState<T>.setValue(thisObj: Any?, property: KProperty<*>, value: T) {
    this.value = value
}

/**
 * A mutable value holder where reads to the [value] property during the execution of a [Composable]
 * function, the current [RecomposeScope] will be subscribed to changes of that value. When the
 * [value] property is written to and changed, a recomposition of any subscribed [RecomposeScope]s
 * will be scheduled. If [value] is written to with the same value, no recompositions will be
 * scheduled.
 *
 * @see [State]
 * @see [mutableStateOf]
 */
@Stable
interface MutableState<T> : State<T> {
    override var value: T
    operator fun component1(): T
    operator fun component2(): (T) -> Unit
}

With

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

getValue and setValue functions are imported.

With Kotlin delegation you can either create delegation functions as part of the class

/**
 * Delegate [MyState] to this function
 */
fun myStateOf() = MyState()

class MyState internal constructor() {
    var value = 0

    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = value

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
        this.value = value
    }
}

then use them without the need of importing since they are part of the class

var myState by myStateOf()
myState = 5

or define them as extension functions which is the case with State and MutableState interfaces.

/**
 * Delegate [MyState] to this function
 */
fun myStateOf() = MyState()

class MyState internal constructor() {
    var value = 0
    
}

@Suppress("NOTHING_TO_INLINE")
inline operator fun MyState.getValue(thisRef: Any?, property: KProperty<*>): Int = value


@Suppress("NOTHING_TO_INLINE")
inline operator fun MyState.setValue(thisObj: Any?, property: KProperty<*>, value: Int) {
    this.value = value
}

When you try to call

var myState by myStateOf()
myState = 5

you get error ask you to import getValue and setValue functions.

enter image description here

Thracian
  • 43,021
  • 16
  • 133
  • 222