6 Maneiras de Gerenciar Estado em Aplicativos Flutter (Com Exemplos de Código)
Como gerenciar o estado no Flutter
Neste artigo, exploraremos seis maneiras populares de gerenciar o estado no Flutter em aplicativos, incluindo exemplos reais e melhores práticas:
Gerenciamento de estado é um dos tópicos mais importantes — e debatidos — no desenvolvimento Flutter. Ele determina como seu aplicativo lida com mudanças de dados e atualiza a UI de forma eficiente. O Flutter oferece várias estratégias para gerenciar estado — desde simples até altamente escaláveis.
As estratégias de gerenciamento de estado no Flutter são:
- 🧱
setState()— A abordagem nativa e mais simples - 🏗️ InheritedWidget — A base do Flutter para propagação de estado
- 🪄 Provider — A solução recomendada para a maioria dos aplicativos
- 🔒 Riverpod — Evolução moderna e segura em tempo de compilação do Provider
- 📦 Bloc — Para aplicativos escaláveis de nível empresarial
- ⚡ GetX — Solução leve e completa (all-in-one)

1. Usando setState() — O Básico
A maneira mais simples de gerenciar estado no Flutter é usando o método nativo setState() em um StatefulWidget.
Esta abordagem é ideal para estado de UI local — onde o estado pertence a um único widget e não precisa ser compartilhado em todo o aplicativo.
Exemplo: App de Contador com setState()
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: CounterScreen());
}
}
class CounterScreen extends StatefulWidget {
@override
_CounterScreenState createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('setState Counter')),
body: Center(
child: Text('Count: $_count', style: TextStyle(fontSize: 24)),
),
floatingActionButton: FloatingActionButton(
onPressed: _increment,
child: Icon(Icons.add),
),
);
}
}
Prós
- Muito simples de implementar
- Excelente para estado de UI local ou temporário
- Sem dependências externas
Contras
- Não escala para aplicativos grandes
- Difícil de compartilhar estado entre widgets
- Lógica misturada com UI
Use quando:
- Fazendo protótipos ou construindo widgets pequenos
- Lidando com estado de UI isolado (ex: alternar um botão, mostrar um modal)
2. InheritedWidget — A Base do Flutter
InheritedWidget é o mecanismo de baixo nível que o Flutter usa para propagar dados para baixo na árvore de widgets. A maioria das soluções de gerenciamento de estado (incluindo o Provider) é construída sobre ele.
Compreender o InheritedWidget ajuda você a entender como o gerenciamento de estado do Flutter funciona internamente.
Exemplo: Gerenciador de Tema com InheritedWidget
import 'package:flutter/material.dart';
// O InheritedWidget que mantém os dados do tema
class AppTheme extends InheritedWidget {
final bool isDarkMode;
final Function toggleTheme;
const AppTheme({
Key? key,
required this.isDarkMode,
required this.toggleTheme,
required Widget child,
}) : super(key: key, child: child);
// Método de acesso para obter o AppTheme mais próximo na árvore de widgets
static AppTheme? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<AppTheme>();
}
@override
bool updateShouldNotify(AppTheme oldWidget) {
return isDarkMode != oldWidget.isDarkMode;
}
}
// Wrapper Stateful para gerenciar mudanças de estado
class ThemeManager extends StatefulWidget {
final Widget child;
const ThemeManager({Key? key, required this.child}) : super(key: key);
@override
_ThemeManagerState createState() => _ThemeManagerState();
}
class _ThemeManagerState extends State<ThemeManager> {
bool _isDarkMode = false;
void _toggleTheme() {
setState(() {
_isDarkMode = !_isDarkMode;
});
}
@override
Widget build(BuildContext context) {
return AppTheme(
isDarkMode: _isDarkMode,
toggleTheme: _toggleTheme,
child: widget.child,
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ThemeManager(
child: Builder(
builder: (context) {
final theme = AppTheme.of(context);
return MaterialApp(
theme: theme!.isDarkMode ? ThemeData.dark() : ThemeData.light(),
home: HomeScreen(),
);
},
),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = AppTheme.of(context);
return Scaffold(
appBar: AppBar(title: Text('InheritedWidget Theme')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Current Theme: ${theme!.isDarkMode ? "Dark" : "Light"}',
style: TextStyle(fontSize: 20),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => theme.toggleTheme(),
child: Text('Toggle Theme'),
),
],
),
),
);
}
}
Prós
- Incorporado ao Flutter — sem dependências externas
- Recriação de widgets eficiente
- Base para entender outras soluções
- Controle direto sobre a lógica de propagação
Contras
- Código boilerplate verboso
- Requer um wrapper StatefulWidget
- Fácil de cometer erros
- Não amigável para iniciantes
Use quando:
- Aprendendo como o gerenciamento de estado do Flutter funciona internamente
- Construindo soluções de gerenciamento de estado personalizadas
- Você precisa de controle muito específico sobre a propagação de estado
3. Provider — A Solução Recomendada pelo Flutter
Quando o estado do seu aplicativo precisa ser compartilhado entre múltiplos widgets, o Provider vem para salvar.
Provider é baseado em Inversão de Controle — em vez de widgets possuírem o estado, um provedor o expõe para que outros consumam. A equipe do Flutter recomenda oficialmente isso para aplicativos de tamanho médio.
Configuração
Adicione isso ao seu pubspec.yaml:
dependencies:
provider: ^6.0.5
Exemplo: App de Contador com Provider
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: MyApp(),
),
);
}
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: CounterScreen());
}
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = context.watch<CounterModel>();
return Scaffold(
appBar: AppBar(title: Text('Provider Counter')),
body: Center(
child: Text('Count: ${counter.count}', style: TextStyle(fontSize: 24)),
),
floatingActionButton: FloatingActionButton(
onPressed: context.read<CounterModel>().increment,
child: Icon(Icons.add),
),
);
}
}
Prós
- Atualizações reativas e eficientes
- Separação limpa entre UI e lógica
- Bem documentado e suportado pela comunidade
Contras
- Um pouco mais de boilerplate que
setState() - Provedores aninhados podem ficar complexos
Use quando:
- Você precisa de estado compartilhado entre múltiplos widgets
- Você quer um padrão reativo sem complexidade
- Seu aplicativo está crescendo além do tamanho de um protótipo
4. Riverpod — A Evolução Moderna do Provider
Riverpod é uma reescrita completa do Provider que remove a dependência do BuildContext e adiciona segurança em tempo de compilação. Ele foi projetado para resolver as limitações do Provider mantendo a mesma filosofia.
Configuração
Adicione isso ao seu pubspec.yaml:
dependencies:
flutter_riverpod: ^2.4.0
Exemplo: Perfil de Usuário com Riverpod
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// Model
class UserProfile {
final String name;
final int age;
UserProfile({required this.name, required this.age});
UserProfile copyWith({String? name, int? age}) {
return UserProfile(
name: name ?? this.name,
age: age ?? this.age,
);
}
}
// State Notifier
class ProfileNotifier extends StateNotifier<UserProfile> {
ProfileNotifier() : super(UserProfile(name: 'Guest', age: 0));
void updateName(String name) {
state = state.copyWith(name: name);
}
void updateAge(int age) {
state = state.copyWith(age: age);
}
}
// Provider
final profileProvider = StateNotifierProvider<ProfileNotifier, UserProfile>((ref) {
return ProfileNotifier();
});
// Computed provider
final greetingProvider = Provider<String>((ref) {
final profile = ref.watch(profileProvider);
return 'Hello, ${profile.name}! You are ${profile.age} years old.';
});
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: ProfileScreen());
}
}
class ProfileScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final profile = ref.watch(profileProvider);
final greeting = ref.watch(greetingProvider);
return Scaffold(
appBar: AppBar(title: Text('Riverpod Profile')),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(greeting, style: TextStyle(fontSize: 20)),
SizedBox(height: 30),
TextField(
decoration: InputDecoration(labelText: 'Name'),
onChanged: (value) {
ref.read(profileProvider.notifier).updateName(value);
},
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Age: ${profile.age}', style: TextStyle(fontSize: 18)),
SizedBox(width: 20),
ElevatedButton(
onPressed: () {
ref.read(profileProvider.notifier).updateAge(profile.age + 1);
},
child: Text('+'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () {
if (profile.age > 0) {
ref.read(profileProvider.notifier).updateAge(profile.age - 1);
}
},
child: Text('-'),
),
],
),
],
),
),
);
}
}
Prós
- Sem dependência de BuildContext — acesse o estado em qualquer lugar
- Segurança em tempo de compilação — capture erros antes da execução
- Melhor testabilidade
- Composição de estado poderosa
- Sem vazamentos de memória
Contras
- API diferente dos padrões tradicionais do Flutter
- Curva de aprendizado mais íngreme que o Provider
- Comunidade menor (mas crescendo rapidamente)
Use quando:
- Iniciando um novo projeto Flutter
- Você quer segurança de tipo e garantias em tempo de compilação
- Dependências e composições de estado complexas
- Trabalhando em aplicativos de médio a grande porte
5. BLoC (Componente de Lógica de Negócio) — Para Aplicativos Empresariais
O padrão BLoC é uma arquitetura mais avançada que separa completamente a lógica de negócios da UI usando streams (fluxos).
É ideal para aplicativos de grande escala ou empresariais, onde transições de estado previsíveis e testáveis são essenciais.
Configuração
Adicione esta dependência ao seu projeto:
dependencies:
flutter_bloc: ^9.0.0
Exemplo: App de Contador com BLoC
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// Events
abstract class CounterEvent {}
class Increment extends CounterEvent {}
// Bloc
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<Increment>((event, emit) => emit(state + 1));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterBloc(),
child: MaterialApp(home: CounterScreen()),
);
}
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Bloc Counter')),
body: Center(
child: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Text('Count: $count', style: TextStyle(fontSize: 24));
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(Increment()),
child: Icon(Icons.add),
),
);
}
}
Prós
- Escalável e mantível para aplicativos complexos
- Separação clara de camadas
- Fácil de testar com testes unitários
Contras
- Mais boilerplate
- Curva de aprendizado mais íngreme
Use quando:
- Construindo projetos empresariais ou de longo prazo
- Você precisa de lógica previsível e testável
- Vários desenvolvedores trabalham em diferentes módulos do aplicativo
6. GetX — A Solução Leve e Completa (All-in-One)
GetX é uma solução de gerenciamento de estado minimalista e poderosa que também inclui roteamento, injeção de dependência e utilitários. É conhecida por ter o menor boilerplate de todas as soluções.
Configuração
Adicione isso ao seu pubspec.yaml:
dependencies:
get: ^4.6.5
Exemplo: Lista de Compras com GetX
import 'package:flutter/material.dart';
import 'package:get/get.dart';
// Controller
class ShoppingController extends GetxController {
// Lista observável
var items = <String>[].obs;
// Contador observável
var totalItems = 0.obs;
void addItem(String item) {
if (item.isNotEmpty) {
items.add(item);
totalItems.value = items.length;
}
}
void removeItem(int index) {
items.removeAt(index);
totalItems.value = items.length;
}
void clearAll() {
items.clear();
totalItems.value = 0;
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'GetX Shopping List',
home: ShoppingScreen(),
);
}
}
class ShoppingScreen extends StatelessWidget {
// Inicializar controller
final ShoppingController controller = Get.put(ShoppingController());
final TextEditingController textController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GetX Shopping List'),
actions: [
Obx(() => Padding(
padding: EdgeInsets.all(16),
child: Center(
child: Text(
'Items: ${controller.totalItems}',
style: TextStyle(fontSize: 18),
),
),
)),
],
),
body: Column(
children: [
Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: TextField(
controller: textController,
decoration: InputDecoration(
hintText: 'Enter item name',
border: OutlineInputBorder(),
),
),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () {
controller.addItem(textController.text);
textController.clear();
},
child: Icon(Icons.add),
),
],
),
),
Expanded(
child: Obx(() {
if (controller.items.isEmpty) {
return Center(
child: Text('No items in your list'),
);
}
return ListView.builder(
itemCount: controller.items.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(child: Text('${index + 1}')),
title: Text(controller.items[index]),
trailing: IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => controller.removeItem(index),
),
);
},
);
}),
),
if (controller.items.isNotEmpty)
Padding(
padding: EdgeInsets.all(16),
child: ElevatedButton(
onPressed: () {
Get.defaultDialog(
title: 'Clear List',
middleText: 'Are you sure you want to clear all items?',
textConfirm: 'Yes',
textCancel: 'No',
onConfirm: () {
controller.clearAll();
Get.back();
},
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
minimumSize: Size(double.infinity, 50),
),
child: Text('Clear All'),
),
),
],
),
);
}
}
Prós
- Boilerplate mínimo — desenvolvimento mais rápido
- Solução completa (estado, roteamento, DI, snackbars, dialogs)
- Sem necessidade de BuildContext
- Muito leve e rápido
- Fácil de aprender
Contras
- Menos “estilo Flutter” comparado ao Provider/BLoC
- Pode levar a acoplamento forte se não tiver cuidado
- Ecossistema menor que o Provider
- Alguns desenvolvedores acham “mágico demais”
Use quando:
- Prototipação rápida ou desenvolvimento de MVP
- Aplicativos pequenos a médios
- Você quer boilerplate mínimo
- A equipe prefere simplicidade em vez de arquitetura estrita
Escolhendo a Solução de Gerenciamento de Estado Certa
| Solução | Complexidade | Boilerplate | Curva de Aprendizado | Melhor Para |
|---|---|---|---|---|
🧱 setState() |
Baixa | Mínimo | Fácil | Estado local simples, protótipos |
🏗️ InheritedWidget |
Média | Alta | Média | Aprender a fundo do Flutter, soluções personalizadas |
🪄 Provider |
Baixa-Média | Baixo | Fácil | A maioria dos apps, estado compartilhado |
🔒 Riverpod |
Média | Baixo-Médio | Médio | Apps modernos, segurança de tipos |
📦 BLoC |
Alta | Alta | Íngreme | Apps empresariais, lógica de negócios complexa |
⚡ GetX |
Baixa | Mínimo | Fácil | Desenvolvimento rápido, MVPs |
Guia Rápido de Decisão:
| Complexidade do App | Abordagem Recomendada | Caso de Uso Exemplo |
|---|---|---|
| 🪶 Simples | setState() ou GetX |
Botões de alternância, animações, widgets pequenos |
| ⚖️ Médio | Provider ou Riverpod |
Temas compartilhados, configurações de usuário, cache de dados |
| 🏗️ Complexo | BLoC ou Riverpod |
E-commerce, apps de chat, painéis financeiros |
Pensamentos Finais
Não existe uma solução única para o gerenciamento de estado no Flutter.
Aqui está uma abordagem prática:
- Comece pequeno com
setState()para widgets simples e protótipos - Aprenda os fundamentos com InheritedWidget para entender como o Flutter funciona internamente
- Progrida para o Provider conforme seu aplicativo cresce e precisa de estado compartilhado
- Considere o Riverpod para novos projetos onde segurança de tipos e padrões modernos importam
- Adote o BLoC para grandes aplicativos empresariais com lógica de negócios complexa
- Experimente o GetX quando o desenvolvimento rápido e boilerplate mínimo forem prioridades
Principais Pontos:
✅ Não superengenharia — use setState() até precisar de mais
✅ Provider e Riverpod são excelentes escolhas para a maioria das aplicações
✅ BLoC brilha em grandes equipes e aplicativos complexos
✅ GetX é ótimo para velocidade, mas tenha cuidado com o acoplamento
✅ Entender o InheritedWidget ajuda você a dominar qualquer solução
A chave é equilibrar simplicidade, escalabilidade e manutenibilidade — e escolher a ferramenta certa para suas necessidades específicas, experiência da equipe e requisitos do projeto.
Links para Bibliotecas de Gerenciamento de Estado do Flutter
- Documentação Oficial do Flutter sobre Gerenciamento de Estado
- Pacote Provider
- Documentação do Riverpod
- Biblioteca BLoC
- Documentação do GetX