Introdução: "Fiz tudo conforme o tutorial, mas o aplicativo trava!"
\nSe você está começando no desenvolvimento Android com Kotlin, provavelmente já passou por isso. Você escreve o código seguindo um tutorial, executa o aplicativo… e crash. Nos logs — o famoso java.lang.NullPointerException (ou seu equivalente em Kotlin). E a mensagem: "Cannot read field… because the view is null".
Essa é uma dor real que gerou milhares de visualizações no Stack Overflow. Hoje vamos analisá-la de uma vez por todas. Por que ocorre o NullPointerException ao acessar View no Fragment em Kotlin? E, principalmente, como corrigir isso para você dormir tranquilo.
\nVamos lá!
\n\nPor que o erro ocorre? A raiz do problema — Lifecycle
\nA principal razão pela qual iniciantes recebem NullPointerException no Fragment Kotlin é a falta de compreensão do ciclo de vida do fragment. Na Activity é simples: onCreate — e pronto. No Fragment é mais complicado.
O fragment passa por várias etapas:
\n- \n
onCreateView()— aqui o layout é inflado (criado). \n onViewCreated()— aqui a View já existe com certeza. \n onDestroyView()— a View é destruída (por exemplo, ao girar a tela). \n
O erro mais comum é tentar acessar a View antes de o fragment criar seu layout. Por exemplo, no construtor ou no onCreate() do fragment. Nesse momento, view é igual a null — daí o crash.
A segunda causa é o vazamento de referências. Você salvou uma referência à View em uma variável, depois o fragment foi recriado, e a referência antiga leva a lugar nenhum.
\n\nExemplo típico: como o problema aparece no código
\nImagine que você tem um fragment simples com um campo de texto:
\nclass MyFragment : Fragment() {\n\n private lateinit var myTextView: TextView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n // ERRO! Aqui a View ainda não existe\n myTextView = requireView().findViewById(R.id.my_text_view)\n myTextView.text = "Olá!"\n }\n\n override fun onCreateView(\n inflater: LayoutInflater,\n container: ViewGroup?,\n savedInstanceState: Bundle?\n ): View? {\n return inflater.inflate(R.layout.fragment_my, container, false)\n }\n}\nEsse código vai falhar com NullPointerException, porque requireView() no onCreate() retorna null. A View ainda não foi criada!
3 formas funcionais de corrigir o erro
\nVamos ver três abordagens que resolvem garantidamente o problema de acesso à View no Fragment.
\n\nForma 1: Mover o código para onViewCreated()
\nA maneira mais simples e correta é esperar o momento em que a View existe com certeza. O método onViewCreated() é chamado imediatamente após a criação do layout.
class MyFragment : Fragment() {\n\n private lateinit var myTextView: TextView\n\n override fun onCreateView(\n inflater: LayoutInflater,\n container: ViewGroup?,\n savedInstanceState: Bundle?\n ): View? {\n return inflater.inflate(R.layout.fragment_my, container, false)\n }\n\n override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n super.onViewCreated(view, savedInstanceState)\n // Agora a view definitivamente não é null!\n myTextView = view.findViewById(R.id.my_text_view)\n myTextView.text = "Tudo funcionando!"\n }\n}\nVantagens: simples, claro, funciona sempre.
\nDesvantagens: é preciso lembrar do lateinit var e não esquecer de anular as referências em onDestroyView() (caso contrário, pode haver vazamento de memória).