Adicionar um fluxo de autenticação do usuário a um app do Flutter usando a FirebaseUI

1. Antes de começar

Neste codelab, você vai aprender a adicionar o Firebase Authentication ao seu app Flutter usando o pacote da interface do FlutterFire. Com esse pacote, você vai adicionar a autenticação por e-mail/senha e a autenticação do Google Sign In a um app Flutter. Também vai aprender a configurar um projeto do Firebase e usar a CLI do FlutterFire para inicializar o Firebase no seu app Flutter.

Pré-requisitos

Este codelab presume que você tem alguma experiência com o Flutter. Caso contrário, talvez você queira primeiro aprender o básico. Confira alguns links úteis:

Você também precisa ter alguma experiência com o Firebase, mas não precisa ter adicionado o Firebase a um projeto do Flutter. Se você não conhece o Console do Firebase ou é novo no Firebase, consulte os links a seguir:

O que você vai criar

Este codelab orienta você a criar o fluxo de autenticação de um app Flutter usando o Firebase para autenticação. O aplicativo terá uma tela de login, uma tela de "Registro", uma tela de recuperação de senha e uma tela de perfil do usuário.

6604fc9157f2c6ae.png eab9509a41074930.png da49189a5838e0bb.png b2ccfb3632b77878.png

O que você vai aprender

Este codelab aborda:

  • Como adicionar o Firebase a um app Flutter
  • Configuração do Console do Firebase
  • Como usar a CLI do Firebase para adicionar o Firebase ao seu aplicativo
  • Como usar a CLI do FlutterFire para gerar a configuração do Firebase no Dart
  • Adicionar o Firebase Authentication ao seu app Flutter
  • Configuração do Firebase Authentication no console
  • Como adicionar o login com e-mail e senha ao pacote firebase_ui_auth
  • Como adicionar o registro de usuário com o pacote firebase_ui_auth
  • Como adicionar a página "Esqueceu a senha?"
  • Como adicionar o Login do Google com firebase_ui_auth
  • Configurar o app para funcionar com vários provedores de login.
  • Como adicionar uma tela de perfil do usuário ao seu app com o pacote firebase_ui_auth

Este codelab trata especificamente de adicionar um sistema de autenticação robusto usando o pacote firebase_ui_auth. Como você vai notar, esse app inteiro, com todos os recursos acima, pode ser implementado com cerca de 100 linhas de código.

Pré-requisitos

  • Conhecimento prático do Flutter (link em inglês) e do SDK instalado.
  • Um editor de texto (os ambientes de desenvolvimento integrado da JetBrains, o Android Studio e o VS Code são compatíveis com o Flutter)
  • Navegador Google Chrome ou outra plataforma de desenvolvimento da sua preferência para o Flutter. Alguns comandos do terminal neste codelab vão presumir que o app está sendo executado no Chrome.

2. Criar e configurar um projeto do Firebase

A primeira tarefa que você precisa concluir é criar um projeto do Firebase no console da Web do Firebase.

Criar um projeto do Firebase

  1. Faça login no Firebase.
  2. No Console do Firebase, clique em Adicionar projeto (ou Criar um projeto) e insira um nome para o projeto do Firebase (por exemplo, FlutterFire-UI-Codelab).

df42a5e3d9584b48.png

  1. Clique nas opções de criação de projeto. Aceite os termos do Firebase se solicitado. Ignore a configuração do Google Analytics, porque você não vai usá-lo para este app.

d1fcec48bf251eaa.png

Para saber mais sobre os projetos do Firebase, consulte Noções básicas sobre projetos do Firebase.

Ativar o login por e-mail para o Firebase Authentication

O app que você está criando usa o Firebase Authentication para permitir que os usuários façam login e também permite que novos usuários se registrem no aplicativo Flutter.

O Firebase Authentication precisa ser ativado por meio do Console do Firebase e de configuração especial após a ativação.

