☁️ Cap 13: Conectando ao Mundo (PokeAPI)

🎯 Objetivo da Aula: Ao final desta aula, você entenderá como os aplicativos “conversam” com a internet. Você aprenderá a criar um “Contrato” (Modelagem) e usar um “Dublê” (Mock) para simular dados de Pokémons sem precisar de conexão real no início.


🏢 O Cenário Prático (Seu Desafio): Você está criando a Pokedex Definitiva. Mas os dados dos Pokémons não estão no seu celular, eles estão em um servidor na nuvem (a PokeAPI). Seu desafio é aprender a pedir esses dados e entender como eles chegam até o seu código.


🧠 Fundamentos: A Teoria Traduzida

📖 Dicionário do Programador

🎨 Padrão de Modelagem

No Android, os dados que chegam da internet moram na pasta data/model/. O nome da classe deve ser o que o dado representa. Ex: PokemonResponse.kt.

graph LR
    A["App Pokedex"] -->|Pedido: ID 25| B{Garçom: API}
    B -->|Bandeja: JSON| A

🏗️ Construindo o Projeto (Checklist Studio)

Para este novo módulo do seu projeto AppPokedexMestre_SeuNome (o mesmo desde o Cap 07), organize suas pastas:

  1. Pasta: br.com.curso.pokedex.data.model
  2. Pasta: br.com.curso.pokedex.data.network (Para as interfaces da API)

📦 Dependência Gradle

Para criar interfaces como GameService (com @GET e suspend fun), adicione no build.gradle (Module: app), dentro de dependencies { }:

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

💡 A palavra suspend indica uma função que pode “pausar” sem travar o app enquanto espera a resposta da internet. Vamos explorar isso com calma nos próximos capítulos — por enquanto, copie como está.


📖 Exemplo Passo a Passo: Modelando o Pokémon

  1. Veja como o dado chega (JSON):
    { "id": 25, "name": "pikachu" }
    
  2. Crie a classe correspondente no Kotlin:
    package br.com.curso.pokedex.data.model
    
    data class Pokemon(
        val id: Int,
        val name: String
    )
    

🛠️ Prática Obrigatória 1: Utilize o Beeceptor para criar uma rota GET /meu-poke. Configure para que ela retorne um JSON com o nome e o poder de um personagem de jogo.


🛠️ Prática Obrigatória 2: Crie a interface GameService na pasta data/network. Ela deve ter uma função para buscar o herói que você criou no Mock.


🔑 Gabarito Passo a Passo:

package br.com.curso.pokedex.data.network

import retrofit2.http.GET

interface GameService {
    @GET("meu-poke")
    suspend fun buscarHeroi(): Personagem
}

data class Personagem(val name: String, val power: String)

🧠 Fundamentos (Parte 2): Do ViewModel até a API

A interface GameService que você criou sabe como pedir os dados, mas alguém precisa chamar essa função suspend e entregar o resultado para a tela. É aqui que entram as Corrotinas e o Repository.

📖 Dicionário do Programador

💡 As corrotinas (launch) já vêm incluídas na dependência lifecycle-viewmodel-ktx do Cap 12 — não é necessário adicionar nada novo no Gradle.

graph LR
    A["View: Tela"] -->|chama| B["ViewModel: carregarHeroi()"]
    B -->|viewModelScope.launch| C["Repository: buscarHeroi()"]
    C -->|suspend| D["GameService: Retrofit"]
    D -->|JSON| C
    C --> B
    B -->|atualiza State| A

📖 Exemplo Passo a Passo: O Repository e o ViewModel

  1. Crie o Repository na pasta data/repository/. Ele recebe o GameService e apenas repassa a chamada suspend.
  2. No ViewModel (pasta ui/viewmodel/ do Cap 12), use viewModelScope.launch para chamar o Repository e guardar o resultado num State.
package br.com.curso.pokedex.data.repository

import br.com.curso.pokedex.data.network.GameService
import br.com.curso.pokedex.data.network.Personagem

class GameRepository(private val service: GameService) {
    suspend fun buscarHeroi(): Personagem {
        return service.buscarHeroi()
    }
}
package br.com.curso.pokedex.ui.viewmodel

import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import br.com.curso.pokedex.data.network.Personagem
import br.com.curso.pokedex.data.repository.GameRepository
import kotlinx.coroutines.launch

class HeroiViewModel(private val repository: GameRepository) : ViewModel() {
    private val _heroi = mutableStateOf<Personagem?>(null)
    val heroi: State<Personagem?> = _heroi

    fun carregarHeroi() {
        // viewModelScope.launch abre uma corrotina: a tela não trava esperando a internet
        viewModelScope.launch {
            _heroi.value = repository.buscarHeroi()
        }
    }
}

🛠️ Prática Obrigatória 3: Crie a classe PersonagemRepository, que recebe um GameService e expõe suspend fun buscarPersonagem(): Personagem (chamando service.buscarHeroi()). Depois, crie a classe PersonagemViewModel2 com um estado nomePersonagem: State<String> (começando em "") e uma função carregarPersonagem() que usa viewModelScope.launch para chamar o repository e atualizar nomePersonagem com o name do personagem recebido.


🔑 Gabarito (Parte 2):

package br.com.curso.pokedex.data.repository

import br.com.curso.pokedex.data.network.GameService
import br.com.curso.pokedex.data.network.Personagem

class PersonagemRepository(private val service: GameService) {
    suspend fun buscarPersonagem(): Personagem {
        return service.buscarHeroi()
    }
}
package br.com.curso.pokedex.ui.viewmodel

import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import br.com.curso.pokedex.data.repository.PersonagemRepository
import kotlinx.coroutines.launch

class PersonagemViewModel2(private val repository: PersonagemRepository) : ViewModel() {
    private val _nomePersonagem = mutableStateOf("")
    val nomePersonagem: State<String> = _nomePersonagem

    fun carregarPersonagem() {
        viewModelScope.launch {
            _nomePersonagem.value = repository.buscarPersonagem().name
        }
    }
}

📤 Instruções de Entrega (Microsoft Teams):

  1. Envie a URL do seu Beeceptor.
  2. Envie o código da sua Interface.
  3. Envie o código do seu Repository e ViewModel com corrotinas (Parte 2).
  4. Submeta no canal de tarefas.

⬅️ Voltar para a Home