O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Conheça o Firebase para Flutter

Neste codelab, você vai aprender alguns dos princípios básicos de Firebase para criar aplicativos móveis da vibração para Android e iOS.

Pré-requisitos

Este codelab pressupõe que está familiarizado com vibração, e você tiver instalado o Flutter SDK , e um editor .

O que você vai criar

Neste codelab, você criará um RSVP de evento e um aplicativo de bate-papo de livro de visitas no Android, iOS, Web e macOS usando o Flutter. Você autenticará usuários com Firebase Authentication e sincronizará dados usando o Cloud Firestore.

O que você precisará

Você pode executar este codelab usando qualquer um dos seguintes dispositivos:

Além dos itens acima, você também precisará:

  • Um navegador de sua escolha, como o Chrome.
  • Um IDE ou editor de texto de sua escolha, tais como Android estúdio ou Código VS configurado com os plugins de dardo e vibração.
  • O mais recente stable versão do Flutter (ou beta se você gosta de viver no limite).
  • Uma conta do Google, como uma conta do gmail, para criar e gerenciar seu projeto Firebase.
  • O código de amostra do codelab. Veja a próxima etapa para obter o código.

Vamos começar baixando a versão inicial do nosso projeto no GitHub.

Clonar o repositório GitHub a partir da linha de comando:

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

Alternativamente, se você tem cli do GitHub ferramenta instalada:

gh repo clone flutter/codelabs flutter-codelabs

O código de amostra deve ser clonado no flutter-codelabs directório, o qual contém o código de um conjunto de codelabs. O código para esta codelab está em flutter-codelabs/firebase-get-to-know-flutter .

A estrutura de diretório sob flutter-codelabs/firebase-get-to-know-flutter é uma série de instantâneos de onde você deve ser no final de cada etapa nomeado. Esta é a Etapa 2, portanto, localizar os arquivos correspondentes é tão fácil quanto:

cd flutter-codelabs/firebase-get-to-know-flutter/step_02

Se você quiser pular para a frente ou ver como algo deve ficar após uma etapa, procure no diretório com o nome da etapa em que está interessado.

Importe o aplicativo inicial

Abrir ou importar os flutter-codelabs/firebase-get-to-know-flutter/step_02 diretório em seu IDE preferido. Este diretório contém o código inicial para o codelab que consiste em um aplicativo Flutter Meetup ainda não funcional.

Localize os arquivos para trabalhar

O código neste aplicativo é distribuído por vários diretórios. Essa divisão de funcionalidade foi projetada para facilitar o trabalho, agrupando o código por funcionalidade.

Localize os seguintes arquivos no projeto:

  • lib/main.dart : Este arquivo contém o principal ponto de entrada eo widget do aplicativo.
  • lib/src/widgets.dart : Este arquivo contém um punhado de widgets para ajudar a padronizar o estilo da aplicação. Eles são usados ​​para compor a tela do aplicativo inicial.
  • lib/src/authentication.dart : Este arquivo contém uma implementação parcial do FirebaseUI Auth com um conjunto de widgets para criar uma experiência de usuário de login para autenticação baseada Firebase e-mail. Esses widgets para o fluxo de autenticação ainda não são usados ​​no aplicativo inicial, mas você os conectará em breve.

Você adicionará arquivos adicionais conforme necessário para construir o resto do aplicativo.

Revendo o lib/main.dart arquivo

Este aplicativo aproveita a google_fonts pacote para nos permitir fazer Roboto a fonte padrão em todo o aplicativo. Um exercício para o leitor motivado é explorar fonts.google.com e usar as fontes que você descobrir que existe em diferentes partes do aplicativo.

Você está utilizando os widgets auxiliares de lib/src/widgets.dart na forma de Header , Paragraph e IconAndDetail . Esses widgets reduzir a desordem no layout da página descrita em HomePage , eliminando código duplicado. Isso tem o benefício adicional de permitir uma aparência consistente.

Esta é a aparência do seu aplicativo no Android, iOS, Web e macOS:

Visualização do aplicativo

Exibir as informações do evento é ótimo para seus convidados, mas apenas mostrar os eventos não é muito útil para ninguém. Vamos adicionar algumas funcionalidades dinâmicas a este aplicativo. Para isso, você precisa conectar o Firebase ao seu aplicativo. Para começar a usar o Firebase, você precisará criar e configurar um projeto do Firebase.

Crie um projeto Firebase

  1. Entrar no Firebase .
  2. No console Firebase, clique em Add Project (ou Criar um projeto), eo nome do seu Firebase projeto Firebase-Flutter-Codelab.

4395e4e67c08043a.png

  1. Clique nas opções de criação do projeto. Aceite os termos do Firebase, se solicitado. Pule a configuração do Google Analytics, porque você não usará o Analytics para este aplicativo.

b7138cde5f2c7b61.png

Para saber mais sobre os projetos Firebase, consulte Compreender projectos Firebase .

O aplicativo que você está construindo usa vários produtos Firebase que estão disponíveis para aplicativos da web:

  • Firebase autenticação para permitir que seus usuários para fazer login no seu aplicativo.
  • Nuvem Firestore para salvar dados estruturados na nuvem e obter notificação instantânea quando alterações de dados.
  • Regras de Segurança Firebase para garantir a sua base de dados.

Alguns desses produtos precisam de configuração especial ou precisam ser ativados usando o console do Firebase.

Ativar e-mail de início de sessão para autenticação Firebase