Para permitir que os usuários façam login no app da Web, primeiro use o método de login E-mail/senha. Mais tarde, você vai adicionar o método Google Sign-In.

  1. No Console do Firebase, abra o menu Build no painel à esquerda.
  2. Clique em Autenticação e depois no botão Começar. Em seguida, clique na guia Método de login (ou clique aqui para acessar diretamente a guia Método de login).
  3. Clique em E-mail/Senha na lista Provedores de login, defina a chave Ativar como "Ativado" e clique em Salvar. 58e3e3e23c2f16a4.png

3. Configurar o app Flutter

Você vai precisar fazer o download do código inicial e instalar a CLI do Firebase antes de começar.

Acessar o código inicial

Clone o repositório do GitHub (link em inglês) na linha de comando:

git clone https://github.com/flutter/codelabs.git flutter-codelabs

Como alternativa, se você tiver a ferramenta de CLI do GitHub instalada:

gh repo clone flutter/codelabs flutter-codelabs

O exemplo de código será clonado no diretório flutter-codelabs da sua máquina, que contém o código de uma coleção de codelabs. O código deste codelab está no subdiretório flutter-codelabs/firebase-auth-flutterfire-ui.

O diretório flutter-codelabs/firebase-auth-flutterfire-ui contém dois projetos do Flutter. Um se chama complete e o outro é start. O diretório start contém um projeto incompleto, e é nele que você vai passar mais tempo.

cd flutter-codelabs/firebase-auth-flutterfire-ui/start

Se você quiser pular para a frente ou conferir como algo vai ficar quando estiver concluído, procure no diretório "complete" para fazer uma referência cruzada.

Se você quiser acompanhar o codelab e adicionar código por conta própria, comece com o app do Flutter em flutter-codelabs/firebase-auth-flutterfire-ui/start e adicione código a esse projeto durante todo o codelab. Abra ou importe esse diretório no ambiente de desenvolvimento integrado de sua preferência.

Instalar a CLI do Firebase

A CLI do Firebase oferece ferramentas para gerenciar seus projetos do Firebase. A CLI é necessária para a CLI do FlutterFire, que você vai instalar em breve.

Há várias maneiras de instalar a CLI. A maneira mais simples, se você estiver usando o MacOS ou Linux, é executar este comando no terminal:

curl -sL https://firebase.tools | bash

Depois de instalar a CLI, você precisa fazer a autenticação com o Firebase.

  1. Faça login no Firebase com sua Conta do Google executando o seguinte comando:
firebase login
  1. Esse comando conecta sua máquina local ao Firebase e concede acesso aos seus projetos do Firebase.
  1. Liste seus projetos do Firebase para testar se a CLI está instalada corretamente e tem acesso à sua conta. Execute este comando:
firebase projects:list
  1. A lista exibida precisa conter os mesmos projetos que aparecem no Console do Firebase. Você verá pelo menos flutterfire-ui-codelab.

Instalar a CLI do FlutterFire

A CLI do FlutterFire é uma ferramenta que ajuda a facilitar o processo de instalação do Firebase em todas as plataformas compatíveis com seu app do Flutter. Ela foi criada com base na CLI do Firebase.

Primeiro, instale a CLI:

dart pub global activate flutterfire_cli

Verifique se a CLI foi instalada. Execute o comando a seguir e verifique se a CLI exibe o menu de ajuda.

flutterfire -—help

Adicionar o projeto do Firebase ao app Flutter

Configurar o FlutterFire

Você pode usar o FlutterFire para gerar o código Dart necessário para usar o Firebase no seu app Flutter.

flutterfire configure

Quando esse comando for executado, você vai precisar selecionar o projeto do Firebase que quer usar e as plataformas que quer configurar.

