Autenticación en Firebase: Configuración y Manejo de Estados con Bloc
Clase 9 de 30 • Curso de Flutter con Firebase
Resumen
La arquitectura BLoC en Flutter es una poderosa herramienta para gestionar el estado de tus aplicaciones de manera eficiente y organizada. En este artículo, exploraremos cómo implementar la autenticación con Firebase utilizando el patrón BLoC, creando un sistema robusto que separa la lógica de negocio de la interfaz de usuario.
¿Cómo configurar Firebase en tu proyecto Flutter?
Antes de comenzar a implementar la autenticación, es necesario configurar Firebase en nuestro proyecto. Este proceso implica varios pasos importantes que establecerán la conexión entre nuestra aplicación y los servicios de Firebase.
Inicialización de Firebase desde la terminal
Para comenzar a trabajar con Firebase, debemos autenticarnos desde la terminal:
- Ejecutamos el comando para iniciar sesión:
firebase login
-
Este comando abrirá una ventana del navegador donde seleccionaremos nuestra cuenta de Google asociada a Firebase.
-
Después de conceder los permisos necesarios, configuramos el acceso global:
firebase use --add
- Exportamos las variables de entorno necesarias:
export PATH="$PATH":"$HOME/.pub-cache/bin"
Configuración del proyecto Flutter con Firebase
Una vez autenticados, debemos inicializar Firebase en nuestro proyecto:
- Ejecutamos el comando para activar Firebase CLI:
flutterfire configure
-
Seleccionamos nuestro proyecto de Firebase de la lista mostrada.
-
Elegimos las plataformas para las que queremos configurar Firebase (en este caso, solo iOS).
Inicialización de Firebase en el código
Después de la configuración inicial, debemos inicializar Firebase en nuestro código:
- En el archivo de iOS (AppDelegate.swift), importamos Firebase y lo inicializamos:
import Firebase
// En el método didFinishLaunchingWithOptions
FirebaseApp.configure()
- En nuestro archivo main.dart, modificamos la función main para inicializar Firebase:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
¿Cómo implementar la arquitectura BLoC para autenticación?
La arquitectura BLoC se compone de tres elementos principales: State (estado), Event (eventos) y Bloc (controlador). Vamos a implementar cada uno de estos componentes para nuestra funcionalidad de autenticación.
Creación del AuthState
El estado representa la información actual de nuestra aplicación. Para la autenticación, necesitamos:
class AuthState {
final String email;
final String password;
final bool isSubmitting;
final String? errorMessage;
final bool isAuthenticated;
AuthState({
required this.email,
required this.password,
required this.isSubmitting,
this.errorMessage,
required this.isAuthenticated,
});
factory AuthState.initial() {
return AuthState(
email: '',
password: '',
isSubmitting: false,
errorMessage: null,
isAuthenticated: false,
);
}
AuthState copyWith({
String? email,
String? password,
bool? isSubmitting,
String? errorMessage,
bool? isAuthenticated,
}) {
return AuthState(
email: email ?? this.email,
password: password ?? this.password,
isSubmitting: isSubmitting ?? this.isSubmitting,
errorMessage: errorMessage ?? this.errorMessage,
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
);
}
}
El método copyWith
es fundamental ya que nos permite crear nuevas instancias del estado con valores actualizados, manteniendo la inmutabilidad.
Definición de los AuthEvents
Los eventos son las acciones que pueden ocurrir en nuestra aplicación:
abstract class AuthEvent {}
class AuthEmailChanged extends AuthEvent {
final String email;
AuthEmailChanged(this.email);
}
class AuthPasswordChanged extends AuthEvent {
final String password;
AuthPasswordChanged(this.password);
}
class AuthLoginRequest extends AuthEvent {}
class AuthLogoutRequest extends AuthEvent {}
Cada evento representa una acción específica que puede cambiar el estado de nuestra aplicación.
Creación del repositorio de autenticación
El repositorio es el encargado de comunicarse con Firebase:
class AuthRepository {
FirebaseAuth? _firebaseAuth;
AuthRepository([FirebaseAuth? firebaseAuth])
: _firebaseAuth = firebaseAuth;
FirebaseAuth get firebaseAuth {
_firebaseAuth ??= FirebaseAuth.instance;
return _firebaseAuth!;
}
Future<User> signInWithEmailAndPassword(String email, String password) async {
try {
final UserCredential userCredential = await firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
return userCredential.user!;
} on FirebaseAuthException catch (e) {
throw e.message ?? 'Error de autenticación';
}
}
Future<void> signOut() async {
await firebaseAuth.signOut();
}
Stream<User?> get authStateChanges => firebaseAuth.authStateChanges();
}
Este repositorio encapsula toda la lógica de comunicación con Firebase Authentication.
Implementación del AuthBloc
El Bloc es el centro de control que conecta los eventos con el estado:
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final AuthRepository authRepository;
AuthBloc({required this.authRepository}) : super(AuthState.initial()) {
on<AuthEmailChanged>((event, emit) {
emit(state.copyWith(email: event.email));
});
on<AuthPasswordChanged>((event, emit) {
emit(state.copyWith(password: event.password));
});
on<AuthLoginRequest>((event, emit) async {
emit(state.copyWith(isSubmitting: true, errorMessage: null));
try {
await authRepository.signInWithEmailAndPassword(
state.email,
state.password,
);
emit(state.copyWith(isSubmitting: false, isAuthenticated: true));
} catch (e) {
emit(state.copyWith(
isSubmitting: false,
errorMessage: e.toString(),
));
}
});
on<AuthLogoutRequest>((event, emit) async {
await authRepository.signOut();
emit(state.copyWith(isSubmitting: false, isAuthenticated: false));
});
}
}
El Bloc recibe eventos, ejecuta la lógica correspondiente y emite nuevos estados. La función on<T>
registra un manejador para cada tipo de evento.
¿Por qué es importante la separación de responsabilidades en BLoC?
La arquitectura BLoC nos permite separar claramente las responsabilidades en nuestra aplicación:
- Estado (State): Contiene solo los datos y no tiene lógica.
- Eventos (Events): Representan acciones que pueden ocurrir en la aplicación.
- Bloc: Contiene la lógica de negocio y conecta los eventos con los cambios de estado.
- Repositorio: Encapsula la comunicación con servicios externos como Firebase.
Esta separación hace que nuestro código sea más mantenible, testeable y escalable. Podemos cambiar la implementación de cualquiera de estos componentes sin afectar a los demás.
La implementación de autenticación con Firebase utilizando BLoC nos proporciona una estructura sólida para gestionar el flujo de autenticación en nuestra aplicación Flutter. Este enfoque nos permite manejar de manera eficiente los estados de autenticación, los errores y las transiciones entre diferentes pantallas.
¿Has implementado BLoC en tus proyectos Flutter? ¿Qué otros patrones de gestión de estado has utilizado? Comparte tu experiencia en los comentarios.