Para permitir que os usuários assinem na aplicação web, você vai usar o sign-in E-mail / senha método para este codelab:

  1. No console Firebase, expanda o menu Build no painel esquerdo.
  2. Clique em Autenticação e clique no botão começar, em seguida, a guia método Sign-in (ou clique aqui para ir diretamente para o Sign-in guia método).
  3. Clique E-mail / senha na lista de Sign-in fornecedores, coloque o interruptor para a posição Ativar, e depois clique em Salvar. 58e3e3e23c2f16a4.png

Ativar Cloud Firestore

O aplicativo da Web usa Nuvem Firestore para guardar as mensagens de bate-papo e receber novas mensagens de chat.

Ative o Cloud Firestore:

  1. Na seção de construção do o Firebase console, clique Nuvem Firestore.
  2. Clique em Criar banco de dados. 99e8429832d23fa3.png
  1. Selecione a opção Iniciar o modo de teste no. Leia a isenção de responsabilidade sobre as regras de segurança. O modo de teste garante que você possa gravar livremente no banco de dados durante o desenvolvimento. Clique em Avançar. 6be00e26c72ea032.png
  1. Selecione o local para o seu banco de dados (você pode apenas usar o padrão). Observe que este local não pode ser alterado posteriormente. 278656eefcfb0216.png
  2. Clique em Ativar.

Para usar o Firebase com o Flutter, você precisa seguir um processo para configurar o projeto do Flutter para utilizar as bibliotecas do FlutterFire corretamente:

  • Adicionar as dependências FlutterFire para pubspec.yaml
  • Registre a plataforma desejada no projeto Firebase
  • Baixe o arquivo de configuração específico da plataforma e adicione-o ao código.

No diretório de nível superior do seu aplicativo Flutter, existem subdiretórios chamados ios e android . Esses diretórios contêm os arquivos de configuração específicos da plataforma para iOS e Android, respectivamente.

Configurar dependências

Você precisa adicionar as bibliotecas FlutterFire para os dois produtos Firebase que está utilizando neste aplicativo - Firebase Auth e Cloud Firestore. Editar pubspec.yaml e adicione as seguintes dependências:

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  cloud_firestore: ^1.0.0 # new
  firebase_auth: ^1.0.0   # new
  google_fonts: ^2.0.0
  provider: ^5.0.0        # new

Embora tenha adicionado os pacotes necessários, você também precisa configurar os projetos iOS, Android, macOS e Web runner para utilizar o Firebase de maneira adequada. Você também está usando o provider pacote que irá permitir a separação da lógica de negócios da lógica de exibição.

Configurar iOS

  1. Na consola Firebase , selecione Visão geral do projeto na barra de navegação à esquerda, e clique no botão iOS sob Comece adicionando Firebase ao seu aplicativo.

Você deverá ver a seguinte caixa de diálogo:

c42139f18fb9a2ee.png

  1. O valor importante para fornecer é o iOS pacote ID. Você obtém o ID do pacote executando as próximas três etapas.
  1. Na ferramenta de linha de comando, vá para o diretório de nível superior do seu aplicativo Flutter.
  2. Execute o comando open ios/Runner.xcworkspace para abrir o Xcode.
  1. No Xcode, clique no corredor de nível superior no painel esquerdo, selecione Runner sob Targets, para mostrar a guia Geral no painel da direita, como mostrado. Copie o valor Bundle Identificador.

9d67acd88c718763.png

  1. Volte para o diálogo Firebase, colar o identificador do pacote copiado para o campo ID de pacote iOS e clique em Register App.
  1. Continuando Firebase, siga as instruções para baixar o arquivo de configuração GoogleService-Info.plist .
  2. Volte para o Xcode. Note-se que tem um corredor subpasta também chamado corredor (mostrada na imagem anterior).
  3. Arraste o GoogleService-Info.plist arquivo (que você acabou de baixar) em que Runner subpasta.
  4. Na caixa de diálogo que aparece no Xcode, clique em Concluir.
  5. Sinta-se à vontade para fechar o Xcode neste momento, pois ele não será necessário no futuro.
  6. Volte para o console do Firebase. Na etapa de configuração, clique em Avançar, ignore as etapas restantes, e voltar para a página principal do console Firebase.

Você acabou de configurar seu aplicativo Flutter para iOS. Para mais detalhes, consulte a documentação de instalação FlutterFire iOS .

Configurar Android

  1. No Console Firebase , selecione Visão geral do projeto na barra de navegação à esquerda, e clique no botão Android sob Comece adicionando Firebase ao seu aplicativo.

Você verá a seguinte caixa de diálogo: 8254fc299e82f528.png

  1. O valor importante para fornecer é o nome do pacote Android. Você obtém o nome do pacote ao realizar as duas etapas a seguir:
  1. Em seu diretório app Flutter, abra o arquivo android/app/src/main/AndroidManifest.xml .
  2. No manifest elemento, encontrar o valor da cadeia do package atributo. Este valor é o nome do pacote Android (algo como com.yourcompany.yourproject ). Copie este valor.
  3. Na caixa de diálogo Firebase, cole o nome do pacote copiado para o campo nome do pacote Android.
  4. Você não precisa de depuração certificado de assinatura SHA-1 para este codelab. Deixe em branco.
  5. Clique Register App.
  6. Continuando Firebase, siga as instruções para baixar o arquivo de configuração do google-services.json .
  7. Vá para o seu diretório app Flutter, e mova o google-services.json arquivo (que você acabou de baixar) na android/app diretório.
  8. De volta ao console do Firebase, pule as etapas restantes e volte para a página principal do console do Firebase.
  9. Edite seu android/build.gradle para adicionar os google-services plug-in dependência:

android / build.gradle

dependencies {
        classpath 'com.android.tools.build:gradle:4.1.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.3.5'  // new
}
  1. Editar seu android/app/build.gradle para permitir que os google-services de plugin:

android / app / build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
apply plugin: 'com.google.gms.google-services'  // new
  1. O Firebase requer que o Multidex esteja ativado e uma maneira de fazer isso é definir o SDK mínimo compatível como 21 ou superior. Editar o seu android/app/build.gradle para atualizar minSdkVersion :

android / app / build.gradle

defaultConfig {
    applicationId "com.example.gtk_flutter"
    minSdkVersion 21  // Updated
    targetSdkVersion 30
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}

Você acabou de configurar seu aplicativo Flutter para Android. Para mais detalhes, consulte a documentação de instalação FlutterFire Android .

Configurar para a Web

  1. No Console Firebase , selecione Visão geral do projeto na barra de navegação à esquerda, e clique no botão Web sob Comece adicionando Firebase ao seu aplicativo.

25b14deff9e589ce.png

  1. Dê este aplicativo um apelido, e clique no botão aplicativo Register. Vamos deixar o Firebase Hosting desligado para este tutorial, pois só o estaremos executando localmente. Sinta-se à vontade para ler mais sobre o Firebase Hosting aqui. 9c697cc1b309c806.png
  2. Editar a seção do corpo do seu web/index.html arquivo como segue. Certifique-se de adicionar os firebaseConfig dados da etapa anterior.

web / index.html

<body>
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('flutter-first-frame', function () {
        navigator.serviceWorker.register('flutter_service_worker.js');
      });
    }
  </script>

  <!-- Add from here -->
  <script src="https://www.gstatic.com/firebasejs/7.20.0/firebase-app.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.20.0/firebase-auth.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.20.0/firebase-firestore.js"></script>
  <script>
    // Your web app's Firebase configuration
    var firebaseConfig = {
      // Replace this with your firebaseConfig
    };
    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);
  </script>
  <!-- to here. -->

  <script src="main.dart.js" type="application/javascript"></script>
</body>

Você acabou de configurar seu aplicativo Flutter para a web. Para mais detalhes, consulte a documentação de instalação FlutterFire Web .

Configure o macOS

As etapas de configuração do macOS são quase idênticas às do iOS. Vamos re-uso arquivo de configuração GoogleService-Info.plist do iOS passos acima.

  1. Execute o comando open macos/Runner.xcworkspace para abrir o Xcode.
  1. Arraste o GoogleService-Info.plist arquivo para a subpasta Runner. Isto foi criado nas etapas Configure iOS acima. c2b9229a605fd738.png
  2. No macos/Runner/DebugProfile.entitlements arquivo, adicione um com.apple.security.network.client direito, e configurá-lo para true . 8bee5665e35d3f34.png
  3. No macos/Runner/Release.entitlements arquivo, também adicionar um com.apple.security.network.client direito, e configurá-lo para true . 41e2e23b7928546a.png
  4. Sinta-se à vontade para fechar o Xcode neste momento, pois ele não será necessário no futuro.

Você concluiu a configuração do aplicativo Flutter para macOS. Para mais detalhes, consulte a documentação de instalação FlutterFire MacOS , eo suporte do Google Desktop para Flutter página.

Agora que você adicionou Firebase para o aplicativo, você pode configurar um botão de RSVP que registra pessoas que usam autenticação Firebase . Para Android nativo, iOS nativo e Web, existem pacotes FirebaseUI Auth pré-construídos, mas para Flutter, você precisará construir esse recurso.

O projeto que você recuperou na Etapa 2 incluiu um conjunto de widgets que implementa a interface do usuário para a maior parte do fluxo de autenticação. Você implementará a lógica de negócios para integrar o Firebase Authentication ao aplicativo.

Lógica de Negócios com Provedor

Você está indo para usar o provider pacote para fazer um objeto de estado aplicação centralizada disponível em toda a árvore de widgets da vibração do aplicativo. Para começar, modificar as importações no topo da lib/main.dart :

lib / main.dart

import 'package:firebase_core/firebase_core.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';           // new

import 'src/authentication.dart';                  // new
import 'src/widgets.dart';

Os import linhas introduzir Firebase Core e Auth, tração no provider pacote que você está usando para fazer o objeto de estado de aplicação disponível através da árvore widget, e incluir os widgets de autenticação de lib/src .

Este estado de objeto de aplicação, ApplicationState , tem duas responsabilidades principais para esta etapa, mas vai ganhar responsabilidades adicionais como você adicionar mais recursos para a aplicação em etapas posteriores. A primeira responsabilidade é inicializar a biblioteca Firebase com uma chamada para Firebase.initializeApp() , e depois há a manipulação do fluxo de autorização. Adicionar a seguinte classe ao final do lib/main.dart :

lib / main.dart

class ApplicationState extends ChangeNotifier {
  ApplicationState() {
    init();
  }

