Agrega un flujo de autenticación de usuarios a una app para Flutter con FirebaseUI

1. Antes de comenzar

En este codelab, aprenderás a agregar Firebase Authentication a tu app de Flutter con el paquete de la IU de FlutterFire. Con este paquete, agregarás la autenticación con correo electrónico y contraseña, y la autenticación con Acceso con Google a una app de Flutter. También aprenderás a configurar un proyecto de Firebase y a usar la CLI de FlutterFire para inicializar Firebase en tu app de Flutter.

Requisitos previos

En este codelab, se presupone que tienes experiencia en Flutter. Si no es así, te recomendamos que primero aprendas los conceptos básicos. Los siguientes vínculos son útiles:

También debes tener cierta experiencia con Firebase, pero no hay problema si nunca agregaste Firebase a un proyecto de Flutter. Si no conoces Firebase console o es la primera vez que usas Firebase, consulta primero los siguientes vínculos:

Qué crearás

En este codelab, se te guiará para que compiles el flujo de autenticación de una app de Flutter con Firebase Authentication. La aplicación tendrá una pantalla de acceso, una pantalla de registro, una pantalla de recuperación de contraseña y una pantalla de perfil de usuario.

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

Qué aprenderás

En este codelab, se abordan los siguientes temas:

  • Cómo agregar Firebase a una app de Flutter
  • Configuración de Firebase console
  • Usa Firebase CLI para agregar Firebase a tu aplicación
  • Usa la CLI de FlutterFire para generar la configuración de Firebase en Dart
  • Agrega Firebase Authentication a tu app de Flutter
  • Configuración de Firebase Authentication en la consola
  • Cómo agregar el acceso con correo electrónico y contraseña con el paquete firebase_ui_auth
  • Cómo agregar el registro de usuarios con el paquete firebase_ui_auth
  • Cómo agregar una página “¿Olvidaste la contraseña?”
  • Cómo agregar el Acceso con Google con firebase_ui_auth
  • Configurar tu app para que funcione con varios proveedores de acceso
  • Cómo agregar una pantalla de perfil de usuario a tu aplicación con el paquete firebase_ui_auth

Este codelab se centra específicamente en agregar un sistema de autenticación sólido con el paquete firebase_ui_auth. Como verás, toda esta app, con todas las funciones anteriores, se puede implementar con alrededor de 100 líneas de código.

Requisitos

  • Conocimiento práctico de Flutter y el SDK instalado
  • Un editor de texto (Flutter admite los IDE de JetBrains, Android Studio y VS Code)
  • Navegador Google Chrome o tu otro destino de desarrollo preferido para Flutter (Algunos comandos de terminal de este codelab supondrán que ejecutas tu app en Chrome).

2. Crea y configura un proyecto de Firebase

La primera tarea que deberás completar es crear un proyecto de Firebase en Firebase web console.

Crea un proyecto de Firebase

  1. Accede a la consola de Firebase con tu Cuenta de Google.
  2. Haz clic en el botón para crear un proyecto nuevo y, luego, ingresa un nombre (por ejemplo, FlutterFire-UI-Codelab).
  3. Haz clic en Continuar.
  4. Si se te solicita, revisa y acepta las Condiciones de Firebase y, luego, haz clic en Continuar.
  5. (Opcional) Habilita la asistencia de IA en Firebase console (llamada "Gemini en Firebase").
  6. Para este codelab, no necesitas Google Analytics, por lo que debes desactivar la opción de Google Analytics.
  7. Haz clic en Crear proyecto, espera a que se aprovisione y, luego, haz clic en Continuar.

Para obtener más información sobre los proyectos de Firebase, consulta Información sobre los proyectos de Firebase.

Habilita el acceso por correo electrónico para Firebase Authentication

La app que compilas usa Firebase Authentication para permitir que los usuarios accedan a ella. También permite que los usuarios nuevos se registren desde la aplicación de Flutter.

Firebase Authentication debe habilitarse con Firebase console y requiere una configuración especial una vez habilitado.

