Gerenciamento de Estado Avançado com Cubit e Sealed Classes no Flutter
Introdução
O Cubit, parte do pacote flutter_bloc
, é uma solução simples e poderosa para gerenciar estado em Flutter. Ele permite criar estados reativos e previsíveis com uma curva de aprendizado menor que o BLoC. Combinado com Sealed Classes, o Dart oferece um sistema de tipos mais robusto para estruturar estados complexos, garantindo mais segurança e legibilidade no código.
Neste tutorial, você aprenderá a implementar o gerenciamento de estado usando Cubit e Sealed Classes no Dart, explorando os seguintes tópicos:
- Configuração inicial do projeto
- Criação de estados usando Sealed Classes
- Implementação de um Cubit para gerenciar estados
- Integração com widgets do Flutter
- Boas práticas para arquitetura e testes
1. Configurando o Projeto
Adicione o pacote flutter_bloc
ao seu projeto:
dependencies:
flutter_bloc: ^8.0.0
Certifique-se de que está usando uma versão recente do Dart que suporte Sealed Classes (Dart 3.0 ou superior).
2. Criando Estados com Sealed Classes
Sealed Classes permitem criar hierarquias de estados que são fáceis de manter e garantem que todas as possibilidades sejam tratadas. Aqui está um exemplo para um sistema de autenticação:
sealed class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class Authenticated extends AuthState {
final String username;
Authenticated(this.username);
}
class AuthError extends AuthState {
final String message;
AuthError(this.message);
}
Com essa estrutura, é possível representar os estados iniciais, de carregamento, autenticado e erro de forma clara e imutável.
3. Criando o Cubit
O Cubit é responsável por gerenciar as transições entre os estados definidos. Aqui está um exemplo:
import 'package:flutter_bloc/flutter_bloc.dart';
class AuthCubit extends Cubit {
AuthCubit() : super(AuthInitial());
Future login(String username, String password) async {
emit(AuthLoading());
try {
// Simulação de autenticação
await Future.delayed(Duration(seconds: 2));
if (username == "admin" && password == "1234") {
emit(Authenticated(username));
} else {
emit(AuthError("Credenciais inválidas"));
}
} catch (e) {
emit(AuthError("Erro inesperado"));
}
}
void logout() {
emit(AuthInitial());
}
}
O método login
simula uma autenticação e altera os estados com base no sucesso ou falha da operação.
4. Consumindo o Cubit no Flutter
Com o Cubit configurado, você pode usá-lo em widgets Flutter. Aqui está um exemplo de integração:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class AuthScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => AuthCubit(),
child: Scaffold(
appBar: AppBar(title: Text("Cubit + Sealed Classes")),
body: BlocConsumer(
listener: (context, state) {
if (state is Authenticated) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Bem-vindo, \${state.username}!")),
);
} else if (state is AuthError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message)),
);
}
},
builder: (context, state) {
if (state is AuthInitial) {
return _buildLoginForm(context);
} else if (state is AuthLoading) {
return Center(child: CircularProgressIndicator());
} else if (state is Authenticated) {
return _buildAuthenticatedScreen(context, state);
} else if (state is AuthError) {
return _buildLoginForm(context, errorMessage: state.message);
}
return Container();
},
),
),
);
}
Widget _buildLoginForm(BuildContext context, {String? errorMessage}) {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (errorMessage != null) ...[
Text(errorMessage, style: TextStyle(color: Colors.red)),
SizedBox(height: 8),
],
TextField(
controller: usernameController,
decoration: InputDecoration(labelText: "Usuário"),
),
TextField(
controller: passwordController,
decoration: InputDecoration(labelText: "Senha"),
obscureText: true,
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
context.read().login(
usernameController.text,
passwordController.text,
);
},
child: Text("Entrar"),
),
],
),
);
}
Widget _buildAuthenticatedScreen(BuildContext context, Authenticated state) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Bem-vindo, \${state.username}!", style: TextStyle(fontSize: 24)),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => context.read().logout(),
child: Text("Sair"),
),
],
),
);
}
}
Conclusão
Combinando o poder do Cubit e das Sealed Classes, você pode criar sistemas de gerenciamento de estado robustos, escaláveis e fáceis de manter. Este tutorial demonstrou como estruturar estados complexos e integrá-los eficientemente em widgets Flutter. Para projetos maiores, considere usar BLoC, que é uma extensão natural do Cubit, adicionando suporte completo a eventos.