  Future<void> init() async {
    await Firebase.initializeApp();

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
      } else {
        _loginState = ApplicationLoginState.loggedOut;
      }
      notifyListeners();
    });
  }

  ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
  ApplicationLoginState get loginState => _loginState;

  String? _email;
  String? get email => _email;

  void startLoginFlow() {
    _loginState = ApplicationLoginState.emailAddress;
    notifyListeners();
  }

  void verifyEmail(
    String email,
    void Function(FirebaseAuthException e) errorCallback,
  ) async {
    try {
      var methods =
          await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
      if (methods.contains('password')) {
        _loginState = ApplicationLoginState.password;
      } else {
        _loginState = ApplicationLoginState.register;
      }
      _email = email;
      notifyListeners();
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void signInWithEmailAndPassword(
    String email,
    String password,
    void Function(FirebaseAuthException e) errorCallback,
  ) async {
    try {
      await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void cancelRegistration() {
    _loginState = ApplicationLoginState.emailAddress;
    notifyListeners();
  }

  void registerAccount(String email, String displayName, String password,
      void Function(FirebaseAuthException e) errorCallback) async {
    try {
      var credential = await FirebaseAuth.instance
          .createUserWithEmailAndPassword(email: email, password: password);
      await credential.user!.updateProfile(displayName: displayName);
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void signOut() {
    FirebaseAuth.instance.signOut();
  }
}

É importante notar alguns pontos-chave nesta aula. O usuário começa sem autenticação, o aplicativo mostra um formulário solicitando o endereço de e-mail do usuário, dependendo se esse endereço de e-mail está registrado, o aplicativo solicitará o registro do usuário ou solicitará sua senha e, então, assumindo que tudo deu certo, o usuário é autenticado.

Deve-se observar que esta não é uma implementação completa do fluxo FirebaseUI Auth, pois não lida com o caso de um usuário com uma conta existente que está tendo problemas para fazer login. A implementação desse recurso adicional é deixada como um exercício para o leitor motivado.

Integrando o fluxo de autenticação

Agora que você tem o início do estado do aplicativo é hora de ligar o estado do aplicativo na inicialização do aplicativo e adicionar o fluxo de autenticação em HomePage . Atualize o ponto de entrada principal para integrar o estado do aplicativo através do provider pacote:

lib / main.dart

void main() {
  // Modify from here
  runApp(
    ChangeNotifierProvider(
      create: (context) => ApplicationState(),
      builder: (context, _) => App(),
    ),
  );
  // to here.
}

A modificação para o main função faz com que o pacote de provedor responsável por instanciar o objeto de estado aplicativo usando o ChangeNotifierProvider widget. Você está usando esta classe de provedor específico porque o objeto de estado aplicação estende ChangeNotifier e isso permite que o provider pacote para saber quando para exibir novamente os widgets dependentes. Finalmente, integrar o estado da aplicação com Authentication por meio da atualização HomePage s' build método:

lib / main.dart

class HomePage extends StatelessWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Firebase Meetup'),
      ),
      body: ListView(
        children: <Widget>[
          Image.asset('assets/codelab.png'),
          SizedBox(height: 8),
          IconAndDetail(Icons.calendar_today, 'October 30'),
          IconAndDetail(Icons.location_city, 'San Francisco'),
          // Add from here
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Authentication(
              email: appState.email,
              loginState: appState.loginState,
              startLoginFlow: appState.startLoginFlow,
              verifyEmail: appState.verifyEmail,
              signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
              cancelRegistration: appState.cancelRegistration,
              registerAccount: appState.registerAccount,
              signOut: appState.signOut,
            ),
          ),
          // to here
          Divider(
            height: 8,
            thickness: 1,
            indent: 8,
            endIndent: 8,
            color: Colors.grey,
          ),
          Header("What we'll be doing"),
          Paragraph(
            'Join us for a day full of Firebase Workshops and Pizza!',
          ),
        ],
      ),
    );
  }
}

Você instanciar a Authentication widget, e envolvê-la em um Consumer widget. O Consumer widgets da maneira usual que o provider pacote pode ser usado para reconstruir parte da árvore quando o estado de aplicação mudanças. A Authentication widget é a interface do usuário de autenticação que você irá agora teste.

Testando o fluxo de autenticação

cdf2d25e436bd48d.png

Aqui está o início do fluxo de autenticação, onde o usuário pode tocar no botão RSVP para iniciar o formulário de e-mail.

2a2cd6d69d172369.png

Ao inserir o e-mail, o sistema confirma se o usuário já está cadastrado, caso em que é solicitada a senha do usuário, ou se o usuário não estiver cadastrado, eles passam pelo formulário de cadastro.

e5e65065dba36b54.png

Certifique-se de tentar inserir uma senha curta (menos de seis caracteres) para verificar o fluxo de tratamento de erros. Se o usuário estiver registrado, ele verá a senha de.

fbb3ea35fb4f67a.png

Nesta página, certifique-se de inserir as senhas incorretas para verificar o tratamento de erros nesta página. Finalmente, uma vez que o usuário esteja logado, você verá a experiência de logon que oferece ao usuário a capacidade de sair novamente.

4ed811a25b0cf816.png

E com isso, você implementou um fluxo de autenticação. Parabéns!

Saber que os usuários estão chegando é ótimo, mas vamos dar aos convidados algo mais para fazer no aplicativo. E se eles pudessem deixar mensagens em um livro de visitas? Eles podem compartilhar por que estão ansiosos para vir ou quem eles esperam encontrar.

Para armazenar as mensagens de bate-papo que os usuários escrevem no app, você vai usar Nuvem Firestore .

Modelo de dados

O Cloud Firestore é um banco de dados NoSQL e os dados armazenados no banco de dados são divididos em coleções, documentos, campos e subcoleções. Você irá armazenar cada mensagem do chat como um documento em uma coleção de nível superior chamado guestbook .