Para permitir que los usuarios accedan a la app web, primero usarás el método de acceso Correo electrónico/contraseña. Más adelante, agregarás el método de Acceso con Google.

  1. En Firebase console, expande el menú Build en el panel izquierdo.
  2. Haz clic en Authentication y, luego, en el botón Get Started y en la pestaña Sign-in method (o ve directamente a la pestaña Sign-in method).
  3. En la lista Proveedores de acceso, haz clic en Correo electrónico/contraseña, establece el interruptor Habilitar en la posición de activado y, luego, haz clic en Guardar.

58e3e3e23c2f16a4.png

3. Configura la app de Flutter

Antes de comenzar, deberás descargar el código de inicio e instalar Firebase CLI.

Obtén el código de partida

Clona el repositorio de GitHub desde la línea de comandos:

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

Como alternativa, si tienes instalada la herramienta de la CLI de GitHub, haz lo siguiente:

gh repo clone flutter/codelabs flutter-codelabs

El código de muestra se debe clonar en el directorio flutter-codelabs de tu máquina, que contiene el código de una colección de codelabs. El código de este codelab se encuentra en el subdirectorio flutter-codelabs/firebase-auth-flutterfire-ui.

El directorio flutter-codelabs/firebase-auth-flutterfire-ui contiene dos proyectos de Flutter. Uno se llama complete y el otro, start. El directorio start contiene un proyecto incompleto, y es donde pasarás la mayor parte del tiempo.

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

Si quieres avanzar o ver cómo debería verse algo cuando esté completo, consulta el directorio llamado complete para hacer una referencia cruzada.

Si quieres seguir el codelab y agregar código por tu cuenta, debes comenzar con la app de Flutter en flutter-codelabs/firebase-auth-flutterfire-ui/start y agregar código a ese proyecto a lo largo del codelab. Abre o importa ese directorio en el IDE que prefieras.

Instala Firebase CLI

Firebase CLI proporciona herramientas para administrar tus proyectos de Firebase. La CLI es necesaria para la CLI de FlutterFire, que instalarás en un momento.

Existen varias formas de instalar la CLI. Revisa todas las opciones disponibles para tu sistema operativo en firebase.google.com/docs/cli.

Después de instalar la CLI, debes autenticarte con Firebase.

  1. Accede a Firebase con tu Cuenta de Google ejecutando el siguiente comando:
    firebase login
    
  2. Este comando conecta tu máquina local a Firebase y te otorga acceso a tus proyectos de Firebase.
  3. Enumera tus proyectos de Firebase para probar que se instaló correctamente la CLI y que tienes acceso a tu cuenta. Ejecuta el siguiente comando:
    firebase projects:list
    
  4. La lista que se muestra debe ser la misma que los proyectos de Firebase enumerados en Firebase console. Deberías ver, al menos, flutterfire-ui-codelab.

Instala la CLI de FlutterFire

La CLI de FlutterFire es una herramienta que facilita el proceso de instalación de Firebase en todas las plataformas compatibles con tu app de Flutter. Se basa en la CLI de Firebase.

Primero, instala la CLI:

dart pub global activate flutterfire_cli

Asegúrate de que se haya instalado la CLI. Ejecuta el siguiente comando y verifica que la CLI muestre el menú de ayuda.

flutterfire --help

Agrega tu proyecto de Firebase a tu app de Flutter

Configura FlutterFire

Puedes usar FlutterFire para generar el código Dart necesario para usar Firebase en tu app de Flutter.

flutterfire configure

Cuando ejecutes este comando, se te pedirá que selecciones el proyecto de Firebase que deseas usar y las plataformas que deseas configurar.

En las siguientes capturas de pantalla, se muestran las indicaciones que deberás responder.

  1. Selecciona el proyecto que deseas usar. En este caso, usa flutterfire-ui-codelab1359cdeb83204baa.png.
  2. Selecciona las plataformas que quieres usar. En este codelab, se incluyen pasos para configurar Firebase Authentication para Flutter en la Web, iOS y Android, pero puedes configurar tu proyecto de Firebase para usar todas las opciones. 301c9534f594f472.png
  3. En esta captura de pantalla, se muestra el resultado al final del proceso. Si conoces Firebase, notarás que no tuviste que crear aplicaciones para plataformas (por ejemplo, una aplicación para Android) en la consola, ya que la CLI de FlutterFire lo hizo por ti. 12199a85ade30459.png