As capturas de tela a seguir mostram as solicitações que você precisa responder.

  1. Selecione o projeto que você quer usar. Nesse caso, use flutterfire-ui-codelab 1359cdeb83204baa.png
  2. Selecione as plataformas que você quer usar. Neste codelab, há etapas para configurar a Autenticação do Firebase para Flutter na Web, no iOS e no Android, mas você pode configurar seu projeto do Firebase para usar todas as opções. 301c9534f594f472.png
  3. Esta captura de tela mostra a saída no final do processo. Se você já conhece o Firebase, vai perceber que não precisou criar aplicativos de plataforma (por exemplo, um aplicativo Android) no console, e a CLI do FlutterFire fez isso por você. 12199a85ade30459.png

Quando terminar, observe o app Flutter no editor de texto. A CLI do FlutterFire gerou um novo arquivo chamado firebase_options.dart. Esse arquivo contém uma classe chamada FirebaseOptions, que tem variáveis estáticas que armazenam a configuração do Firebase necessária para cada plataforma. Se você tiver selecionado todas as plataformas ao executar flutterfire configure, verá valores estáticos chamados web, android, ios e macos.

firebase_options.dart

import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
   show defaultTargetPlatform, kIsWeb, TargetPlatform;

/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
///   options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
 static FirebaseOptions get currentPlatform {
   if (kIsWeb) {
     return web;
   }
   // ignore: missing_enum_constant_in_switch
   switch (defaultTargetPlatform) {
     case TargetPlatform.android:
       return android;
     case TargetPlatform.iOS:
       return ios;
     case TargetPlatform.macOS:
       return macos;
   }

   throw UnsupportedError(
     'DefaultFirebaseOptions are not supported for this platform.',
   );
 }

 static const FirebaseOptions web = FirebaseOptions(
   apiKey: 'AIzaSyCqFjCV_9CZmYeIvcK9FVy4drmKUlSaIWY',
   appId: '1:963656261848:web:7219f7fca5fc70afb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   authDomain: 'flutterfire-ui-codelab.firebaseapp.com',
   storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
   measurementId: 'G-DGF0CP099H',
 );

 static const FirebaseOptions android = FirebaseOptions(
   apiKey: 'AIzaSyDconZaCQpkxIJ5KQBF-3tEU0rxYsLkIe8',
   appId: '1:963656261848:android:c939ccc86ab2dcdbb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
 );

 static const FirebaseOptions ios = FirebaseOptions(
   apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
   appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
   iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
   iosBundleId: 'com.example.complete',
 );

 static const FirebaseOptions macos = FirebaseOptions(
   apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
   appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
   iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
   iosBundleId: 'com.example.complete',
 );
}

O Firebase usa o termo “aplicativo” para se referir a uma versão específica de uma plataforma específica em um projeto do Firebase. Por exemplo, o projeto do Firebase chamado FlutterFire-ui-codelab tem vários aplicativos: um para Android, um para iOS, um para MacOS e um para Web.

O método DefaultFirebaseOptions.currentPlatform usa o tipo enumerado TargetPlatform exposto pelo Flutter para detectar a plataforma em que o app está sendo executado e retorna os valores de configuração do Firebase necessários para o aplicativo correto.

Adicionar pacotes do Firebase ao app Flutter

A etapa final de configuração é adicionar os pacotes relevantes do Firebase ao seu projeto Flutter. O arquivo firebase_options.dart precisa conter erros, porque ele depende de pacotes do Firebase que ainda não foram adicionados. No terminal, verifique se você está na raiz do projeto do Flutter em flutter-codelabs/firebase-emulator-suite/start. Em seguida, execute os três comandos a seguir:

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add firebase_ui_auth

Esses são os únicos pacotes necessários neste momento.

Inicializar o Firebase

Para usar os pacotes adicionados e o DefaultFirebaseOptions.currentPlatform,, atualize o código na função main do arquivo main.dart.

main.dart

void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(
   options: DefaultFirebaseOptions.currentPlatform,
 );


 runApp(const MyApp());
}