7c20dc8424bb1d84.png

Adicionar mensagens ao Firestore

Nesta seção, você adicionará a funcionalidade para que os usuários gravem novas mensagens no banco de dados. Primeiro, você adiciona os elementos da IU (campo de formulário e botão de envio) e, em seguida, adiciona o código que conecta esses elementos ao banco de dados.

Primeiro, adicione as importações para a cloud_firestore pacote e dart:async .

lib / main.dart

import 'dart:async';                                    // new
import 'package:cloud_firestore/cloud_firestore.dart';  // new
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';

import 'src/authentication.dart';
import 'src/widgets.dart';

Para construir os elementos de interface do usuário de um campo de mensagem e um botão de envio, adicionar um novo stateful Widget GuestBook na parte inferior da lib/main.dart .

lib / main.dart

class GuestBook extends StatefulWidget {
  GuestBook({required this.addMessage});
  final FutureOr<void> Function(String message) addMessage;

  @override
  _GuestBookState createState() => _GuestBookState();
}

class _GuestBookState extends State<GuestBook> {
  final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
  final _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Form(
        key: _formKey,
        child: Row(
          children: [
            Expanded(
              child: TextFormField(
                controller: _controller,
                decoration: const InputDecoration(
                  hintText: 'Leave a message',
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Enter your message to continue';
                  }
                  return null;
                },
              ),
            ),
            SizedBox(width: 8),
            StyledButton(
              onPressed: () async {
                if (_formKey.currentState!.validate()) {
                  await widget.addMessage(_controller.text);
                  _controller.clear();
                }
              },
              child: Row(
                children: [
                  Icon(Icons.send),
                  SizedBox(width: 4),
                  Text('SEND'),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Existem alguns pontos de interesse aqui. Primeiro, você está instanciando um Form para que upi possa validar se a mensagem realmente tem algum conteúdo e mostrar ao usuário uma mensagem de erro se não houver nenhum. A maneira de validar um formulário envolve acessando o estado forma por trás da forma, e para isso você usar um GlobalKey . Para mais informações sobre Keys, e como usá-los, consulte o Flutter Widgets 101 episódio "Quando usar as teclas" .

Além disso, observe a maneira como os widgets são colocados para fora, você tem uma Row , com uma TextFormField e uma StyledButton , que em si contém uma Row . Observe também o TextFormField é envolto em um Expanded widget, isso força o TextFormField para ocupar qualquer espaço extra na linha. Para entender melhor por que isso é necessário, por favor leia as restrições Compreensão .

Agora que você tem um widget que permite ao usuário inserir algum texto para adicionar ao Livro de visitas, você precisa colocá-lo na tela. Para fazer isso, editar o corpo de HomePage para adicionar as duas linhas seguintes na parte inferior do ListView crianças 's:

Header("What we'll be doing"),
Paragraph(
  'Join us for a day full of Firebase Workshops and Pizza!',
),
// Add the following two lines.
Header('Discussion'),
GuestBook(addMessage: (String message) => print(message)),

Embora isso seja suficiente para exibir o widget, não é suficiente para fazer nada útil. Você irá atualizar este código em breve para torná-lo funcional.

Visualização do aplicativo

Um usuário clicar no botão ENVIAR irá acionar o trecho de código abaixo. Acrescenta o conteúdo do campo de entrada de mensagem para o guestbook coleção de banco de dados. Especificamente, o addMessageToGuestBook método adiciona o conteúdo da mensagem para um novo documento (com um ID gerado automaticamente) ao guestbook coleção.

Note-se que FirebaseAuth.instance.currentUser.uid é uma referência para a identificação única gerada automaticamente que a autenticação Firebase dá para todos os usuários conectados.

Fazer outra alteração para o lib/main.dart arquivo. Adicione o addMessageToGuestBook método. Você conectará a interface do usuário e esse recurso na próxima etapa.

lib / main.dart

class ApplicationState extends ChangeNotifier {

  // Current content of ApplicationState elided ...

  // Add from here
  Future<DocumentReference> addMessageToGuestBook(String message) {
    if (_loginState != ApplicationLoginState.loggedIn) {
      throw Exception('Must be logged in');
    }

    return FirebaseFirestore.instance.collection('guestbook').add(<String, dynamic>{
      'text': message,
      'timestamp': DateTime.now().millisecondsSinceEpoch,
      'name': FirebaseAuth.instance.currentUser!.displayName,
      'userId': FirebaseAuth.instance.currentUser!.uid,
    });
  }
  // To here
}

Conectando a IU ao banco de dados

Você tem uma IU em que o usuário pode inserir o texto que deseja adicionar ao livro de visitas e o código para adicionar a entrada ao Cloud Firestore. Agora tudo o que você precisa fazer é conectar os dois. Em lib/main.dart faça a seguinte alteração à HomePage widget.

lib / main.dart

class HomePage extends StatelessWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Firebase Meetup'),
      ),
      body: ListView(
        children: <Widget>[
          Image.asset('assets/codelab.png'),
          SizedBox(height: 8),
          IconAndDetail(Icons.calendar_today, 'October 30'),
          IconAndDetail(Icons.location_city, 'San Francisco'),
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Authentication(
              email: appState.email,
              loginState: appState.loginState,
              startLoginFlow: appState.startLoginFlow,
              verifyEmail: appState.verifyEmail,
              signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
              cancelRegistration: appState.cancelRegistration,
              registerAccount: appState.registerAccount,
              signOut: appState.signOut,
            ),
          ),
          Divider(
            height: 8,
            thickness: 1,
            indent: 8,
            endIndent: 8,
            color: Colors.grey,
          ),
          Header("What we'll be doing"),
          Paragraph(
            'Join us for a day full of Firebase Workshops and Pizza!',
          ),
          // Modify from here
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (appState.loginState == ApplicationLoginState.loggedIn) ...[
                  Header('Discussion'),
                  GuestBook(
                    addMessage: (String message) =>
                        appState.addMessageToGuestBook(message),
                  ),
                ],
              ],
            ),
          ),
          // To here.
        ],
      ),
    );
  }
}

