Criar um app Android com o Firebase e o Jetpack Compose

1. Introdução

Última atualização:16/11/2022

Como criar um app Android com o Firebase e o Jetpack Compose

Neste codelab, você criará um app Android chamado Make It So. A interface dele é totalmente criada com o Jetpack Compose, que é o kit de ferramentas moderno do Android para criar interfaces nativas. Ele é intuitivo e requer menos código do que escrever arquivos .xml e vinculá-los a atividades, fragmentos ou visualizações.

A primeira etapa para entender como o Firebase e o Jetpack Compose funcionam juntos é entender a arquitetura moderna do Android. Uma boa arquitetura torna o sistema fácil de entender, desenvolver e manter, porque deixa muito claro como os componentes são organizados e se comunicam entre si. No mundo do Android, a arquitetura recomendada é chamada de Modelo – Visualização – ViewModel. O modelo representa a camada que acessa os dados no aplicativo. A View é a camada da interface e não precisa saber nada sobre a lógica de negócios. E o ViewModel é onde a lógica de negócios é aplicada, o que às vezes exige que o ViewModel chame a camada Model.

Recomendamos a leitura deste artigo para entender como Model - View - ViewModel é aplicado a um app Android criado com o Jetpack Compose, já que isso vai facilitar o entendimento da base de código e a conclusão das próximas etapas.

O que você vai criar

O Make it So é um aplicativo simples de lista de tarefas que permite ao usuário adicionar e editar tarefas, adicionar sinalizações, prioridades e datas de conclusão e marcar as tarefas como concluídas. As imagens abaixo mostram as duas páginas principais desse aplicativo: a página de criação de tarefas e a página principal com a lista de tarefas criadas.

Tela "Adicionar tarefa" Tela inicial

Você vai adicionar alguns recursos que estão faltando no app:

  • Autenticar usuários com e-mail e senha
  • Adicionar um listener a uma coleção do Firestore e fazer a interface reagir às mudanças
  • Adicione traces personalizados para monitorar o desempenho de código específico no app
  • Criar um botão de alternância de recursos usando a Configuração remota e usar o lançamento gradual para iniciá-lo

O que você aprenderá

  • Como usar o Firebase Authentication, o Monitoramento de desempenho, a Configuração remota e o Cloud Firestore em um aplicativo Android moderno
  • Como fazer as APIs do Firebase se encaixarem em uma arquitetura MVVM
  • Como refletir as mudanças feitas com as APIs do Firebase em uma interface do Compose.

O que é necessário

2. Fazer o download do app de exemplo e configurar o Firebase

Fazer o download do código do app de exemplo

Clone o repositório do GitHub na linha de comando:

git clone https://github.com/FirebaseExtended/make-it-so-android.git

Configurar o Firebase

Primeiro, acesse o Console do Firebase e clique no botão "+ Adicionar projeto" para criar um projeto, conforme mostrado abaixo:

Console do Firebase

Siga as etapas na tela para concluir a criação do projeto.

Dentro de cada projeto do Firebase, você pode criar apps diferentes: para Android, iOS, Web, Flutter e Unity. Escolha a opção Android, como mostrado aqui:

Visão geral do projeto do Firebase

Em seguida:

  1. Insira com.example.makeitso como o nome do pacote e, se quiser, insira um apelido. Neste codelab, não é necessário adicionar o certificado de assinatura de depuração.
  2. Clique em Próxima para registrar o app e acessar o arquivo de configuração do Firebase.
  3. Clique em Fazer o download do google-services.json para fazer o download do arquivo de configuração e salvá-lo no diretório make-it-so-android/app.
  4. Clique em Próxima. Como os SDKs do Firebase já estão incluídos no arquivo build.gradle no projeto de amostra, clique em Próxima e pule para Próximas etapas.
  5. Clique em Continuar no console para concluir.

Para que o app Make it So funcione corretamente, você precisa fazer duas coisas no console antes de ir para o código: ativar os provedores de autenticação e criar o banco de dados do Firestore. Primeiro, vamos ativar o Authentication para que os usuários possam fazer login no app:

  1. No menu Build, selecione Authentication e clique em Get Started.
  2. No card Método de login, selecione E-mail/senha e ative essa opção.
  3. Em seguida, clique em Adicionar novo provedor, selecione e ative Anônimo.

