Pânico silencioso: quando Kotlin grita que State não pode ser um delegado
\n\nVocê está escrevendo código em Kotlin, usando Jetpack Compose, tudo correndo bem. E de repente — bum! — o compilador exibe uma mensagem que faz o iniciante gelar por dentro: Type 'State<List<User>?>' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegate. Parece uma sentença, mas na verdade é apenas um pedido de ajuda do seu código. Vamos entender o que ele quer dizer e como se reconciliar com ele.
Este erro é um dos mais populares no Stack Overflow (milhares de visualizações!) e persegue desenvolvedores que estão começando a aprender propriedades delegadas em Kotlin. Neste artigo, não vamos apenas traduzir a mensagem de erro, mas mostrar três situações reais em que ela ocorre e dar soluções funcionais. Você aprenderá como usar corretamente StateFlow no Compose, como não se confundir com delegados Kotlin e por que LiveData às vezes se comporta de forma inesperada.
\n\nO que é um delegado em Kotlin e por que State não se torna um?
\n\nPara começar — uma rápida introdução. Propriedades delegadas em Kotlin (palavra-chave by) permitem transferir a lógica do getter e setter para um objeto separado. Por exemplo:
var name: String by Delegates.observable("") { prop, old, new ->\n println("$old → $new")\n}\n\n\nPara que um objeto possa ser um delegado, ele deve implementar os métodos getValue() e, possivelmente, setValue() com uma assinatura específica. O erro Type 'State<List<User>?>' has no method 'getValue(Nothing?, KProperty<*>)' diz exatamente isso: seu objeto do tipo State<List<User>?> não possui um método getValue adequado. O compilador não pode usá-lo como delegado.
Agora vamos para os três cenários específicos onde esse erro aparece com mais frequência.
\n\nErro 1: Confusão com by e atribuição de StateFlow
\n\nO caso mais comum é quando o desenvolvedor tenta usar by para atribuir StateFlow no ViewModel, mas faz isso incorretamente. Veja este código:
// ❌ Erro! Não se faz assim\nclass MyViewModel : ViewModel() {\n private val _users = MutableStateFlow<List<User>?>(null)\n val users: StateFlow<List<User>?> by _users // Erro!\n}\n\n\nAqui, _users é um MutableStateFlow, e estamos tentando torná-lo um delegado para a propriedade users. Mas StateFlow não implementa getValue para propriedades Kotlin. Não é um delegado, é um fluxo reativo.
Solução: use asStateFlow()
\n\nA maneira correta não é delegar, mas transformar:
\n\n// ✅ Correto\nclass MyViewModel : ViewModel() {\n private val _users = MutableStateFlow<List<User>?>(null)\n val users: StateFlow<List<User>?> = _users.asStateFlow()\n}\n\n\nO método asStateFlow() cria uma cópia imutável que pode ser exposta com segurança. Sem delegados, sem erros.
Erro 2: Uso de by com LiveData no Compose
\n\nA segunda situação mais popular é a tentativa de usar LiveData diretamente com by no Jetpack Compose. Desenvolvedores que vêm do mundo dos sistemas View estão acostumados com LiveData e tentam aplicar a mesma abordagem:
// ❌ Erro!\n@Composable\nfun UserList(viewModel: MyViewModel) {\n val users: List<User>? by viewModel.users // Erro!\n // ...\n}\n\n\nAqui, viewModel.users tem o tipo LiveData<List<User>?>. E LiveData não é um delegado para propriedades do Compose. O compilador novamente exibe o mesmo erro: Type 'LiveData<List<User>?>' has