Você substituiu as duas linhas adicionadas no início desta etapa pela implementação completa. Você está novamente usando Consumer<ApplicationState> para tornar o estado do aplicativo disponível para a parte da árvore que você está prestando. Isso permite que você reaja a alguém inserindo uma mensagem na IU e publique-a no banco de dados. Na próxima seção, você testará se as mensagens adicionadas são publicadas no banco de dados.

Teste o envio de mensagens

  1. Certifique-se de que você esteja conectado ao aplicativo.
  2. Digite uma mensagem como "Hey lá!", E clique em Enviar.

Esta ação grava a mensagem em seu banco de dados do Cloud Firestore. No entanto, você ainda não verá a mensagem em seu aplicativo Flutter real porque ainda precisa implementar a recuperação dos dados. Você fará isso na próxima etapa.

Mas você pode ver a mensagem recém-adicionada no console do Firebase.

No console Firebase, no painel de banco de dados , você deve ver o guestbook coleção com a sua mensagem recém-adicionado. Se você continuar enviando mensagens, sua coleção de livros de visitas conterá muitos documentos, como este:

Console do Firebase

713870af0b3b63c.png

É ótimo que os convidados possam escrever mensagens no banco de dados, mas ainda não as possam ver no aplicativo. Vamos consertar isso!

Sincronizar mensagens

Para exibir mensagens, você precisará adicionar ouvintes que são acionados quando os dados são alterados e, em seguida, criar um elemento de IU que mostra novas mensagens. Você adicionará código ao estado do aplicativo que escuta as mensagens recém-adicionadas do aplicativo.

Logo acima da GuestBook widgets a seguinte classe de valor. Esta classe expõe uma visão estruturada dos dados que você está armazenando no Cloud Firestore.

lib / main.dart

class GuestBookMessage {
  GuestBookMessage({required this.name, required this.message});
  final String name;
  final String message;
}

Na seção de ApplicationState onde você define Estado e getters, adicione as seguintes novas linhas:

lib / main.dart

  ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
  ApplicationLoginState get loginState => _loginState;

  String? _email;
  String? get email => _email;

  // Add from here
  StreamSubscription<QuerySnapshot>? _guestBookSubscription;
  List<GuestBookMessage> _guestBookMessages = [];
  List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
  // to here.

E, finalmente, na seção de inicialização de ApplicationState , adicione o seguinte para inscrever-se a uma consulta sobre a coleção de documentos quando um usuário se conecta, e unsubscribe quando eles log out.

lib / main.dart

  Future<void> init() async {
    await Firebase.initializeApp();

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        // Add from here
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          snapshot.docs.forEach((document) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'],
                message: document.data()['text'],
              ),
            );
          });
          notifyListeners();
        });
        // to here.
      } else {
        _loginState = ApplicationLoginState.loggedOut;
        // Add from here
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        // to here.
      }
      notifyListeners();
    });
  }

Esta seção é importante, porque aqui é onde você construir uma consulta sobre o guestbook coleção, e pega em subscrever e cancelar a esta coleção. Você ouvir o riacho, onde você reconstruir uma cache local das mensagens no guestbook coleção, e também armazenar uma referência a essa assinatura para que você pode cancelar sua inscrição a partir dele mais tarde. Há muita coisa acontecendo aqui e vale a pena gastar algum tempo em um depurador inspecionando o que acontece quando se obtém um modelo mental mais claro.

Para mais informações, consulte a documentação Nuvem Firestore .

No GuestBook Widget você precisa conectar esse estado mudar para a interface do usuário. Você modifica o widget adicionando uma lista de mensagens como parte de sua configuração.

lib / main.dart

class GuestBook extends StatefulWidget {
  // Modify the following line
  GuestBook({required this.addMessage, required this.messages});
  final FutureOr<void> Function(String message) addMessage;
  final List<GuestBookMessage> messages; // new

  @override
  _GuestBookState createState() => _GuestBookState();
}

Em seguida, vamos expor essa nova configuração em _GuestBookState modificando a build método da seguinte forma.

lib / main.dart

class _GuestBookState extends State<GuestBook> {
  final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
  final _controller = TextEditingController();

  @override
  // Modify from here
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // to here.
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Form(
            key: _formKey,
            child: Row(
              children: [
                Expanded(
                  child: TextFormField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      hintText: 'Leave a message',
                    ),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'Enter your message to continue';
                      }
                      return null;
                    },
                  ),
                ),
                SizedBox(width: 8),
                StyledButton(
                  onPressed: () async {
                    if (_formKey.currentState!.validate()) {
                      await widget.addMessage(_controller.text);
                      _controller.clear();
                    }
                  },
                  child: Row(
                    children: [
                      Icon(Icons.send),
                      SizedBox(width: 4),
                      Text('SEND'),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
        // Modify from here
        SizedBox(height: 8),
        for (var message in widget.messages)
          Paragraph('${message.name}: ${message.message}'),
        SizedBox(height: 8),
        // to here.
      ],
    );
  }
}