Em seguida, configure o Firestore. Você vai usar o Firestore para armazenar as tarefas de um usuário conectado. Cada usuário vai receber o próprio documento em uma coleção do banco de dados.

  1. No menu Build, selecione Firestore e clique em Criar banco de dados.
  2. Mantenha a opção Iniciar no modo de produção ativada e clique em Próxima.
  3. Quando solicitado, selecione o local onde os dados do Cloud Firestore serão armazenados. Ao desenvolver um app de produção, é necessário que ele esteja em uma região próxima à maioria dos usuários e em comum com outros serviços do Firebase, como o Functions. Neste codelab, é possível manter a região padrão ou selecionar a mais próxima de você.
  4. Clique em Ativar para provisionar seu banco de dados do Firestore.

Vamos criar regras de segurança robustas para o banco de dados do Firestore. Abra o painel do Firestore e acesse a guia Regras. Em seguida, atualize as regras de segurança para que fiquem assim:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null && resource.data.userId == request.auth.uid;
    }
  }
}

Essas regras basicamente dizem que qualquer usuário conectado ao aplicativo pode criar um documento para si mesmo em qualquer coleção. Após a criação, somente o usuário que criou o documento poderá visualizá-lo, atualizá-lo ou excluí-lo.

Executar o aplicativo

Agora você já pode executar o aplicativo. Abra a pasta make-it-so-android/start no Android Studio e execute o app. Isso pode ser feito usando um Android Emulator ou um dispositivo Android real.

3. Firebase Authentication

Qual recurso você vai adicionar?

No estado atual do app de exemplo Make It So, o usuário pode começar a usar o app sem precisar fazer login. Para isso, ele usa autenticação anônima. No entanto, contas anônimas não permitem que um usuário acesse seus dados em outros dispositivos ou mesmo em sessões futuras. Embora a autenticação anônima seja útil para uma integração aconchegante, sempre forneça a opção de conversão para uma forma diferente de login. Com isso em mente, neste codelab, você vai adicionar a autenticação por e-mail e senha ao app Make It So.

Hora de começar a programar

Assim que o usuário criar uma conta, digitando um e-mail e uma senha, você precisará solicitar à API Firebase Authentication uma credencial de e-mail e, em seguida, vincular a nova credencial à conta anônima. Abra o arquivo AccountServiceImpl.kt no Android Studio e atualize a função linkAccount para que fique assim:

model/service/impl/AccountServiceImpl.kt (link em inglês)

override suspend fun linkAccount(email: String, password: String) {
    val credential = EmailAuthProvider.getCredential(email, password)
    auth.currentUser!!.linkWithCredential(credential).await()
}

Agora abra SignUpViewModel.kt e chame a função linkAccount de serviço dentro do bloco launchCatching da função onSignUpClick:

screens/sign_up/SignUpViewModel.kt (link em inglês)

launchCatching {
    accountService.linkAccount(email, password)
    openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}

Primeiro, ela tenta se autenticar e, se a chamada for bem-sucedida, ela segue para a próxima tela (SettingsScreen). À medida que você está executando essas chamadas dentro de um bloco launchCatching, se ocorrer um erro na primeira linha, a exceção será capturada e processada, e a segunda linha não será alcançada.

Assim que o SettingsScreen for aberto novamente, verifique se as opções Fazer login e Criar conta desaparecem, porque o usuário já está autenticado. Para fazer isso, vamos fazer o SettingsViewModel detectar o status do usuário atual (disponível em AccountService.kt) para verificar se a conta é anônima. Para fazer isso, atualize a uiState no SettingsViewModel.kt para que fique assim:

Telas/settings/SettingsViewModel.kt

val uiState = accountService.currentUser.map {
    SettingsUiState(it.isAnonymous)
}

A última coisa que você precisa fazer é atualizar o uiState em SettingsScreen.kt para coletar os estados emitidos pelo SettingsViewModel:

screen/settings/SettingsScreen.kt (link em inglês)

val uiState by viewModel.uiState.collectAsState(
    initial = SettingsUiState(false)
)

Agora, sempre que o usuário mudar, o SettingsScreen vai ser recomposto para mostrar as opções de acordo com o novo estado de autenticação do usuário.

Hora de testar!

Execute Make it So e navegue até as configurações clicando no ícone de engrenagem no canto superior direito da tela. Depois, clique na opção "Criar conta":

Tela de configurações do Make it So Use a tela de inscrição