Esse código faz duas coisas.

  1. WidgetsFlutterBinding.ensureInitialized() informa ao Flutter que ele não deve começar a executar o código do widget do aplicativo até que o framework do Flutter seja inicializado completamente. O Firebase usa canais de plataforma nativos, que exigem que o framework esteja em execução.
  2. O Firebase.initializeApp configura uma conexão entre o app Flutter e o projeto do Firebase. O DefaultFirebaseOptions.currentPlatform é importado do arquivo firebase_options.dart gerado. Esse valor estático detecta a plataforma em que você está executando e transmite as chaves do Firebase correspondentes.

4. Adicionar a página inicial da autenticação da interface do Firebase

A interface do Firebase para Auth oferece widgets que representam telas inteiras no seu aplicativo. Essas telas processam fluxos de autenticação diferentes no seu app, como "Fazer login", "Registro", "Esqueci a senha", "Perfil do usuário" e muito mais. Para começar, adicione uma página de destino ao app que funcione como um protetor de autenticação para o aplicativo principal.

App do Material Design ou da Cupertino

A interface do FlutterFire exige que seu aplicativo seja envolvido em um MaterialApp ou CupertinoApp. Dependendo da sua escolha, a interface vai refletir automaticamente as diferenças entre os widgets do Material Design e do Cupertino. Neste codelab, use MaterialApp, que já foi adicionado ao app em app.dart.

app.dart

import 'package:flutter/material.dart';
import 'auth_gate.dart';

class MyApp extends StatelessWidget {
 const MyApp({super.key});
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: const AuthGate(),
   );
 }
}

Verificar o estado da autenticação

Antes de exibir uma tela de login, você precisa determinar se o usuário está autenticado no momento. A maneira mais comum de verificar isso é detectar o authStateChanges do FirebaseAuth usando o plug-in do Firebase Auth.

No exemplo de código acima, o MaterialApp está criando um widget AuthGate no método de build. Esse é um widget personalizado, não fornecido pela interface do FlutterFire.

Esse widget precisa ser atualizado para incluir o stream authStateChanges.

A API authStateChanges retorna um Stream com o usuário atual (se ele tiver feito login) ou nulo. Para se inscrever nesse estado no app, use o widget StreamBuilder do Flutter e transmita o stream para ele.

O StreamBuilder é um widget criado com base no snapshot mais recente de dados de um Stream que você transmite. Ele é recriado automaticamente quando o stream emite um novo snapshot.

Atualize o código em auth_gate.dart.

auth_gate.dart (link em inglês)

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [],
          );
        }

        return const HomeScreen();
      },
    );
  }
}
  • StreamBuilder.stream está sendo transmitido FirebaseAuth.instance.authStateChanged, o stream mencionado, que vai retornar um objeto User do Firebase se o usuário tiver feito a autenticação. Caso contrário, null será retornado.
  • Em seguida, o código está usando snapshot.hasData para verificar se o valor do stream contém o objeto User.
  • Se não houver, ele retornará um widget SignInScreen. No momento, essa tela não faz nada. Isso será atualizado na próxima etapa.
  • Caso contrário, ele retornará um HomeScreen, que é a parte principal do aplicativo que apenas usuários autenticados podem acessar.

O SignInScreen é um widget que vem do pacote de interface do FlutterFire. Esse será o foco da próxima etapa deste codelab. Ao executar o app nesse momento, você verá uma tela de login em branco.

5. Tela de login

O widget SignInScreen, fornecido pela FlutterFire UI, adiciona a seguinte funcionalidade:

  • Permite que os usuários façam login
  • Se os usuários esquecerem a senha, poderão tocar em "Esqueceu a senha?" e acessar um formulário para redefinir a senha.
  • Se um usuário ainda não estiver registrado, ele pode tocar em "Registrar-se" e ser direcionado para outro formulário que permite que ele se inscreva.

Novamente, isso requer apenas algumas linhas de código. Recupere o código no widget do AuthGate:

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [
              EmailAuthProvider(), // new
            ],
          );
        }

        return const HomeScreen();
      },
    );
  }
}

O widget SignInScreen e o argumento providers são o único código necessário para aproveitar todas as funcionalidades mencionadas acima. Uma tela de login com as entradas de texto "e-mail" e "senha" vai aparecer, além do botão "Fazer login".