Você embrulhar o conteúdo anterior do método de construção com uma Column widget, e depois na cauda da Column 's crianças que você adicionar uma coleção para gerar um novo Paragraph para cada mensagem na lista de mensagens.

Finalmente, agora você precisa atualizar o corpo de HomePage para construir corretamente GuestBook com o novo messages parâmetro.

lib / main.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      if (appState.loginState == ApplicationLoginState.loggedIn) ...[
        Header('Discussion'),
        GuestBook(
          addMessage: (String message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages, // new
        ),
      ],
    ],
  ),
),

Testar mensagens de sincronização

O Cloud Firestore sincroniza dados de forma automática e instantânea com clientes inscritos no banco de dados.

  1. As mensagens que você criou anteriormente no banco de dados devem ser exibidas no aplicativo. Sinta-se à vontade para escrever novas mensagens; eles devem aparecer instantaneamente.
  2. Se você abrir seu espaço de trabalho em várias janelas ou guias, as mensagens serão sincronizadas em tempo real entre as guias.
  3. (Opcional) Você pode tentar manualmente apagar, modificar ou adicionar novas mensagens diretamente na seção Banco de Dados do console Firebase; todas as alterações devem aparecer na IU.

Parabéns! Você está lendo documentos do Cloud Firestore em seu aplicativo!

App revisão p

Você inicialmente configurou o Cloud Firestore para usar o modo de teste, o que significa que seu banco de dados está aberto para leituras e gravações. No entanto, você só deve usar o modo de teste durante os estágios iniciais de desenvolvimento. Como prática recomendada, você deve configurar regras de segurança para seu banco de dados à medida que desenvolve seu aplicativo. A segurança deve ser parte integrante da estrutura e do comportamento do seu aplicativo.

As regras de segurança permitem que você controle o acesso a documentos e coleções em seu banco de dados. A sintaxe de regras flexíveis permite criar regras que correspondem a qualquer coisa, desde todas as gravações em todo o banco de dados até operações em um documento específico.

Você pode escrever regras de segurança para o Cloud Firestore no Firebase console:

  1. Na seção Desenvolver do Firebase console, clique em banco de dados e, em seguida, selecione a guia Regras (ou clique aqui para ir diretamente à guia Regras).
  2. Você deve ver as seguintes regras de segurança padrão, junto com um aviso sobre as regras serem públicas.

7767a2d2e64e7275.png

Identificar coleções

Primeiro, identifique as coleções nas quais o aplicativo grava dados.

Em match /databases/{database}/documents , identificar a coleção que você deseja proteger:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
     // You'll add rules here in the next step.
  }
}

Adicionar regras de segurança

Como você usou o UID de autenticação como um campo em cada documento do livro de visitas, você pode obter o UID de autenticação e verificar se qualquer pessoa que tentar gravar no documento possui um UID de autenticação correspondente.

Adicione as regras de leitura e gravação ao seu conjunto de regras, conforme mostrado abaixo:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
      allow read: if request.auth.uid != null;
      allow write:
        if request.auth.uid == request.resource.data.userId;
    }
  }
}

Agora, para o livro de visitas, apenas usuários conectados podem ler mensagens (qualquer mensagem!), Mas apenas o autor de uma mensagem pode editar uma mensagem.

Adicionar regras de validação

Adicione validação de dados para garantir que todos os campos esperados estejam presentes no documento:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
      allow read: if request.auth.uid != null;
      allow write:
      if request.auth.uid == request.resource.data.userId
          && "name" in request.resource.data
          && "text" in request.resource.data
          && "timestamp" in request.resource.data;
    }
  }
}

Registre o status RSVP de um participante

No momento, seu aplicativo apenas permite que as pessoas comecem a bater papo se estiverem interessadas no evento. Além disso, a única maneira de saber se alguém está vindo é postar no chat. Vamos nos organizar e informar às pessoas quantas pessoas virão.

Você adicionará alguns novos recursos ao estado do aplicativo. A primeira é a capacidade de um usuário logado indicar se participará ou não. A segunda capacidade é um contador de quantas pessoas estão realmente participando.

Em lib/main.dart , adicione o seguinte para a seção acessores para ativar o código UI para interagir com este estado:

lib / main.dart

int _attendees = 0;
int get attendees => _attendees;

Attending _attending = Attending.unknown;
StreamSubscription<DocumentSnapshot>? _attendingSubscription;
Attending get attending => _attending;
set attending(Attending attending) {
  final userDoc = FirebaseFirestore.instance
      .collection('attendees')
      .doc(FirebaseAuth.instance.currentUser!.uid);
  if (attending == Attending.yes) {
    userDoc.set({'attending': true});
  } else {
    userDoc.set({'attending': false});
  }
}

Atualização ApplicationState 's init método da seguinte forma:

lib / main.dart

  Future<void> init() async {
    await Firebase.initializeApp();

    // Add from here
    FirebaseFirestore.instance
        .collection('attendees')
        .where('attending', isEqualTo: true)
        .snapshots()
        .listen((snapshot) {
      _attendees = snapshot.docs.length;
      notifyListeners();
    });
    // To here

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          snapshot.docs.forEach((document) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'],
                message: document.data()['text'],
              ),
            );
          });
          notifyListeners();
        });
        // Add from here
        _attendingSubscription = FirebaseFirestore.instance
            .collection('attendees')
            .doc(user.uid)
            .snapshots()
            .listen((snapshot) {
          if (snapshot.data() != null) {
            if (snapshot.data()!['attending']) {
              _attending = Attending.yes;
            } else {
              _attending = Attending.no;
            }
          } else {
            _attending = Attending.unknown;
          }
          notifyListeners();
        });
        // to here
      } else {
        _loginState = ApplicationLoginState.loggedOut;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        _attendingSubscription?.cancel(); // new
      }
      notifyListeners();
    });
  }

O acima adiciona uma consulta sempre inscrita para descobrir o número de participantes e uma segunda consulta que fica ativa apenas enquanto um usuário está logado para descobrir se o usuário está participando. Em seguida, adicione o seguinte enumeração após a GuestBookMessage declaração:

lib / main.dart

enum Attending { yes, no, unknown }

Agora você vai definir um novo widget que atua como botões de rádio antigos. Ele começa em um estado indeterminado, sem sim nem não selecionado, mas uma vez que o usuário seleciona se está participando ou não, você mostra aquela opção destacada com um botão preenchido, e a outra opção recuando com uma renderização plana.

lib / main.dart

class YesNoSelection extends StatelessWidget {
  const YesNoSelection({required this.state, required this.onSelection});
  final Attending state;
  final void Function(Attending selection) onSelection;

  @override
  Widget build(BuildContext context) {
    switch (state) {
      case Attending.yes:
        return Padding(
          padding: EdgeInsets.all(8.0),
          child: Row(
            children: [
              ElevatedButton(
                style: ElevatedButton.styleFrom(elevation: 0),
                onPressed: () => onSelection(Attending.yes),
                child: Text('YES'),
              ),
              SizedBox(width: 8),
              TextButton(
                onPressed: () => onSelection(Attending.no),
                child: Text('NO'),
              ),
            ],
          ),
        );
      case Attending.no:
        return Padding(
          padding: EdgeInsets.all(8.0),
          child: Row(
            children: [
              TextButton(
                onPressed: () => onSelection(Attending.yes),
                child: Text('YES'),
              ),
              SizedBox(width: 8),
              ElevatedButton(
                style: ElevatedButton.styleFrom(elevation: 0),
                onPressed: () => onSelection(Attending.no),
                child: Text('NO'),
              ),
            ],
          ),
        );
      default:
        return Padding(
          padding: EdgeInsets.all(8.0),
          child: Row(
            children: [
              StyledButton(
                onPressed: () => onSelection(Attending.yes),
                child: Text('YES'),
              ),
              SizedBox(width: 8),
              StyledButton(
                onPressed: () => onSelection(Attending.no),
                child: Text('NO'),
              ),
            ],
          ),
        );
    }
  }
}

Em seguida, você precisa atualizar HomePage método build 's para tirar proveito de YesNoSelection , permitindo um usuário logado para nomear se eles estão presentes. Você também exibirá o número de participantes deste evento.

lib / main.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      // Add from here
      if (appState.attendees >= 2)
        Paragraph('${appState.attendees} people going')
      else if (appState.attendees == 1)
        Paragraph('1 person going')
      else
        Paragraph('No one going'),
      // To here.
      if (appState.loginState == ApplicationLoginState.loggedIn) ...[
        // Add from here
        YesNoSelection(
          state: appState.attending,
          onSelection: (attending) => appState.attending = attending,
        ),
        // To here.
        Header('Discussion'),
        GuestBook(
          addMessage: (String message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages,
        ),
      ],
    ],
  ),
),

Adicionar regras

Como você já tem algumas regras configuradas, os novos dados que você está adicionando com os botões serão rejeitados. Você precisa atualizar as regras para permitir a adição ao attendees coleção.

Para a attendees coleção, desde que você usou o UID de autenticação como o nome do documento, você pode agarrá-lo e verificar se o apresentador uid é o mesmo que o documento que está escrevendo. Você permitirá que todos leiam a lista de participantes (já que não há dados privados lá), mas apenas o criador deve ser capaz de atualizá-la.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId;
    }
  }
}

Adicionar regras de validação

Adicione validação de dados para garantir que todos os campos esperados estejam presentes no documento:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId
          && "attending" in request.resource.data;

    }
  }
}

(Opcional) Você pode agora ver os resultados de clicar nos botões. Acesse o painel do Cloud Firestore no Firebase console.

Visualização do aplicativo

Você usou o Firebase para criar um aplicativo da web interativo e em tempo real!

O que cobrimos

  • Firebase Authentication
  • Cloud Firestore
  • Regras de segurança do Firebase

Próximos passos

  • Quer saber mais sobre outros produtos Firebase? Talvez você queira armazenar arquivos de imagem que os usuários carregam? Ou enviar notificações para seus usuários? Confira a documentação Firebase . Quer saber mais sobre os plug-ins Flutter para Firebase? Confira FlutterFire para mais informações.
  • Quer saber mais sobre o Cloud Firestore? Talvez você queira aprender sobre subcoleções e transações? De cabeça para o codelab web Nuvem Firestore para uma codelab que entra em mais profundidade sobre Nuvem Firestore. Ou confira esta série YouTube para conhecer Nuvem Firestore !

Saber mais

Como foi?

Adoraríamos receber seu feedback! Por favor, preencha um (muito) pequeno formulário aqui .