State<List<User>?> no es un delegado: top 3 errores y soluciones en Kotlin

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Pánico silencioso: cuando Kotlin grita que State no puede ser un delegado

\n\n

Estás escribiendo código en Kotlin, usando Jetpack Compose, todo va según lo planeado. Y de repente — ¡pum! — el compilador muestra un mensaje que hace que a un novato se le hiele la sangre: Type 'State<List<User>?>' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate. Suena como una sentencia, pero en realidad es solo un grito de ayuda de tu código. Analicemos qué quiere decir y cómo hacer las paces con él.

\n\n

Este error es uno de los más populares en Stack Overflow (¡miles de visitas!), y persigue a los desarrolladores que recién comienzan a dominar las propiedades delegadas en Kotlin. En este artículo no solo traduciremos el mensaje de error, sino que mostraremos tres situaciones reales donde ocurre y daremos soluciones funcionales. Aprenderás cómo usar correctamente StateFlow en Compose, cómo no confundirte con los delegados de Kotlin y por qué LiveData a veces se comporta de manera inesperada.

\n\n

¿Qué es un delegado en Kotlin y por qué State no se convierte en uno?

\n\n

Para empezar, una mini-guía rápida. Las propiedades delegadas en Kotlin (palabra clave by) permiten trasladar la lógica del getter y setter a un objeto separado. Por ejemplo:

\n\n
var name: String by Delegates.observable("") { prop, old, new ->\n    println("$old → $new")\n}\n
\n\n

Para que un objeto pueda ser un delegado, debe implementar los métodos getValue() y, posiblemente, setValue() con una firma específica. El error Type 'State<List<User>?>' has no method 'getValue(Nothing?, KProperty<*>)' dice exactamente eso: tu objeto de tipo State<List<User>?> no tiene un método getValue adecuado. El compilador no puede usarlo como delegado.

\n\n

Ahora pasemos a los tres escenarios concretos donde este error aparece con más frecuencia.

\n\n

Error 1: Confusión con by y asignación de StateFlow

\n\n

El caso más común es cuando el desarrollador intenta usar by para asignar un StateFlow en un ViewModel, pero lo hace incorrectamente. Observa este código:

\n\n
// ❌ ¡Error! No se puede hacer así\nclass MyViewModel : ViewModel() {\n    private val _users = MutableStateFlow<List<User>?>(null)\n    val users: StateFlow<List<User>?> by _users  // ¡Error!\n}\n
\n\n

Aquí, _users es un MutableStateFlow, y estamos intentando convertirlo en delegado para la propiedad users. Pero StateFlow no implementa getValue para propiedades de Kotlin. No es un delegado, es un flujo reactivo.

\n\n

Solución: usa asStateFlow()

\n\n

La forma correcta no es delegar, sino transformar:

\n\n
// ✅ Correcto\nclass MyViewModel : ViewModel() {\n    private val _users = MutableStateFlow<List<User>?>(null)\n    val users: StateFlow<List<User>?> = _users.asStateFlow()\n}\n
\n\n

El método asStateFlow() crea una copia inmutable que es segura de exponer al exterior. Sin delegados, sin errores.

\n\n

Error 2: Uso de by con LiveData en Compose

\n\n

La segunda situación más popular es intentar usar LiveData directamente con by en Jetpack Compose. Los desarrolladores que vienen del mundo de los sistemas View están acostumbrados a LiveData e intentan aplicar el mismo enfoque:

\n\n
// ❌ ¡Error!\n@Composable\nfun UserList(viewModel: MyViewModel) {\n    val users: List<User>? by viewModel.users  // ¡Error!\n    // ...\n}\n
\n\n

Aquí, viewModel.users tiene el tipo LiveData<List<User>?>. Y LiveData no es un delegado para propiedades de Compose. El compilador vuelve a mostrar el mismo error: Type 'LiveData<List<User>?>' has

Blogs

Book Recommendations