Embora funcional, ele não tem estilo. O widget expõe parâmetros para personalizar a aparência da tela de login. Por exemplo, você pode adicionar o logotipo da sua empresa.

Personalizar a tela de login

headerBuilder

Usando o argumento SignInScreen.headerBuilder, você pode adicionar os widgets que quiser acima do formulário de login. Esse widget só aparece em telas estreitas, como dispositivos móveis. Em telas amplas, você pode usar SignInScreen.sideBuilder, que será discutido mais adiante neste codelab.

Atualize o arquivo auth_gate.dart com este código:

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('assets/flutterfire_300x.png'),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

O argumento headerBuilder requer uma função do tipo HeaderBuilder, que é definida no pacote de interface do FlutterFire.

typedef HeaderBuilder = Widget Function(
 BuildContext context,
 BoxConstraints constraints,
 double shrinkOffset,
);

Como é um callback, ele expõe valores que você pode usar, como BuildContext e BoxConstraints, e exige que você retorne um widget. O widget retornado é exibido na parte de cima da tela. Neste exemplo, o novo código adiciona uma imagem à parte de cima da tela. O aplicativo ficará assim:

73d7548d91bbd2ab.png

Criador de legendas

A tela de login expõe três parâmetros adicionais que permitem personalizar a tela: subtitleBuilder, footerBuilder e sideBuilder.

O subtitleBuilder é um pouco diferente, já que os argumentos de callback incluem uma ação, que é do tipo AuthAction. O AuthAction é um tipo enumerado que pode ser usado para detectar se a tela em que o usuário está é a de "login" ou "registro".

Atualize o código em auth_gate.dart para usar o subtitleBuilder.

auth_gate.dart (link em inglês)

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
              EmailAuthProvider()
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

Atualize o aplicativo. Ele vai ficar assim:

O argumento footerBuilder é o mesmo que subtitleBuilder. Ele não expõe BoxConstraints ou shrinkOffset, porque é destinado a texto, não a imagens. No entanto, você pode adicionar qualquer widget.

Adicione um rodapé à tela de login com este código.

auth_gate.dart (link em inglês)

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider()
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }}

Criador secundário

O argumento SignInScreen.sidebuilder aceita um callback e, desta vez, os argumentos são BuildContext e double shrinkOffset. O widget retornado pelo sideBuilder será exibido à esquerda do formulário de login e apenas em telas amplas. Isso significa que o widget só vai aparecer em apps para computador e da Web.

Internamente, a FlutterFire UI usa um ponto de interrupção para determinar se o conteúdo do cabeçalho deve ser mostrado (em telas altas, como dispositivos móveis) ou se o conteúdo lateral deve ser mostrado (em telas largas, computadores ou na Web). Especificamente, se uma tela tiver mais de 800 pixels de largura, o conteúdo do builder lateral será mostrado, mas o do cabeçalho não. Se a tela tiver menos de 800 pixels de largura, o oposto será verdadeiro.

Atualize o código em auth_gate.dart para adicionar widgets sideBuilder.

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
           sideBuilder: (context, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
         );
       }
       return const HomeScreen();
     },
   );
 }
}

Agora, o app vai ficar assim quando você expandir a largura da janela (se estiver usando o Flutter na Web ou o MacOS).

8dc60b4e5d7dd2d0.png

Criar um usuário

Neste ponto, todo o código para essa tela está pronto. No entanto, antes de fazer login, você precisa criar um usuário. Você pode fazer isso com a tela "Registrar" ou criar um usuário no console do Firebase.

Para usar o console:

  1. Acesse a tabela "Usuários" no console do Firebase.
  2. Clique aqui
  3. Selecione "flutterfire-ui-codelab" (ou outro projeto se você usou um nome diferente). Você verá esta tabela:

f038fd9a58ed60d9.png

  1. Clique no botão "Adicionar usuário".