Cuando termines, mira la app de Flutter en tu editor de texto. La CLI de FlutterFire modificó un archivo llamado firebase_options.dart. Este archivo contiene una clase llamada FirebaseOptions, que tiene variables estáticas que contienen la configuración de Firebase necesaria para cada plataforma. Si seleccionaste todas las plataformas cuando ejecutaste flutterfire configure, verás valores estáticos llamados web, android, ios y macos.

lib/firebase_options.dart

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

class DefaultFirebaseOptions {
  static FirebaseOptions get currentPlatform {
    if (kIsWeb) {
      return web;
    }

    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        return android;
      case TargetPlatform.iOS:
        return ios;
      case TargetPlatform.macOS:
        return macos;
      default:
        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',
  );
}

Firebase usa la palabra aplicación para referirse a una compilación específica para una plataforma específica en un proyecto de Firebase. Por ejemplo, el proyecto de Firebase llamado FlutterFire-ui-codelab tiene varias aplicaciones: una para Android, una para iOS, una para macOS y una para la Web.

El método DefaultFirebaseOptions.currentPlatform usa el enum TargetPlatform que expone Flutter para detectar la plataforma en la que se ejecuta tu app y, luego, devuelve los valores de configuración de Firebase necesarios para la aplicación de Firebase correcta.

Agrega paquetes de Firebase a la app de Flutter

El último paso de configuración es agregar los paquetes relevantes de Firebase a tu proyecto de Flutter. El archivo firebase_options.dart debería tener errores, ya que depende de paquetes de Firebase que aún no se agregaron. En la terminal, asegúrate de estar en la raíz del proyecto de Flutter en flutter-codelabs/firebase-emulator-suite/start. Luego, ejecuta los tres comandos siguientes:

flutter pub add firebase_core firebase_auth firebase_ui_auth

Estos son los únicos paquetes que necesitas en este momento.

Inicializa Firebase

Para usar los paquetes agregados y DefaultFirebaseOptions.currentPlatform,, actualiza el código en la función main del archivo main.dart.

lib/main.dart

import 'package:firebase_core/firebase_core.dart';                  // Add this import
import 'package:flutter/material.dart';

import 'app.dart';
import 'firebase_options.dart';                                     // And this import

// TODO(codelab user): Get API key
const clientId = 'YOUR_CLIENT_ID';

void main() async {
  // Add from here...
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  // To here.

  runApp(const MyApp(clientId: clientId));
}

Este código realiza dos acciones.

  1. WidgetsFlutterBinding.ensureInitialized() le indica a Flutter que no comience a ejecutar el código del widget de la aplicación hasta que el framework de Flutter se haya iniciado por completo. Firebase usa canales de plataformas nativas, que requieren que el framework se esté ejecutando.
  2. Firebase.initializeApp establece una conexión entre tu app de Flutter y tu proyecto de Firebase. El DefaultFirebaseOptions.currentPlatform se importa desde nuestro archivo firebase_options.dart generado. Este valor estático detecta en qué plataforma se ejecuta y pasa las claves de Firebase correspondientes.

4. Agrega la página inicial de Firebase UI Auth

La IU de Firebase para Auth proporciona widgets que representan pantallas completas en tu aplicación. Estas pantallas controlan diferentes flujos de autenticación en toda la aplicación, como Acceder, Registro, Olvidé mi contraseña, Perfil del usuario y muchos más. Para comenzar, agrega una página de destino a tu app que actúe como protección de autenticación para la aplicación principal.

App de Material o Cupertino

La IU de FlutterFire requiere que tu aplicación esté encapsulada en un MaterialApp o un CupertinoApp. Según tu elección, la IU reflejará automáticamente las diferencias de los widgets de Material o Cupertino. En este codelab, usa MaterialApp, que ya se agregó a la app en app.dart.

lib/app.dart

import 'package:flutter/material.dart';

import 'auth_gate.dart';

class MyApp extends StatelessWidget {
  const MyApp({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: AuthGate(clientId: clientId),
    );
  }
}

Comprueba el estado de autenticación

