Os 15 tops packages de flutter
🏗️ Gerenciamento de Estado e Arquitetura
1. Riverpod
O sucessor espiritual do Provider. É reativo, seguro contra erros em tempo de compilação e não depende do BuildContext.
-
Exemplo:
final counterProvider = StateProvider((ref) => 0); -
Utilidade: Gerenciamento de estado global e injeção de dependências.
// Definição do provedor
final counterProvider = StateProvider((ref) => 0);
// Uso no Widget
class Home extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).state++,
child: Text('Contagem: $count'),
);
}
}
2. Flutter BLoC
Implementa o padrão Business Logic Component. Separa a interface da lógica de forma rígida e testável.
-
Exemplo: Uso de
BlocBuildereBlocListenerpara reagir a estados. -
Utilidade: Apps de grande escala com fluxos complexos.
// Evento
abstract class CounterEvent {}
class Increment extends CounterEvent {}
// Bloc
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<Increment>((event, emit) => emit(state + 1));
}
}
// UI
BlocBuilder<CounterBloc, int>(
builder: (context, state) => Text('$state'),
);
3. GetIt
Um localizador de serviços (Service Locator) extremamente simples e rápido.
-
Exemplo:
getIt.registerSingleton<ApiService>(ApiService()); -
Utilidade: Acessar objetos e instâncias de qualquer lugar do código.
final getIt = GetIt.instance;
void setup() {
getIt.registerSingleton<ApiService>(ApiService());
}
// Acessando em qualquer lugar:
final api = getIt<ApiService>();
🗄️ Persistência de Dados (Local e Nuvem)
4. Drift
Banco de dados SQL potente e reativo. Como discutido anteriormente, é ideal para dados relacionais complexos.
-
Exemplo: Permite escrever queries SQL puras com segurança de tipos em Dart.
-
Utilidade: Apps “Offline-First” com tabelas interligadas.
class Tasks extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 1, max: 50)();
}
// Query
final allTasks = await select(tasks).get();
5. Isar
O banco NoSQL mais rápido para Flutter, otimizado para alta performance e múltiplos núcleos.
-
Exemplo: Consultas avançadas com filtros nativos:
isar.users.filter().nameStartsWith("A").findAll();. -
Utilidade: Armazenar grandes volumes de objetos com rapidez extrema.
@collection
class User {
Id id = Isar.autoIncrement;
late String name;
}
// Inserindo
await isar.writeTxn(() => isar.users.put(newUser));
// Consultando
final users = await isar.users.filter().nameStartsWith("Jo").findAll();
6. Cloud Firestore
A solução de nuvem do Firebase para sincronização em tempo real entre dispositivos.
-
Exemplo:
collection('chats').snapshots()para ouvir mensagens novas instantaneamente. -
Utilidade: Apps colaborativos, chats e dashboards em tempo real.
StreamBuilder(
stream: FirebaseFirestore.instance.collection('mensagens').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
return ListView(children: snapshot.data.docs.map((doc) => Text(doc['texto'])));
},
);
🌐 Networking e APIs
7. Dio
Um cliente HTTP poderoso com suporte a interceptadores, cancelamento de requisições e upload de arquivos.
-
Exemplo:
dio.get('/user', queryParameters: {'id': 123}); -
Utilidade: O padrão ouro para consumo de APIs REST no Flutter.
final dio = Dio();
try {
final response = await dio.get('https://api.exemplo.com/dados');
print(response.data);
} on DioException catch (e) {
print(e.message);
}
8. Freezed
Gerador de código para classes de dados imutáveis, uniões (unions) e clonagem.
-
Exemplo: Cria automaticamente métodos
copyWithefromJson. -
Utilidade: Elimina código repetitivo (boilerplate) em modelos e estados.
@freezed
class User with _$User {
const factory User({
required String name,
required int age,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
🎨 Interface e Animações
9. Lottie
Renderiza animações do Adobe After Effects exportadas como JSON.
-
Exemplo:
Lottie.asset('assets/animation.json'); -
Utilidade: Adicionar animações complexas e leves sem perda de qualidade.
Lottie.asset('assets/success_animation.json', width: 200, height: 200);
10. Flutter Spinkit
Uma coleção de indicadores de carregamento (loaders) estilizados.
-
Exemplo:
SpinKitFadingCircle(color: Colors.white, size: 50.0); -
Utilidade: Melhorar o feedback visual durante requisições assíncronas.
SpinKitRotatingCircle( color: Colors.blue, size: 50.0, );
11. Shimmer
Cria aquele efeito de “carregamento brilhante” que vemos no Facebook ou Instagram.
-
Exemplo: Envolver widgets em um
Shimmer.fromColors. -
Utilidade: Placeholder elegante enquanto os dados reais não carregam.
Shimmer.fromColors( baseColor: Colors.grey[300]!, highlightColor: Colors.grey[100]!, child: Container(width: 200, height: 20, color: Colors.white), );
🛠️ Ferramentas Utilitárias e Segurança
12. Flutter Secure Storage
Armazena dados sensíveis (como JWT ou tokens) usando Keychain (iOS) e Keystore (Android).
-
Exemplo:
storage.write(key: 'jwt', value: token);. -
Utilidade: Proteção de credenciais que não devem ficar no SQLite ou SharedPreferences.
final storage = FlutterSecureStorage(); // Escrever await storage.write(key: 'auth_token', value: 'meu_jwt_secreto'); // Ler String? token = await storage.read(key: 'auth_token');
13. Go Router
O pacote oficial para navegação declarativa, lidando facilmente com rotas aninhadas e Deep Linking.
-
Exemplo: Define rotas de forma simples:
GoRoute(path: '/user/:id', builder: ...). -
Utilidade: Navegação robusta, especialmente importante para Flutter Web.
final _router = GoRouter(
routes: [
GoRoute(
path: '/perfil/:userId',
builder: (context, state) => ProfileScreen(id: state.pathParameters['userId']!),
),
],
);
14. Intl
Ferramenta para internacionalização e localização (tradução de textos e formatos de data/moeda).
-
Exemplo:
DateFormat.yMMMd().format(DateTime.now()); -
Utilidade: Essencial para apps que desejam atingir o mercado global.
String preco = NumberFormat.simpleCurrency(locale: 'pt_BR').format(1500.50); // Resultado: R$ 1.500,50
15. Flutter Dotenv
Carrega variáveis de ambiente de um arquivo .env.
-
Exemplo:
dotenv.env['BASE_URL'];. -
Aviso de Segurança: Como vimos, use apenas para configurações públicas (como URLs de teste). Nunca coloque segredos de faturamento ou chaves privadas aqui, pois podem ser extraídos do binário.
// No arquivo .env: BASE_URL=https://api.meuapp.com await dotenv.load(fileName: ".env"); String url = dotenv.env['BASE_URL'] ?? 'fallback.com';
🛡️ Lembrete de Segurança Importante (Reforço)
Como vimos nos textos anteriores sobre segurança em Flutter:
-
Use o Flutter Secure Storage para tokens e segredos do usuário.
-
Nunca coloque chaves de API pagas (como Google Maps ou Stripe) no seu arquivo
.envou direto no código se for para produção. -
Em vez disso, faça o app chamar o seu Backend, e o seu backend faz a chamada para a API paga protegendo a chave. Se a chave estiver no
.env, ela pode ser extraída via engenharia reversa do APK, gerando faturas altíssimas para você!
📊 Tabela de Decisão Rápida
| Objetivo | Sugestão |
| Escalabilidade | Flutter BLoC + Riverpod |
| Velocidade Local | Isar |
| Sincronização Nuvem | Firestore |
| Segurança de Tokens | Flutter Secure Storage |
Gerenciador de Finanças Pessoais
Este exemplo mostra como os pacotes se conectam desde a segurança do token até à exibição reativa dos dados.
📦 Os 7 Packages Utilizados:
-
Riverpod: Controlo de estado e injeção de dependências.
-
Dio: Chamadas HTTP para a API de cotações.
-
Drift: Base de dados SQL local para guardar transações.
-
Flutter Secure Storage: Para guardar a chave privada do utilizador.
-
Freezed: Modelos de dados imutáveis com JSON.
-
Intl: Formatação de moeda (R$) e datas.
-
Flutter Spinkit: Feedback visual de carregamento.
1. O Modelo de Dados (Freezed)
Define a estrutura de uma transação financeira.
import 'package:freezed_annotation/freezed_annotation.dart';
part 'transaction.freezed.dart';
part 'transaction.g.dart';
@freezed
class Transaction with _$Transaction {
const factory Transaction({
required int id,
required String description,
required double value,
required DateTime date,
}) = _Transaction;
factory Transaction.fromJson(Map<String, dynamic> json) => _$TransactionFromJson(json);
}
2. Base de Dados Local (Drift)
Cria a tabela e a lógica de persistência local.
import 'package:drift/drift.dart';
// ... imports do drift ...
class LocalTransactions extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get description => text()();
RealColumn get value => real()();
DateTimeColumn get date => dateTime()();
}
3. Lógica de Negócio e API (Riverpod + Dio + SecureStorage)
Este “Provider” gere tudo: verifica o token, vai à API buscar cotações e lê do banco local.
final financeProvider = StateNotifierProvider<FinanceNotifier, AsyncValue<List<Transaction>>>((ref) {
return FinanceNotifier();
});
class FinanceNotifier extends StateNotifier<AsyncValue<List<Transaction>>> {
final _dio = Dio();
final _storage = const FlutterSecureStorage();
FinanceNotifier() : super(const AsyncValue.loading()) {
_init();
}
Future<void> _init() async {
try {
// 1. Recuperar chave de segurança
final apiKey = await _storage.read(key: 'user_api_key');
// 2. Exemplo de chamada API com Dio
final response = await _dio.get('https://api.exemplo.com/v1/finance?key=$apiKey');
// 3. Mapear dados para o modelo Freezed
final List data = response.data['transactions'];
final transactions = data.map((e) => Transaction.fromJson(e)).toList();
state = AsyncValue.data(transactions);
} catch (e, st) {
state = AsyncValue.error(e, st);
}
}
}
4. A Interface (Spinkit + Intl)
A parte visual que consome o estado reativo.
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:intl/intl.dart';
class FinanceHomePage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(financeProvider);
return Scaffold(
appBar: AppBar(title: const Text('Meu Extrato')),
body: state.when(
loading: () => const Center(
child: SpinKitDoubleBounce(color: Colors.green, size: 50.0), // Spinkit
),
error: (err, _) => Center(child: Text('Erro ao carregar: $err')),
data: (transactions) => ListView.builder(
itemCount: transactions.length,
itemBuilder: (context, index) {
final item = transactions[index];
// Intl: Formatação para Real Brasileiro e Data
final currency = NumberFormat.simpleCurrency(locale: 'pt_BR').format(item.value);
final date = DateFormat.yMd('pt_BR').format(item.date);
return ListTile(
leading: const Icon(Icons.monetization_on, color: Colors.green),
title: Text(item.description),
subtitle: Text(date),
trailing: Text(currency, style: const TextStyle(fontWeight: FontWeight.bold)),
);
},
),
),
);
}
}
🚀 Por que este projeto é “nível profissional”?
-
Segurança: O token de acesso não está “hardcoded” nem em ficheiros de texto simples; está no Secure Storage (encriptado pelo SO).
-
Performance: O Drift permite que o app funcione offline. O Dio lida com timeouts e interceptores de forma eficiente.
-
Manutenção: Se a API mudar um campo, o Freezed ajuda a identificar o erro rapidamente. O Riverpod garante que a UI só reconstrói quando o dado realmente muda.
-
UX: O utilizador nunca vê uma tela branca ou travada, graças ao Spinkit e ao estado
AsyncValue.
Dica Final: Para colocar este projeto a funcionar, não se esqueça de correr o comando dart run build_runner build no terminal para gerar os ficheiros automáticos do Drift e do Freezed!