2d78390d4c5dbbfa.png

  1. Digite um endereço de e-mail e uma senha para o novo usuário. O e-mail e a senha podem ser falsos, como inseri na imagem abaixo. Isso funcionará, mas a funcionalidade "Esqueci a senha" não funcionará se você usar um endereço de e-mail falso.

62ba0feb33d54add.png

  1. Clique em "Adicionar usuário"

32b236b3ef94d4c7.png

Agora, você pode voltar ao seu aplicativo Flutter e fazer login de um usuário pela página de login. O app vai ficar assim:

dd43d260537f3b1a.png

6. Tela de perfil

A FlutterFire UI também fornece um widget ProfileScreen, que oferece muitas funcionalidades em poucas linhas de código.

Adicionar o widget ProfileScreen

Navegue até o arquivo home.dart no seu editor de texto. Atualize com este código:

home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => const ProfileScreen(),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

O novo código de observação é o callback transmitido para IconButton.isPressed method.. Quando esse IconButton é pressionado, seu app cria uma nova rota anônima e navega até ela. Essa rota vai mostrar o widget ProfileScreen, que é retornado do callback MaterialPageRoute.builder.

Recarregue o app e toque no ícone no canto superior direito (na barra de apps). Uma página como esta será exibida:

36487fc4ab4f26a7.png

Essa é a interface padrão fornecida pela página de interface do FlutterFire. Todos os botões e campos de texto são conectados ao Firebase Auth e funcionam imediatamente. Por exemplo, é possível inserir um nome no campo de texto "Name" e a interface do FlutterFire vai chamar o método FirebaseAuth.instance.currentUser?.updateDisplayName, que o salvará no Firebase.

Como sair

No momento, se você pressionar o botão "Sair", o app não mudará. Você será desconectado, mas não será redirecionado para o widget do AuthGate. Para implementar, use o parâmetro ProfileScreen.actions.

Primeiro, atualize o código em home.dart.

home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      })
                    ],
                  ),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

Agora, ao criar uma instância de ProfileScreen, você também transmite uma lista de ações a ela para o argumento ProfileScreen.actions. Essas ações são do tipo FlutterFireUiAction. Há muitas classes diferentes que são subtipos de FlutterFireUiAction. Em geral, elas são usadas para instruir o app a reagir a diferentes mudanças no estado de autenticação. A SignedOutAction chama uma função de callback que você fornece quando o estado do Firebase Auth muda para o currentUser ser nulo.

Ao adicionar um callback que chama Navigator.of(context).pop() quando SignedOutAction é acionado, o app vai navegar para a página anterior. Neste aplicativo de exemplo, há apenas uma rota permanente, que mostra a página de login se não houver um usuário conectado, e a página inicial, se houver um. Como isso acontece quando o usuário sai da conta, o app vai mostrar a página de login.

Personalizar a página do perfil

Semelhante à página de login, a página de perfil é personalizável. Primeiro, nossa página atual não tem como voltar à página inicial quando um usuário está na página do perfil. Para corrigir isso, forneça uma AppBar ao widget ProfileScreen.

home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
 const HomeScreen({super.key});

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       actions: [
         IconButton(
           icon: const Icon(Icons.person),
           onPressed: () {
             Navigator.push(
               context,
               MaterialPageRoute<ProfileScreen>(
                 builder: (context) => ProfileScreen(
                   appBar: AppBar(
                     title: const Text('User Profile'),
                   ),
                   actions: [
                     SignedOutAction((context) {
                       Navigator.of(context).pop();
                     })
                   ],
                 ),
               ),
             );
           },
         )
       ],
       automaticallyImplyLeading: false,
     ),
     body: Center(
       child: Column(
         children: [
           Image.asset('dash.png'),
           Text(
             'Welcome!',
             style: Theme.of(context).textTheme.displaySmall,
           ),
           const SignOutButton(),
         ],
       ),
     ),
   );
 }
}