Digite um e-mail válido e uma senha forte para criar sua conta. Isso deve funcionar e você será redirecionado para a página de configurações, onde verá duas novas opções: sair e excluir sua conta. Para verificar a nova conta criada no painel do Authentication no Console do Firebase, clique na guia Usuários.

4. Cloud Firestore

Qual recurso você vai adicionar?

No caso do Cloud Firestore, você adicionará um listener à coleção do Firestore que armazena os documentos que representam as tarefas exibidas em Make it So. Depois de adicionar esse listener, você receberá todas as atualizações feitas na coleção.

Hora de começar a programar

Atualize o Flow disponível no StorageServiceImpl.kt para que fique assim:

model/service/impl/StorageServiceImpl.kt (link em inglês)

override val tasks: Flow<List<Task>>
    get() =
      auth.currentUser.flatMapLatest { user ->
        firestore.collection(TASK_COLLECTION).whereEqualTo(USER_ID_FIELD, user.id).dataObjects()
      }

Esse código adiciona um listener à coleção de tarefas com base no user.id. Cada tarefa é representada por um documento em uma coleção chamada tasks, e cada uma delas tem um campo chamado userId. Um novo Flow será emitido se o status das currentUser mudar (por exemplo, ao sair da conta).

Agora, você precisa fazer com que o Flow em TasksViewModel.kt reflita o mesmo que no serviço:

screen/tasks/TasksViewModel.kt (link em inglês)

val tasks = storageService.tasks

A última coisa é criar a composable function no TasksScreens.kt, que representa a interface, estar ciente desse fluxo e coletá-lo como um estado. Sempre que o estado muda, a função combinável é recomposta automaticamente e mostra o estado mais recente ao usuário. Adicione isto a TasksScreen composable function:

screen/tasks/TasksScreen.kt (link em inglês)

val tasks = viewModel
    .tasks
    .collectAsStateWithLifecycle(emptyList())

Depois que a função combinável tiver acesso a esses estados, você vai poder atualizar a LazyColumn, que é a estrutura usada para mostrar uma lista na tela, para que fique assim:

screen/tasks/TasksScreen.kt (link em inglês)

LazyColumn {
    items(tasks.value, key = { it.id }) { taskItem ->
        TaskItem( [...] )
    }
}

Hora de testar!

Para testar se funcionou, adicione uma nova tarefa usando o app (clicando no botão de adição no canto inferior direito da tela). Depois que você terminar de criar a tarefa, ela aparecerá na coleção do Firestore no Console do Firestore. Se você fizer login no Make it So em outros dispositivos com a mesma conta, você poderá editar seus itens de tarefas e vê-los sendo atualizados em todos os dispositivos em tempo real.

5. Monitoramento de desempenho

Qual recurso você vai adicionar?

É muito importante prestar atenção no desempenho, porque os usuários provavelmente desistirão de usar o app se ele não for bom e demorarem muito para realizar uma tarefa simples com ele. É por isso que, às vezes, é útil coletar algumas métricas sobre uma jornada específica que um usuário faz no seu app. E, para ajudar você com isso, o Monitoramento de desempenho do Firebase oferece traces personalizados. Siga as próximas etapas para adicionar traces personalizados e medir o desempenho em diferentes partes de código em Faça isso.

Hora de começar a programar

Se você abrir o arquivo Performance.kt, vai encontrar uma função in-line chamada trace. Essa função chama a API Performance Monitoring para criar um trace personalizado, transmitindo o nome dele como um parâmetro. O outro parâmetro é o bloco de código que você quer monitorar. A métrica padrão coletada para cada trace é o tempo necessário para a execução completa:

model/service/Performance.kt (link em inglês)

inline fun <T> trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block)

Você pode escolher quais partes da base de código você considera importantes medir e adicionar traces personalizados a elas. Confira um exemplo de como adicionar um rastro personalizado à função linkAccount que você conferiu anteriormente (em AccountServiceImpl.kt) neste codelab:

model/service/impl/AccountServiceImpl.kt (link em inglês)

override suspend fun linkAccount(email: String, password: String): Unit =
  trace(LINK_ACCOUNT_TRACE) {
      val credential = EmailAuthProvider.getCredential(email, password)
      auth.currentUser!!.linkWithCredential(credential).await()
  }

Agora é sua vez. Adicione alguns traces personalizados ao app Make it So e avance até a próxima seção para testar se funcionou como esperado.