Antes de mostrar una pantalla de acceso, debes determinar si el usuario está autenticado. La forma más común de verificar esto es escuchar el authStateChanges de FirebaseAuth con el complemento de Firebase Authentication.

En la muestra de código anterior, el MaterialApp está compilando un widget AuthGate en su método build. (Este es un widget personalizado que no proporciona la IU de FlutterFire).

Ese widget debe actualizarse para incluir la transmisión de authStateChanges.

La API de authStateChanges devuelve un Stream con el usuario actual (si accedió) o null si no accedió. Para suscribirte a este estado en nuestra aplicación, puedes usar el widget StreamBuilder de Flutter y pasarle la transmisión.

StreamBuilder es un widget que se compila en función de la instantánea de datos más reciente de un Stream que le pasas. Se vuelve a compilar automáticamente cuando el Stream emite una nueva instantánea.

Actualiza el código en auth_gate.dart.

lib/auth_gate.dart

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

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

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

        return const HomeScreen();
      },
    );                                                                 // To here.
  }
}
  • Se pasa StreamBuilder.stream, el flujo mencionado anteriormente, que devolverá un objeto User de Firebase si el usuario se autenticó; de lo contrario, devolverá null.FirebaseAuth.instance.authStateChanged
  • A continuación, el código usa snapshot.hasData para verificar si el valor de la transmisión contiene el objeto User.
  • Si no hay, devolverá un widget de SignInScreen. Por ahora, esta pantalla no hará nada, se actualizará en el siguiente paso.
  • De lo contrario, devuelve un HomeScreen, que es la parte principal de la aplicación a la que solo pueden acceder los usuarios autenticados.

El SignInScreen es un widget que proviene del paquete de IU de FlutterFire. Este será el enfoque del siguiente paso de este codelab. Cuando ejecutes la app en este punto, deberías ver una pantalla de acceso en blanco.

5. Pantalla de acceso

El widget SignInScreen, proporcionado por FlutterFire UI, agrega la siguiente funcionalidad:

  • Permite que los usuarios accedan
  • Si los usuarios olvidaron su contraseña, pueden presionar "¿Olvidaste la contraseña?" y se los dirigirá a un formulario para restablecerla.
  • Si un usuario aún no se registró, puede presionar "Registrarse" y se lo dirigirá a otro formulario que le permitirá registrarse.

Nuevamente, esto solo requiere un par de líneas de código. Recuerda el código en el widget AuthGate:

lib/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, required this.clientId});

  final String clientId;

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

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

El widget SignInScreen y su argumento providers son el único código necesario para obtener toda la funcionalidad mencionada. Ahora deberías ver una pantalla de acceso con las entradas de texto "correo electrónico" y "contraseña", así como un botón "Acceder".

Si bien es funcional, no tiene diseño. El widget expone parámetros para personalizar la apariencia de la pantalla de acceso. Por ejemplo, es posible que quieras agregar el logotipo de tu empresa.

Personaliza la pantalla de acceso

headerBuilder

Con el argumento SignInScreen.headerBuilder, puedes agregar los widgets que quieras sobre el formulario de acceso. Este widget solo se muestra en pantallas angostas, como las de dispositivos móviles. En pantallas anchas, puedes usar SignInScreen.sideBuilder, que se analiza más adelante en este codelab.

Actualiza el archivo lib/auth_gate.dart con este código:

lib/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, required this.clientId});

  final String clientId;

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

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

The headerBuilder argument requires a function of the type HeaderBuilder, which
is defined in the FlutterFire UI package.

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

Como es una devolución de llamada, expone valores que podrías usar, como BuildContext y BoxConstraints, y requiere que devuelvas un widget. El widget que devuelvas se mostrará en la parte superior de la pantalla. En este ejemplo, el código nuevo agrega una imagen en la parte superior de la pantalla. Tu aplicación ahora debería verse así.

73d7548d91bbd2ab.png

Creador de subtítulos

La pantalla de acceso expone tres parámetros adicionales que te permiten personalizarla: subtitleBuilder, footerBuilder y sideBuilder.

El subtitleBuilder es ligeramente diferente, ya que los argumentos de devolución de llamada incluyen una acción, que es de tipo AuthAction. AuthAction es una enumeración que puedes usar para detectar si la pantalla en la que se encuentra el usuario es la de "acceso" o la de "registro".