O argumento ProfileScreen.appBar aceita um widget AppBar do pacote do Flutter Material, para que ele possa ser tratado como qualquer outro AppBar criado e transmitido para um Scaffold. Neste exemplo, a funcionalidade padrão de adicionar automaticamente um botão "Voltar" é mantida, e a tela agora tem um título.

Adicionar crianças à tela de perfil

O widget ProfileScreen também tem um argumento opcional chamado "children". Esse argumento aceita uma lista de widgets, que serão colocados verticalmente dentro de um widget de coluna que já é usado internamente para criar a ProfileScreen. Esse widget de coluna no método de build ProfileScreen vai colocar os filhos que você transmite acima do botão "Sair".

Atualize o código em home.dart para mostrar o logotipo da empresa aqui, semelhante à tela de login.

home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    appBar: AppBar(
                      title: const Text('User Profile'),
                    ),
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      })
                    ],
                    children: [
                      const Divider(),
                      Padding(
                        padding: const EdgeInsets.all(2),
                        child: AspectRatio(
                          aspectRatio: 1,
                          child: Image.asset('flutterfire_300x.png'),
                        ),
                      ),
                    ],
                  ),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

Atualize o app para ver esta tela:

ebe5792b765dbf87.png

7. Login no Google Auth multiplataforma

A interface do FlutterFire também oferece widgets e funcionalidades para autenticação com provedores de terceiros, como Google, Twitter, Facebook, Apple e GitHub.

Para integrar com a autenticação do Google, instale o plug-in oficial firebase_ui_oauth_google e as dependências dele, que processarão o fluxo de autenticação nativa. No terminal, navegue até a raiz do seu projeto do Flutter e digite o seguinte comando:

flutter pub add google_sign_in
flutter pub add firebase_ui_oauth_google

Ativar o provedor de login do Google

Em seguida, ative o provedor do Google no Console do Firebase:

  1. Navegue até a tela Provedores de login de autenticação no console.
  2. Clique em "Adicionar novo provedor". 8286fb28be94bf30.png
  3. Selecione "Google". c4e28e6f4974be7f.png
  4. Ative a opção "Ativar" e pressione "Salvar". e74ff86990763826.png
  5. Se aparecer um modal com informações sobre o download de arquivos de configuração, clique em "Concluído".
  6. Confirme se o provedor de login do Google foi adicionado. 5329ce0543c90d95.png

Adicionar botão de Login do Google

Com o recurso ativado, adicione o widget necessário para mostrar um botão estilizado do Login do Google na página de login. Navegue até o arquivo auth_gate.dart e atualize o código para o seguinte:

auth_gate.dart (link em inglês)

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:firebase_ui_oauth_google/firebase_ui_oauth_google.dart'; // new
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
             GoogleProvider(clientId: "YOUR_WEBCLIENT_ID"),  // new
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
           sideBuilder: (context, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

O único código novo aqui é a adição de GoogleProvider(clientId: "YOUR_WEBCLIENT_ID") à configuração do widget SignInScreen.

Com isso adicionado, atualize o aplicativo e você verá um botão de Login do Google.

aca71a46a011bfb5.png

Configurar o botão de login

O botão não funciona sem configurações adicionais. Se você estiver desenvolvendo com o Flutter Web, essa é a única etapa que precisa ser adicionada para que isso funcione. Outras plataformas exigem etapas adicionais, que serão discutidas mais adiante.

  1. Acesse a página "Provedores de autenticação" no Console do Firebase.
  2. Clique no provedor do Google. 9b3a325c5eca6e49.png
  3. Clique no painel de expansão "Configuração do SDK da Web".
  4. Copie o valor de "ID do cliente da Web" 711a79f0d931c60f.png
  5. Volte ao editor de texto e atualize a instância de GoogleProvider no arquivo auth_gate.dart transmitindo esse ID para o parâmetro chamado clientId.
GoogleProvider(
   clientId: "YOUR_WEBCLIENT_ID"
)

Depois que o ID do cliente da Web for inserido, recarregue o app. Quando você pressionar o botão "Fazer login com o Google", uma nova janela vai aparecer (se você estiver usando a Web) que vai orientar você pelo fluxo de login do Google. Inicialmente, ele terá esta aparência:

14e73e3c9de704bb.png

Configurar o iOS

Para que isso funcione no iOS, há um processo de configuração adicional.

  1. Navegue até a tela "Configurações do projeto" no Console do Firebase. Será exibido um card listando seus apps do Firebase, como este: fefa674acbf213cc.png
  2. Clique no iOS. O nome do seu aplicativo será diferente do meu. Quando o meu diz "concluir", o seu vai dizer "iniciar", se você usou o projeto flutter-codelabs/firebase-auth-flutterfire-ui/start para acompanhar este codelab.
  3. Clique no botão "GoogleServices-Info.plist" para fazer o download do arquivo de configuração necessário. f89b3192871dfbe3.png
  4. Arraste e solte o arquivo baixado para o diretório chamado ./ios/Runner no seu projeto do Flutter.
  5. Abra o Xcode executando o seguinte comando do terminal na raiz do seu projeto: open ios/Runner.xcworkspace
  6. Clique com o botão direito do mouse no diretório do Runner e selecione Adicionar arquivos ao "Runner". 858986063a4c5201.png
  7. Selecione GoogleService-Info.plist no gerenciador de arquivos.
  8. No editor de texto (que não é o Xcode), adicione os atributos CFBundleURLTypes abaixo ao arquivo [my_project]/ios/Runner/Info.plist.
<!-- Put me in the [my_project]/ios/Runner/Info.plist file -->
<!-- Google Sign-in Section -->
<key>CFBundleURLTypes</key>
<array>
        <dict>
                <key>CFBundleTypeRole</key>
                <string>Editor</string>
                <key>CFBundleURLSchemes</key>
                <array>
                        <!-- TODO Replace this value: -->
                        <!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->
                        <string>com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn</string>
                </array>
        </dict>
</array>
<!-- End of the Google Sign-in Section -->
  1. É necessário substituir o GoogleProvider.clientId adicionado na configuração da Web pelo ID do cliente associado ao seu ID do cliente do Firebase iOS. Primeiro, você pode encontrar esse ID no arquivo firebase_options.dart, como parte da constante iOS. Copie o valor transmitido para iOSClientId.
static const FirebaseOptions ios = FirebaseOptions(
  apiKey: 'YOUR API KEY',
  appId: 'YOUR APP ID',
  messagingSenderId: '',
  projectId: 'PROJECT_ID',
  storageBucket: 'PROJECT_ID.firebasestorage.app',
  iosClientId: 'IOS CLIENT ID', // Find your iOS client Id here.
  iosBundleId: 'com.example.BUNDLE',
);
  1. Cole esse valor no argumento GoogleProvider.clientId do widget AuthGate.
import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:firebase_ui_oauth_google/firebase_ui_oauth_google.dart';
import 'package:flutter/material.dart';

import 'home.dart';


class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
             GoogleProvider(clientId: "YOUR IOS CLIENT ID"),  // replace String
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
           sideBuilder: (context, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

Se o app do Flutter já estiver em execução no iOS, será necessário desativá-lo completamente e executar o aplicativo novamente. Caso contrário, execute o app no iOS.

8. Parabéns!

Você concluiu o codelab da interface do Firebase Auth para Flutter. O código completo deste codelab está disponível no diretório "complete" no GitHub: Flutter Codelabs.

O que vimos

  • Configurar um app Flutter para usar o Firebase
  • Como configurar um projeto do Firebase no Console do Firebase
  • CLI do FlutterFire
  • CLI do Firebase
  • Como usar o Firebase Authentication
  • Como usar a interface do FlutterFire para processar a autenticação do Firebase no seu app do Flutter facilmente

Próximas etapas

Saiba mais

O Sparky está aqui para comemorar com você!

2a0ad195769368b1.gif