Hora de testar!

Depois de adicionar os traces personalizados, execute o app e use os recursos que você quer medir algumas vezes. Em seguida, acesse o Console do Firebase e acesse o Painel de desempenho. Na parte inferior da tela, você encontrará três guias: Solicitações de rede, Traces personalizados e Renderização de tela.

Acesse a guia Traces personalizados e verifique se os traces adicionados à base de código estão sendo exibidos nela e se você pode ver quanto tempo normalmente leva para executar esses códigos.

6. Configuração remota

Qual recurso você vai adicionar?

Há uma infinidade de casos de uso para a Configuração remota, desde mudar a aparência do app remotamente até configurar diferentes comportamentos para segmentos de usuários distintos. Neste codelab, você vai usar a Configuração remota para criar um botão de alternância que vai mostrar ou ocultar o novo recurso Editar tarefas no app Faça isso.

Hora de começar a programar

A primeira coisa que você precisa fazer é criar a configuração no console do Firebase. Para isso, acesse o painel da Configuração remota e clique no botão Adicionar parâmetro. Preencha os campos de acordo com a imagem abaixo:

Caixa de diálogo &quot;Criar um parâmetro&quot; da Configuração remota

Quando todos os campos estiverem preenchidos, clique no botão Salvar e em Publicar. Agora que o parâmetro foi criado e está disponível para sua base de código, adicione o código que vai buscar os novos valores no app. Abra o arquivo ConfigurationServiceImpl.kt e atualize a implementação dessas duas funções:

model/service/impl/ConfigurationServiceImpl.kt (link em inglês)

override suspend fun fetchConfiguration(): Boolean {
  return remoteConfig.fetchAndActivate().await()
}

override val isShowTaskEditButtonConfig: Boolean
  get() = remoteConfig[SHOW_TASK_EDIT_BUTTON_KEY].asBoolean()

A primeira função busca os valores do servidor e é chamada assim que o app é iniciado, em SplashViewModel.kt. Essa é a melhor maneira de garantir que os valores mais atualizados estejam disponíveis em todas as telas desde o início. Não é uma boa experiência do usuário se você mudar a interface ou o comportamento do app mais tarde, quando o usuário estiver fazendo algo.

A segunda função retorna o valor booleano que foi publicado para o parâmetro que você acabou de criar no console. Você vai precisar extrair essas informações no TasksViewModel.kt adicionando o seguinte à função loadTaskOptions:

screen/tasks/TasksViewModel.kt (link em inglês)

fun loadTaskOptions() {
  val hasEditOption = configurationService.isShowTaskEditButtonConfig
  options.value = TaskActionOption.getOptions(hasEditOption)
}

Você está recuperando o valor na primeira linha e usando-o para carregar as opções de menu dos itens da tarefa na segunda linha. Se o valor for false, o menu não vai conter a opção de edição. Agora que você tem a lista de opções, é preciso que a IU a exiba corretamente. Ao criar um app com o Jetpack Compose, você precisa procurar o composable function que declara como será a interface do TasksScreen. Abra o arquivo TasksScreen.kt e atualize LazyColum para apontar para as opções disponíveis em TasksViewModel.kt:

screen/tasks/TasksScreen.kt (link em inglês)

val options by viewModel.options

LazyColumn {
  items(tasks.value, key = { it.id }) { taskItem ->
    TaskItem(
      options = options,
      [...]
    )
  }
}

O TaskItem é outro composable function que declara como será a interface de uma única tarefa. Cada tarefa tem um menu com opções que é exibido quando o usuário clica no ícone de três pontos ao final.

Hora de testar!

Agora você já pode executar o app. Verifique se o valor publicado usando o Console do Firebase corresponde ao comportamento do app:

  • Se for false, você só vai ver duas opções ao clicar no ícone de três pontos:
  • Se for true, três opções vão aparecer ao clicar no ícone de três pontos:

Tente mudar o valor algumas vezes no console e reinicie o app. Com a Configuração remota, é fácil iniciar novos recursos no app.

7. Parabéns

Parabéns! Você criou um app Android com o Firebase e o Jetpack Compose.

Você adicionou o Firebase Authentication, o Monitoramento de desempenho, a Configuração remota e o Cloud Firestore a um app Android inteiramente desenvolvido com o Jetpack Compose para a interface do usuário e o colocou na arquitetura MVVM recomendada.

Leia mais

Documentos de referência