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 desse app foi totalmente criada com o Jetpack Compose, que é um 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 Android, a arquitetura recomendada é chamada de Model - View - ViewModel. O modelo representa a camada que acessa os dados no aplicativo. A View é a camada da interface e não pode 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 que você leia este artigo para entender como o Model – View – ViewModel é aplicado a um app Android criado com o Jetpack Compose, porque isso facilita a compreensão da base de código e a conclusão das próximas etapas.
O que você vai criar
Make It So é um aplicativo de lista de tarefas simples que permite ao usuário adicionar e editar tarefas, incluir 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.
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 com que a interface reaja às alterações
- Adicione rastros personalizados para monitorar o desempenho de um código específico no app
- Criar uma alternância de recursos usando a Configuração remota e usar o lançamento gradual para iniciá-la
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 encaixar as APIs do Firebase em uma arquitetura MVVM
- Como refletir as mudanças feitas com as APIs do Firebase em uma interface do Compose.
O que é necessário
- Android Studio Flamingo ou mais recente.
- Android Emulator com API 21 ou mais recente
- Conhecer a linguagem de programação Kotlin.
2. Instalar o 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
A primeira coisa que você precisa fazer é acessar o Console do Firebase e criar um projeto do Firebase clicando no botão "+ Adicionar projeto" como mostrado abaixo:
Siga as etapas na tela para concluir a criação do projeto.
Dentro de cada projeto do Firebase, é possível criar apps diferentes para Android, iOS, Web, Flutter e Unity. Escolha a opção do Android, como você vê aqui:
Em seguida:
- Digite
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. - Clique em Próxima para registrar o app e acessar o arquivo de configuração do Firebase.
- 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
. - 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 para pular para Próximas etapas. - Clique em Continuar no console para concluir.
Para que o app Make it So funcione corretamente, há duas coisas que você precisa fazer no console antes de ir para o código: ativar os provedores de autenticação e criar o banco de dados do Firestore. Primeiro, ative a autenticação para que os usuários possam fazer login no app:
- No menu Build, selecione Authentication e clique em Get Started.
- No card Método de login, selecione E-mail/senha e ative a opção.
- Em seguida, clique em Adicionar novo provedor e 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.
- No menu Build, selecione Firestore e clique em Create database.
- Mantenha a opção Iniciar no modo de produção ativada e clique em Próxima.
- Quando solicitado, selecione o local em que os dados do Cloud Firestore serão armazenados. Ao desenvolver um app de produção, é recomendável que ele esteja em uma região próxima à maioria dos seus 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ê.
- 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;
}
}
}
Basicamente, essas regras dizem que qualquer usuário conectado ao aplicativo pode criar um documento para si em qualquer coleção. Após a criação, apenas o usuário que criou o documento poderá visualizá-lo, atualizá-lo ou excluí-lo.
Executar o aplicativo
Agora está tudo pronto para 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 primeiro. Ele usa autenticação anônima para fazer isso. No entanto, contas anônimas não permitem que um usuário acesse os dados dele em outros dispositivos ou mesmo em sessões futuras. Embora a autenticação anônima seja útil para uma integração interna, você sempre deve oferecer a opção de conversão para uma forma diferente de login para os usuários. Com isso em mente, neste codelab, você vai adicionar autenticação por e-mail e senha ao app Make It So.
Hora de programar
Assim que o usuário cria uma conta, digitando um e-mail e uma senha, você precisa solicitar uma credencial de e-mail à API Firebase Authentication e vincular a nova credencial à conta anônima. Abra o arquivo AccountServiceImpl.kt
no Android Studio e atualize a função linkAccount
para que ela 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
:
telas/sign_up/SignUpViewModel.kt (link em inglês)
launchCatching {
accountService.linkAccount(email, password)
openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}
Primeiro, ele tenta fazer a autenticação e, se a chamada for bem-sucedida, segue para a próxima tela (a SettingsScreen
). Como 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, as opções para Fazer login e Criar conta não estarão mais disponíveis, porque o usuário já está autenticado. Para isso, vamos fazer com que a SettingsViewModel
detecte o status do usuário atual (disponível em AccountService.kt
) para verificar se a conta é anônima ou não. Para fazer isso, atualize o uiState
no SettingsViewModel.kt
para que fique assim:
screens/settings/SettingsViewModel.kt (link em inglês)
val uiState = accountService.currentUser.map {
SettingsUiState(it.isAnonymous)
}
A última coisa que você precisa fazer é atualizar o uiState
no SettingsScreen.kt
para coletar os estados emitidos pelo SettingsViewModel
:
screens/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 acesse as configurações clicando no ícone de engrenagem no canto superior direito da tela. Depois, clique na opção criar conta:
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 do Console do Firebase, clique na guia Usuários.
4. Cloud Firestore
Qual recurso você vai adicionar?
Para o 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 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 está adicionando 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 do currentUser
mudar (por exemplo, ao sair).
Agora é necessário 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 etapa é fazer com que a composable function
no TasksScreens.kt
, que representa a interface, reconheça esse fluxo e o colete como um estado. Toda vez que o estado muda, a função combinável é recomposta automaticamente e mostra o estado mais recente para o usuário. Adicione isto ao TasksScreen composable function
:
screen/tasks/TasksScreen.kt (link em inglês)
val tasks = viewModel
.tasks
.collectAsStateWithLifecycle(emptyList())
Quando a função combinável tiver acesso a esses estados, você poderá atualizar LazyColumn
, que é a estrutura usada para mostrar uma lista na tela, para ficar 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. Para isso, clique no botão "Adicionar" 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, poderá editar itens de tarefas e vê-los sendo atualizados em todos os dispositivos em tempo real.
5. Monitoramento de desempenho
Qual recurso você vai adicionar?
O desempenho é muito importante, porque é muito provável que os usuários desistam de usar seu app se ele não for bom e levarem muito tempo para concluir uma tarefa simples com ele. Por isso, às vezes, é útil coletar algumas métricas sobre a jornada específica do usuário no app. Para ajudar você com isso, o Monitoramento de desempenho do Firebase oferece rastros personalizados. Siga as próximas etapas para adicionar traces personalizados e medir o desempenho em diferentes partes do código em Make it So.
Hora de 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 exibido é 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)
É possível escolher quais partes da base de código você acha que é importante medir e adicionar rastros personalizados a ela. Confira um exemplo de como adicionar um rastro personalizado à função linkAccount
mostrada 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 rastros personalizados ao app Make it So e vá para a próxima seção para testar se funcionou como esperado.
É hora de testar!
Depois de terminar de adicionar os rastros 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, há três guias: Solicitações de rede, Traces personalizados e Renderização de tela.
Acesse a guia Traces personalizados e verifique se os rastros adicionados à base de código são exibidos lá. Confira também quanto tempo leva para executar esses trechos de código.
6. Configuração remota
Qual recurso você vai adicionar?
Há uma infinidade de casos de uso para a Configuração remota, desde alterar a aparência do app remotamente até configurar diferentes comportamentos para diferentes segmentos de usuários. Neste codelab, você vai usar a Configuração remota para criar um botão de alternância que mostra ou oculta o novo recurso de edição no app Make it So.
Hora de 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:
Depois de preencher todos os campos, clique no botão Save e em Publish. Agora que o parâmetro foi criado e está disponível para sua base de código, você precisa adicionar o código que buscará os novos valores no seu app. Abra o arquivo ConfigurationServiceImpl.kt
e atualize a implementação destas 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 em SplashViewModel.kt
assim que o app é iniciado. 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 publicado para o parâmetro que você acabou de criar no console. Você precisará recuperar essas informações em 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 de tarefa na segunda linha. Se o valor for false
, significa que o menu não conterá a opção de edição. Agora que você tem a lista de opções, precisa fazer com que a IU a exiba corretamente. Ao criar um app com o Jetpack Compose, você precisa procurar o composable function
que declara a aparência da 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,
[...]
)
}
}
A TaskItem
é outra composable function
que declara a aparência da interface de uma única tarefa. E cada tarefa tem um menu com opções que são exibidas quando o usuário clica no ícone de três pontos no 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ê verá apenas duas opções ao clicar no ícone de três pontos. - Se for
true
, você verá três opções ao clicar no ícone de três pontos.
Tente mudar o valor algumas vezes no console e reiniciar o app. É assim que é fácil lançar novos recursos no seu app usando a Configuração remota.
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 totalmente criado com o Jetpack Compose para a interface e o adaptou à arquitetura MVVM recomendada.
Leia mais
- Como criar um app Android com o Firebase e o Compose
- Como adicionar o Firebase Authentication a um app do Jetpack Compose
- Como adicionar o Cloud Firestore a um app do Jetpack Compose
- Como adicionar corrotinas e fluxos a um app Android criado com o Firebase e o Compose
- Como adicionar o Monitoramento de desempenho do Firebase a um app do Jetpack Compose
- Como adicionar a Configuração remota do Firebase a um app do Jetpack Compose