Actualiza el código en auth_gate.dart para usar subtitleBuilder.

lib/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, required this.clientId});

  final String clientId;

  @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'),
                ),
              );
            },
            subtitleBuilder: (context, action) {                     // Add from here...
              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!'),
              );
            },                                                       // To here.
          );
        }

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

El argumento footerBuilder es el mismo que el de subtitleBuilder. No expone BoxConstraints ni shrinkOffset, ya que está diseñado para texto en lugar de imágenes. Por supuesto, puedes agregar cualquier widget que desees.

Agrega un pie de página a la pantalla de acceso con este código.

lib/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, required this.clientId});

  final String clientId;

  @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'),
                ),
              );
            },
            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) {                       // Add from here...
              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),
                ),
              );
            },                                                       // To here.
          );
        }

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

Side Builder

El argumento SignInScreen.sidebuilder acepta una devolución de llamada y, esta vez, los argumentos para esa devolución de llamada son BuildContext y double shrinkOffset. El widget que devuelve sideBuilder se mostrará a la izquierda del formulario de acceso y solo en pantallas anchas. Esto significa que el widget solo se mostrará en computadoras y apps web.

Internamente, la IU de FlutterFire usa un punto de interrupción para determinar si se debe mostrar el contenido del encabezado (en pantallas altas, como las de dispositivos móviles) o el contenido lateral (en pantallas anchas, como las de computadoras o la Web). Específicamente, si una pantalla tiene más de 800 píxeles de ancho, se muestra el contenido del compilador lateral y no el del encabezado. Si la pantalla tiene menos de 800 píxeles de ancho, sucede lo contrario.

Actualiza el código en auth_gate.dart para agregar widgets sideBuilder.

lib/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, required this.clientId});

  final String clientId;

  @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'),
                ),
              );
            },
            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();
      },
    );
  }
}

Tu app ahora debería verse así cuando expandas el ancho de la ventana (si usas Flutter web o macOS).

8dc60b4e5d7dd2d0.png

Crea un usuario

En este punto, se completó todo el código de esta pantalla. Sin embargo, antes de acceder, debes crear un usuario. Puedes hacerlo con la pantalla "Registrarse" o crear un usuario en Firebase console.

Para usar la consola, haz lo siguiente:

  1. Ve a la tabla"Usuarios" en Firebase console. Selecciona "flutterfire-ui-codelab" o cualquier otro proyecto si usaste un nombre diferente. Verás esta tabla: f038fd9a58ed60d9.png
  2. Haz clic en el botón “Agregar usuario”. 2d78390d4c5dbbfa.png
  3. Ingresa una dirección de correo electrónico y una contraseña para el usuario nuevo. Puede ser un correo electrónico y una contraseña falsos, como los que ingresé en la siguiente imagen. Eso funcionará, pero la función "¿Olvidaste la contraseña?" no funcionará si usas una dirección de correo electrónico falsa. 62ba0feb33d54add.png
  4. Haz clic en “Agregar usuario”. 32b236b3ef94d4c7.png

Ahora puedes volver a tu aplicación de Flutter y acceder con un usuario a través de la página de acceso. Tu app debería verse de la siguiente manera:

dd43d260537f3b1a.png

6. Pantalla de perfil

La IU de FlutterFire también proporciona un widget ProfileScreen, que, nuevamente, te brinda mucha funcionalidad en pocas líneas de código.

Agregar widget ProfileScreen

Navega al archivo home.dart en el editor de texto. Actualízalo con este código:

lib/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: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

El nuevo código de interés es la devolución de llamada que se pasa al método IconButton.isPressed. Cuando se presiona ese IconButton, tu aplicación crea una ruta anónima nueva y navega a ella. Esa ruta mostrará el widget ProfileScreen, que se devuelve desde la devolución de llamada MaterialPageRoute.builder.

Vuelve a cargar tu app y presiona el ícono en la esquina superior derecha (en la barra de la app). Se mostrará una página como esta:

36487fc4ab4f26a7.png

