Home > other >  Why we cannot use delegate property in mutableStateListOf in jetpack compose
Why we cannot use delegate property in mutableStateListOf in jetpack compose

Time:12-07

I want to know that why we cannot use delegate property in mutableStateListOf. When I am trying to add

val favourites by remember { mutableStateListOf<Int>() }

It gives me error

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

Also I tried to imports

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

but it stills gives the same error. Is there any specific reason for this? Thanks

CodePudding user response:

What you should be asking is how do i use mutableStateOf with delegation because delagation is not part of any class by default.

MutableState delegation is implemented as

/**
 * 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
}

Functions you import with

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

are getValue and setValue functions in snippet above.

You can create delegation functions for classes as extension functions which require you to import them or as part of class as in examples below.

class CalculateDelegate {

    private var calculatedProperty = 0

    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        return calculatedProperty
    }

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

}

/**
 * Delegate [CalculateDelegate] to this function
 */
fun delegateCalculationFunction() = CalculateDelegate()

If you create a instances using

var delegatedNum1 by CalculateDelegate()
var delegatedNum2 by delegateCalculationFunction()

And then if you change or call setValue as

delegatedNum1 = 4
delegatedNum2 = 3

if you print these delegateNum1, and delegateNum2 you will see that they are 8 and 6 because of overriding setValue

If you wish to force overriding setValue and getValue functions for a class you can use ReadWriteProperty as

public interface ReadWriteProperty<in T, V> : ReadOnlyProperty<T, V> {
    /**
     * Returns the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @return the property value.
     */
    public override operator fun getValue(thisRef: T, property: KProperty<*>): V

    /**
     * Sets the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @param value the value to set.
     */
    public operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
}

Then you need to override getValue and setValue functions.

class CalculateDelegate: ReadWriteProperty<Any?, Int> {

    private var calculatedProperty = 0

    override operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        return calculatedProperty
    }

    override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
        calculatedProperty = value * 2
    }
}
  • Related