Esta es la IU estándar que proporciona la página de IU de FlutterFire. Todos los botones y campos de texto están conectados a Firebase Auth y funcionan de inmediato. Por ejemplo, puedes ingresar un nombre en el campo de texto "Nombre", y FlutterFire UI llamará al método FirebaseAuth.instance.currentUser?.updateDisplayName, que guardará ese nombre en Firebase.

Salir

En este momento, si presionas el botón "Cerrar sesión", la app no cambiará. Se cerrará tu sesión, pero no se te redireccionará de nuevo al widget de AuthGate. Para implementar esto, usa el parámetro ProfileScreen.actions.

Primero, actualiza el código en home.dart.

lib/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: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

Ahora, cuando creas una instancia de ProfileScreen, también le pasas una lista de acciones al argumento ProfileScreen.actions. Estas acciones son del tipo FlutterFireUiAction. Existen muchas clases diferentes que son subtipos de FlutterFireUiAction y, en general, se usan para indicarle a tu app que reaccione a diferentes cambios de estado de autenticación. SignedOutAction llama a una función de devolución de llamada que le proporcionas cuando el estado de autenticación de Firebase cambia a currentUser como nulo.

Si agregas una devolución de llamada que llame a Navigator.of(context).pop() cuando se active SignedOutAction, la app navegará a la página anterior. En esta app de ejemplo, solo hay una ruta permanente, que muestra la pantalla de acceso si no hay un usuario conectado y la página principal si hay un usuario. Como esto sucede cuando el usuario cierra la sesión, la app mostrará la pantalla de acceso.

Personaliza la página de perfil

Al igual que la pantalla de acceso, la página de perfil es personalizable. Primero, nuestra página actual no tiene forma de volver a la página principal una vez que un usuario está en la página de perfil. Para corregir esto, dale al widget de ProfileScreen un AppBar.

lib/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: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

El argumento ProfileScreen.appBar acepta un widget AppBar del paquete Material de Flutter, por lo que se puede tratar como cualquier otro AppBar que hayas compilado y pasado a un Scaffold. En este ejemplo, se mantiene la funcionalidad predeterminada de agregar automáticamente un botón "Atrás", y la pantalla ahora tiene un título.

Cómo agregar niños o niñas a la pantalla de perfil

El widget ProfileScreen también tiene un argumento opcional llamado hijos. Este argumento acepta una lista de widgets, y esos widgets se colocarán verticalmente dentro de un widget Column que ya se usa de forma interna para compilar el ProfileScreen. Este widget Column en el método de compilación ProfileScreen colocará los elementos secundarios que le pases sobre el botón "Cerrar sesión".

Actualiza el código en home.dart para mostrar el logotipo de la empresa aquí, de forma similar a la pantalla de acceso.

lib/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: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

Vuelve a cargar la app y verás lo siguiente en la pantalla:

ebe5792b765dbf87.png

7. Acceso con Google multiplataforma

La IU de FlutterFire también proporciona widgets y funciones para la autenticación con proveedores externos, como Google, Twitter, Facebook, Apple y GitHub.

Para integrar la autenticación de Google, instala el complemento oficial firebase_ui_oauth_google y sus dependencias, que controlarán el flujo de autenticación nativo. En la terminal, navega a la raíz de tu proyecto de Flutter y, luego, ingresa el siguiente comando:

flutter pub add google_sign_in firebase_ui_oauth_google

Habilita el proveedor de acceso con Google

A continuación, habilita el proveedor de Google en Firebase console:

  1. Navega a la pantalla Authentication sign-in providers en la consola.
  2. Haz clic en "Agregar proveedor nuevo". 8286fb28be94bf30.png
  3. Selecciona “Google”. c4e28e6f4974be7f.png
  4. Activa el interruptor etiquetado como “Habilitar” y presiona “Guardar”. e74ff86990763826.png
  5. Si aparece una ventana modal con información sobre la descarga de archivos de configuración, haz clic en "Listo".
  6. Confirma que se haya agregado el proveedor de acceso con Google. 5329ce0543c90d95.png

Agrega el botón de acceso con Google

Con el acceso con Google habilitado, agrega el widget necesario para mostrar un botón de acceso con Google estilizado en la pantalla de acceso. Navega al archivo auth_gate.dart y actualiza el código con el siguiente:

lib/auth_gate.dart

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';  // Add this import
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

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

El único código nuevo aquí es la adición de GoogleProvider(clientId: "YOUR_WEBCLIENT_ID") a la configuración del widget SignInScreen.

Con eso agregado, vuelve a cargar tu app y verás un botón de acceso con Google.

aca71a46a011bfb5.png

Configura el botón de acceso

El botón no funciona sin configuración adicional. Si desarrollas con Flutter Web, este es el único paso que debes agregar para que funcione. Otras plataformas requieren pasos adicionales, que se analizarán en breve.

  1. Navega a la página Proveedores de autenticación en Firebase console.
  2. Haz clic en el proveedor de Google. 9b3a325c5eca6e49.png
  3. Haz clic en el panel de expansión "Configuración del SDK web".
  4. Copia el valor de "ID de cliente web". 711a79f0d931c60f.png
  5. Vuelve al editor de texto y actualiza la instancia de GoogleProvider en el archivo auth_gate.dart pasando este ID al parámetro con nombre clientId.
GoogleProvider(
   clientId: "YOUR_WEBCLIENT_ID"
)

Una vez que ingreses el ID de cliente web, vuelve a cargar tu app. Cuando presiones el botón "Acceder con Google", aparecerá una ventana nueva (si usas la Web) que te guiará por el flujo de acceso de Google. Inicialmente, se ve así:

14e73e3c9de704bb.png

Configura iOS

Para que esto funcione en iOS, hay un proceso de configuración adicional.

  1. Navega a la pantalla Configuración del proyecto en Firebase console. Aparecerá una tarjeta con tus apps de Firebase, como la siguiente: fefa674acbf213cc.png
  2. Selecciona iOS. Ten en cuenta que el nombre de tu aplicación será diferente del que se muestra en la captura de pantalla. En la captura de pantalla, se indica "complete", pero en tu caso, se indicará "start" si usaste el proyecto flutter-codelabs/firebase-auth-flutterfire-ui/start para seguir este codelab.
  3. Haz clic en el botón que dice GoogleServices-Info.plist para descargar el archivo de configuración necesario. f89b3192871dfbe3.png
  4. Arrastra el archivo descargado al directorio llamado /ios/Runner en tu proyecto de Flutter.
  5. Abre Xcode ejecutando el siguiente comando de terminal desde la raíz de tu proyecto: open ios/Runner.xcworkspace
  6. Haz clic con el botón derecho en el directorio Runner y selecciona Add Files to "Runner". 858986063a4c5201.png
  7. Selecciona GoogleService-Info.plist en el administrador de archivos.
  8. De vuelta en tu editor de texto (que no es Xcode), agrega los atributos CFBundleURLTypes que se indican a continuación al archivo 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 -->
    
  9. Debes reemplazar el GoogleProvider.clientId que agregaste en la configuración web por el ID de cliente asociado con tu ID de cliente de Firebase para iOS. Primero, puedes encontrar este ID en el archivo firebase_options.dart, como parte de la constante iOS. Copia el valor que se pasó a 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',
    );
    
  10. Pega ese valor en la variable clientId del archivo lib/main.dart.

lib/main.dart

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

import 'app.dart';
import 'firebase_options.dart';

const clientId = 'YOUR_CLIENT_ID'; // Replace this value with your Client ID.

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

  runApp(const MyApp(clientId: clientId));
}

Si tu app de Flutter ya se está ejecutando en iOS, debes cerrarla por completo y, luego, volver a ejecutarla. De lo contrario, ejecuta la app en iOS.

8. ¡Felicitaciones!

Completaste el codelab de la IU de Firebase Auth para Flutter . Puedes encontrar el código completo de este codelab en el directorio firebase-auth-flutterfire-ui/complete en GitHub.

Temas abordados

  • Cómo configurar una app de Flutter para usar Firebase
  • Configura un proyecto de Firebase en Firebase console
  • CLI de FlutterFire
  • Firebase CLI
  • Uso de Firebase Authentication
  • Cómo usar la IU de FlutterFire para controlar la autenticación de Firebase en tu app de Flutter

Próximos pasos

Más información

¡Sparky está aquí para celebrar contigo!

2a0ad195769368b1.gif