Interações Flutter no ponto.
Criado por
IldebertoÚltimas Descobertas
Blur Automático no App Switcher
Galeria 2D Infinita
Sem Material Design? Vá de Forui.
Ações de Deslize com Dismissible
Animated Like Button
Dynamic Moving Backgrounds
Extração Dinâmica de Cores (Estilo Spotify)
Galeria Interativa com Pinch & Zoom
Hux UI: Estado da Arte
Globo 3D Interativo com GoodMap
Expandable FAB
Animações 'Orb' em Flutter
Adiciona botões personalizados ao teclado
Easy Wave Animations with `wave`
Se arrependeu? Desfaz a ação
Onboarding Liquid Swipe
Emojis Animados
Escudo de Privacidade Biométrica 🛡️
Ao desenvolver aplicativos bancários, de criptomoedas ou de mensagens, a segurança é primordial. Um vazamento comum de dados privados acontece quando o usuário desliza para cima para entrar no Multitarefa (App Switcher) do sistema. O SO tira uma “captura de tela” do app para exibir na miniatura, podendo expor saldos de contas ou mensagens privadas para quem estiver olhando por cima do ombro.
Neste tutorial, resolvemos isso injetando um desfoque (blur) no exato milissegundo em que o app entra em segundo plano.
O Segredo: WidgetsBindingObserver
Não precisamos de pacotes externos para isso. O Flutter fornece uma forma nativa de ouvir eventos de ciclo de vida do SO através do mixin WidgetsBindingObserver.
Observando o método didChangeAppLifecycleState, podemos detectar exatamente quando o SO dispara o estado inactive ou paused.
AppLifecycleState.inactive: O app ainda está visível, mas não pode receber toques (ex: o usuário acabou de começar a arrastar o dedo para cima no iOS para abrir o Multitarefa).AppLifecycleState.paused: O app está totalmente em segundo plano.
A Implementação
Quando o estado muda para inactive, definimos instantaneamente uma variável booleana _isBackgrounded como true.
No nosso método build, envolvemos a tela inteira em um Stack. Se _isBackgrounded for true, renderizamos uma camada Positioned.fill com um BackdropFilter configurado com ImageFilter.blur(sigmaX: 15, sigmaY: 15).
Isso garante que a captura de tela do SO feita para o App Switcher capture apenas o nosso belo escudo de privacidade de vidro fosco!
Design Adaptativo com Cores Dinâmicas 🎨
Um dos detalhes mais luxuosos de aplicativos de música (como Apple Music e Spotify) é como a interface inteira se transforma para abraçar a arte do álbum que você está ouvindo. O fundo, os gradientes e até os botões mudam de cor magicamente.
Neste tutorial, replicamos essa arquitetura premium construindo um Album View reativo usando o pacote palette_generator_plus.
Por que “Plus”?
O pacote original do Google (palette_generator) foi descontinuado. Nós atualizámos a nossa arquitetura para usar o sucessor moderno mantido pela comunidade, o palette_generator_plus, que oferece performance otimizada (Isolates) e extração de cor em perfeita sintonia com o Material 3.
O Fluxo de Extração
A mágica acontece em 3 passos simples na nossa função _extractColors():
- Carregamento: Criamos um
NetworkImagecom a capa do nosso álbum (uma imagem neon de alta qualidade do Unsplash). - Clustering Analítico: Passamos essa imagem para o
PaletteGenerator.fromImageProvider. O pacote varre os pixels da imagem e agrupa as cores, devolvendo-nos a Paleta. - Injeção de Cor: Pegamos a cor mais vibrante da imagem usando
paletteGenerator.dominantColor?.color.
Bônus de UX (Acessibilidade)
E se a cor da imagem for um branco super claro e o nosso título sumir na tela? O paletteGenerator já calcula a acessibilidade matemática pra nós! Usamos a propriedade titleTextColor que ele nos fornece para garantir que o título da música terá contraste legível e perfeito contra o gradiente do fundo.
Tudo isso acontece dentro de um AnimatedContainer e de um AnimatedDefaultTextStyle, fazendo com que a UI transicione como seda no instante em que a imagem termina de carregar.
Domínio do Teclado Nativo ⌨️
Um dos maiores diferenciais de apps premium é como eles interagem com a digitação. Usar o teclado padrão do sistema operacional resolve o básico, mas desenvolvedores de elite injetam lógicas customizadas diretas no teclado.
Usando a versão mais atual do pacote keyboard_actions (v5+), nós não apenas resolvemos o clássico problema do iOS (a falta do botão “Done” em teclados numéricos), mas fomos além, criando Ações Rápidas (Quick Actions).
O Que Foi Construído
Ao envolver a tela no widget KeyboardActions, injetamos um overlay (camada de topo) perfeitamente alinhado com o teclado:
-
Campo Numérico (Correção iOS): Adicionamos o famoso botão azul “Done” para desativar o campo numérico (algo que a Apple não entrega nativamente no teclado numérico do iPhone). Também ativamos o
displayArrows, que permite o usuário pular de um input para o outro sem tocar na tela. -
Campo de Texto Genérico (Quick Actions): Aqui está a mágica! Se o usuário abrir o teclado no campo de notas, o overlay agora exibe botões dinâmicos:
- Um botão de Hashtag (#) que injeta a tag ‘#flutter’ instantaneamente na string do TextEditingController.
- Um botão de Menção (@) para marcar a ‘@equipa’.
- Um botão vermelho de Limpar alinhado à direita (usando um
Spacer()) para deletar todo o texto num piscar de olhos.
Essa abordagem de UX poupa dezenas de cliques do usuário em formulários e apps de anotação.
Motor de Galeria 2D Infinita 🗺️
Às vezes, GridView e InteractiveViewer simplesmente não são suficientes quando você deseja construir um canvas verdadeiramente ilimitado (como um mapa ou uma grade de fotos infinita) que pareça completamente nativo e lide com o momento natural do scroll.
Neste tutorial avançado, construímos um Motor de Rolagem 2D Customizado do zero, com direito a layout assimétrico (staggered)!
1. Física de Momentum Natural (Fling)
Para simular a rolagem realista, capturamos a velocidade (velocity) quando o usuário levanta o dedo em onScaleEnd. Calculamos a distância de frenagem e animamos nosso offset usando uma curva (Curves.easeOutCubic) para imitar o atrito físico!
void _onScaleEnd(ScaleEndDetails details) {
final velocity = details.velocity.pixelsPerSecond;
final distance = velocity.distance * 0.15;
if (distance > 10) {
final direction = velocity / velocity.distance;
final endOffset = _offset + (direction * distance);
_animation = Tween<Offset>(begin: _offset, end: endOffset).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic)
);
_controller.forward(from: 0);
}
}
2. Movimento de Pinça com Foco Preciso
Se simplesmente multiplicarmos nossos itens pelo _scale, o zoom centralizará no canto superior esquerdo da tela. Para fazê-lo dar zoom exatamente onde o usuário está fazendo a pinça (o focalPoint), aplicamos este truque de matemática vetorial:
final newScale = (_baseScale * details.scale).clamp(0.3, 3.0);
final scaleRatio = newScale / _scale;
// Mantém o ponto focal travado deslocando o offset
_offset = details.localFocalPoint - (details.localFocalPoint - _offset) * scaleRatio;
_scale = newScale;
3. A Matemática do Staggered (Favo de Mel)
Para fazer a galeria parecer mais orgânica em vez de um tabuleiro de xadrez rígido, nós deslocamos todas as colunas ímpares para baixo exatamente pela metade do tamanho do bloco (tileSize / 2).
// Se o índice da coluna for ímpar, empurre o bloco para baixo pela metade de sua altura
final double staggeredOffsetY = (col.abs() % 2 == 1) ? (tileSize / 2) : 0.0;
final double x = col * tileSize + _offset.dx;
final double y = row * tileSize + _offset.dy + staggeredOffsetY;
4. Renderização de Alta Performance (Lazy)
Uma grade verdadeiramente infinita travaria o app se renderizássemos todos os itens. Em vez disso, calculamos exatamente quantas colunas e linhas cabem na tela com base na escala atual e geramos apenas os widgets Positioned dos blocos visíveis. Isso torna a galeria incrivelmente fluida, mesmo que ela se estenda por milhões de pixels teóricos!
Galeria de Imagens Interativa 🖼️
Um requisito muito comum em apps modernos é uma galeria de fotos onde o usuário pode clicar numa imagem para abri-la em tela cheia, deslizar entre as fotos e dar zoom para ver os detalhes.
No passado, os desenvolvedores dependiam de pacotes de terceiros pesados para conseguir isso. Mas hoje o Flutter possui um widget nativo super poderoso para esse exato propósito: o InteractiveViewer.
Neste tutorial, nós combinamos três mecânicas nativas:
- Canvas 2D Infinito: Envolvemos uma grade gigante de 20x20 imagens dentro de um
InteractiveViewercomconstrained: false. Isso permite que o usuário navegue arrastando livremente para cima, para baixo ou para as diagonais, além de poder fazer o movimento de pinça para afastar e ver o “mapa” completo de fotos! - Animações
Hero: Para a transição fluida do Canvas para a tela cheia. - Duplo Clique Inteligente: Matemática customizada para dar zoom exatamente onde o dedo tocou na tela cheia.
O InteractiveViewer
O widget InteractiveViewer lida com gestos multi-touch automaticamente sem precisar de configurações complexas.
InteractiveViewer(
panEnabled: true,
scaleEnabled: true,
minScale: 1.0, // Impede que o usuário diminua a foto mais que o tamanho da tela
maxScale: 4.0, // Limite máximo de zoom
child: Image.network(url),
)
Adicionando “Dois toques para dar Zoom”
Embora o movimento de pinça seja automático, os usuários também esperam poder dar “dois toques” rápidos na tela para aproximar e afastar a imagem. Para conseguir isso, capturamos a posição exata do toque usando o GestureDetector e usamos um TransformationController misturado com um AnimationController para animar a escala suavemente!
// 1. Captura a coordenada exata onde o usuário tocou
void _handleDoubleTapDown(TapDownDetails details) {
_doubleTapDetails = details;
}
// 2. Anima a matriz para dar escala exatamente naquele ponto
void _handleDoubleTap() {
final position = _doubleTapDetails!.localPosition;
final isZoomed = _transformationController.value.getMaxScaleOnAxis() > 1.0;
final endMatrix = isZoomed
? Matrix4.identity()
: Matrix4.identity()
..translate(-position.dx * 2, -position.dy * 2)
..scale(3.0);
_animation = Matrix4Tween(
begin: _transformationController.value,
end: endMatrix,
).animate(_animationController);
_animationController.forward(from: 0);
}
Isso garante que o seu aplicativo passe exatamente a mesma sensação de uma galeria nativa do iOS ou Android, sem adicionar nenhuma dependência externa!
Beautiful Waves without the Math 🌊
Criar animações complexas de ondas usando CustomPainters e fórmulas matemáticas pode tomar muito tempo. Se você quer um resultado incrível e rápido para fundos de tela, cabeçalhos ou efeitos visuais, o pacote wave é a melhor escolha!
Neste mini tutorial, vamos criar uma animação de onda multi-camadas em poucos passos.
1. Instalação
Primeiro, adicione o pacote wave ao seu arquivo pubspec.yaml:
dependencies:
wave: ^0.2.2 # verifique a versão mais recente no pub.dev
Depois, rode flutter pub get no seu terminal.
2. Configurando o WaveWidget
O segredo deste pacote está no WaveWidget. Ele precisa de uma configuração (config) onde definimos como cada camada da onda vai se comportar.
No código abaixo, nós usamos o CustomConfig para criar 4 camadas de ondas com cores e velocidades diferentes:
import 'package:flutter/material.dart';
import 'package:wave/wave.dart';
class WaveBackground extends StatelessWidget {
@override
Widget build(BuildContext context) {
return WaveWidget(
config: CustomConfig(
// Cores de cada camada da onda (da mais atrás para a mais à frente)
colors: [
Colors.blue.shade100,
Colors.blueAccent.shade200,
Colors.lightBlueAccent.shade200,
Colors.black54,
],
// Velocidade da onda em milissegundos. Menos tempo = mais rápido
durations: [3000, 4000, 5000, 6500],
// A altura da onda em relação ao container (0.0 até 1.0)
heightPercentages: [0.64, 0.66, 0.68, 0.70],
),
// O tamanho que o widget irá ocupar
size: const Size(double.infinity, double.infinity),
);
}
}
3. Entendendo os Parâmetros 🛠️
colors: É uma lista de cores. Cada color representa uma camada da onda.durations: Controla a velocidade de cada onda. A primeira onda (azul claro) leva 3000ms para completar o ciclo, enquanto a última (preta) é mais lenta e leva 6500ms. O contraste nas velocidades cria o efeito realista da água!heightPercentages: Define a altura base de cada onda.0.64significa que a onda começa a partir de 64% da altura da tela (do topo para baixo).
Dica de UI ✨
Experimente usar gradientes (propriedade gradients em vez de colors) no CustomConfig ou usar cores com opacidade (Colors.blue.withOpacity(0.5)) para que as ondas se sobreponham de forma suave e pareçam translúcidas!
A Tela em Branco: Flutter Além do Material 🎨
Com as atualizações recentes do Flutter (especialmente rumo ao Flutter 3.47+), o framework tem se distanciado cada vez mais de forçar o Material Design goela abaixo de todo desenvolvedor. A arquitetura central foi modularizada, provando de uma vez por todas: O Flutter não é um construtor de apps do Google, ele é um motor gráfico puro (um canvas).
Se você está cansado daquela estética padrão com cara de Android e quer um visual elegante, moderno e minimalista (fortemente inspirado no lendário shadcn/ui), o Forui é o seu melhor amigo.
1. Abandonando a Dependência do Material
Dê uma olhada no topo do seu arquivo dart. Em vez disso:
import 'package:flutter/material.dart'; // ❌ Não precisamos mais disso!
Agora você pode usar estritamente os widgets centrais e o seu novo Design System:
import 'package:flutter/widgets.dart'; // ✅ Core Puro do Flutter
import 'package:forui/forui.dart'; // ✅ Design System Forui
2. Os F-Widgets
O Forui substitui os componentes padrão do Material por componentes com o prefixo F, que seguem uma linguagem de design rigorosa e minimalista.
FScaffoldno lugar deScaffoldFButtonno lugar deElevatedButtonFTextFieldno lugar deTextField
FButton(
onPress: () => print('Olá Forui!'),
child: const Text('Clique aqui'),
)
3. O Poder dos Temas Customizados
Ao envolver seu aplicativo em FTheme(data: FTheme.neutral.dark.touch, ...) dentro de um WidgetsApp nativo, cada componente se adapta instantaneamente a um modo escuro de alto contraste, com cantos perfeitamente arredondados e bordas sutis. Chega de brigar com o ThemeData ou lidar com erros de semântica causados pelo MaterialApp!
4. Mais Informações
Para explorar mais sobre esse design system incrível, confira os links oficiais:
- Pub.dev: https://pub.dev/packages/forui
- GitHub: https://github.com/duobaseio/forui
Abrace a era da “Tela em Branco” (Blank Canvas). Seus aplicativos estão prestes a ficar com uma cara muito mais premium.
Hux UI: O Futuro das Interfaces no Flutter 🚀
Se você está procurando por uma biblioteca de UI “estado da arte” (state-of-the-art) que traga componentes altamente polidos, animados e consistentes para o Flutter, o Hux UI é exatamente o que você precisa.
Projetado pelo brilhante pessoal da LofiDesigner, o Hux UI oferece componentes que parecem incrivelmente premium logo de cara, reduzindo drasticamente o tempo que você gasta ajustando preenchimentos, sombras e estados de hover.
1. Por que Hux UI?
O Hux UI se destaca por trazer uma linguagem de design limpa e moderna para seus aplicativos. Ele fornece:
- Múltiplas variações para componentes nativamente (ex: primary, secondary, outline, ghost).
- Gráficos para visualização de dados.
- Estruturas avançadas de Abas (Tabs) e Navegação.
- Uma estética deslumbrante pensada primeiro para o Modo Escuro (Dark Mode).
2. Usando Componentes Hux
A integração do Hux é super simples. Substitua componentes padrão do Material pelos seus equivalentes com prefixo Hux.
// Um botão lindamente estilizado com diferentes variantes
HuxButton(
variant: HuxButtonVariant.primary,
onPressed: () => print('Olá Hux!'),
child: const Text('Começar Agora'),
)
// Um campo de entrada limpo e moderno
HuxInput(
hint: 'nome@exemplo.com',
)
3. O Poder dos Cards
O HuxCard é um dos componentes mais versáteis do sistema, aplicando automaticamente as cores de fundo, bordas e sombras corretas para garantir que seu conteúdo se destaque sem parecer bagunçado.
4. Mais Informações
Para explorar todo o ecossistema do Hux UI, incluindo o playground interativo na web, confira os links oficiais:
- Pub.dev: https://pub.dev/packages/hux
- Website/Live Demo: https://ui.thehuxdesign.com
- GitHub: https://github.com/lofidesigner/hux
Se você quer que seu app pareça uma ferramenta premium desenhada por um estúdio de alto nível, o Hux UI é o seu kit de ferramentas.
Viagem no Tempo no Flutter ⏳
“É como tirar uma foto do estado de algo que você quer lembrar mais tarde… A grande vantagem é que essa ‘foto’ não estraga NADA nem interfere no que está acontecendo no momento.”
O Memento Pattern é um padrão de projeto comportamental que permite salvar e restaurar o estado de um objeto sem expor seus detalhes internos.
Não há forma melhor de demonstrar isso do que em um Aplicativo de Desenho (Canvas). Imagine tentar apagar matematicamente um traço de pincel específico que cruza com 5 outros traços. É um pesadelo. Em vez disso, usando o padrão Memento, nós apenas tiramos uma “foto” (snapshot) do quadro antes de começar a desenhar. Se o usuário clicar em Undo, nós substituímos o quadro inteiro pela foto antiga!
Os Três Pilares do Memento
A estrutura básica consiste em três componentes principais:
- Originator: O objeto cujo estado precisa ser preservado (Nosso Controller do Canvas).
- Memento: O objeto “foto” (snapshot) contendo o estado.
- Caretaker: O responsável por armazenar o histórico de Mementos (Usando pilhas de Undo/Redo).
Implementação Moderna em Dart
Em aplicações Flutter modernas, a melhor prática é capturar o estado completo em um Memento imutável usando List.unmodifiable().
1. O Memento Imutável
class CanvasMemento {
final List<DrawingStroke> strokes;
// ⚠️ CRÍTICO: Usamos List.unmodifiable() para criar uma cópia real.
// Se apenas atribuíssemos a lista, o Memento apontaria para o mesmo
// endereço de memória do Canvas, quebrando o padrão!
CanvasMemento(List<DrawingStroke> currentStrokes)
: strokes = List.unmodifiable(currentStrokes);
}
2. Originator e Caretaker
Combinamos os dois em um ChangeNotifier para torná-lo reativo. Usamos duas pilhas (stacks): _undoStack e _redoStack.
Antes de qualquer ação (começar um novo traço, ou limpar o quadro), tiramos uma foto:
void _saveSnapshot() {
// Salva o estado atual no histórico
_undoStack.add(CanvasMemento(_strokes));
// Qualquer nova ação invalida o histórico de refazer
_redoStack.clear();
notifyListeners();
}
3. Restaurando o Estado (Undo)
Para desfazer, empurramos o nosso estado atual para a pilha de Redo (Refazer), removemos o último estado da pilha de Undo (Desfazer), e o aplicamos.
void undo() {
if (_undoStack.isEmpty) return;
_redoStack.add(CanvasMemento(_strokes));
final memento = _undoStack.removeLast();
// Restaura a foto perfeitamente!
_strokes = List.from(memento.strokes);
notifyListeners();
}
Aplicando esse padrão, seu app de desenho pode se recuperar elegantemente de traços ruins, limpezas acidentais ou mudanças gigantescas com apenas um toque na tela!
Interações de Deslize Suaves 👈👉
O gesto de deslizar (swipe) se tornou uma parte fundamental da navegação em aplicativos modernos. Seja para arquivar um e-mail, excluir uma notificação ou concluir uma tarefa, a interação de deslizar oferece uma experiência fluida e intuitiva.
O Flutter torna a implementação disso incrivelmente fácil com um widget nativo: Dismissible. Ele gerencia todo o reconhecimento de gestos, animações e lógica de física de forma automática!
1. Conceitos Fundamentais
Para tornar qualquer widget “deslizável”, basta envolvê-lo em um Dismissible. Ele exige duas propriedades cruciais:
key: Isso é absolutamente obrigatório. O Flutter precisa de uma chave única para saber qual widget foi removido da árvore após o término da animação.onDismissed: Um callback onde você deve remover os dados da sua lista.
Dismissible(
key: Key(item.id),
onDismissed: (direction) {
// ⚠️ CRÍTICO: Remova o item da sua fonte de dados!
setState(() {
items.remove(item);
});
},
child: YourListItemWidget(),
)
2. Adicionando Fundos Coloridos 🎨
Um deslize em branco não é muito útil. Os usuários precisam de feedback visual indicando qual ação vai ocorrer. Você pode configurar o background (revelado ao deslizar da esquerda para a direita) e o secondaryBackground (revelado ao deslizar da direita para a esquerda).
Dismissible(
key: Key(item.id),
// Revelado ao deslizar: Esquerda -> Direita
background: Container(
color: Colors.green,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 20),
child: const Icon(Icons.archive, color: Colors.white),
),
// Revelado ao deslizar: Direita -> Esquerda
secondaryBackground: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
child: const Icon(Icons.delete, color: Colors.white),
),
// ...
)
3. Controlando a Direção
Por padrão, o Dismissible permite o deslize em ambas as direções horizontais. Você pode restringir esse comportamento usando a propriedade direction:
DismissDirection.endToStart(Apenas deslizar para a esquerda - útil para exclusões)DismissDirection.startToEnd(Apenas deslizar para a direita)DismissDirection.vertical(Deslizar para cima/baixo)
Dica de Ouro 💡: Confirmando Ações
Deslizes acidentais acontecem. Você pode prevenir uma exclusão imediata usando o callback confirmDismiss. Você pode exibir um AlertDialog; se retornar Future.value(true), o item é dispensado. Se retornar false (ex: o usuário tocou em “Cancelar”), o widget se anima suavemente de volta para o lugar original!
O Mundo na Ponta dos Dedos 🌍
Mapas 2D padrão são funcionais, mas quando você quer mostrar conexões globais, rotas de voos ou a distribuição de dados pelo mundo, nada supera um globo 3D interativo.
O pacote flutter_goodmap (criado especificamente para visualizações geoespaciais avançadas) permite que você renderize um globo 3D de alta performance diretamente no Flutter.
1. Configurando o GoodMapGlobe
O widget central para renderização 3D neste pacote é o GoodMapGlobe. Ele entrega um conjunto massivo de funcionalidades prontas para uso.
import 'package:goodmap/goodmap.dart';
GoodMapGlobe(
initialCenter: LatLng(-8.84, 13.23), // Centralizado em Luanda
initialZoom: 1.0,
// Mostra um grid pontilhado lindo para um visual digital
showDottedGrid: true,
dottedGridColor: Colors.white.withOpacity(0.3),
)
2. Desenhando Conexões (Arcos) ✈️
Uma das funcionalidades mais poderosas do GoodMapGlobe é a capacidade de desenhar arcos 3D entre coordenadas. Isso é perfeito para mostrar rotas, cadeias de suprimentos ou conexões de rede.
Você define objetos do tipo GlobeArc e passa para a propriedade arcs:
final arcs = [
GlobeArc(
from: LatLng(-8.84, 13.23), // Luanda
to: LatLng(40.71, -74.01), // New York
dashed: true,
drawProgress: 1.0, // Anime este valor para fazer o arco crescer!
),
];
3. Visualização de Dados (Heatmaps e Markers)
Você pode sobrepor dados na superfície do globo de forma super fácil:
- Markers (Marcadores): Use
MarkerOptionspara posicionar pontos brilhantes e pulsantes em coordenadas específicas (ex:pulse: true,pulseMaxRadius: 22). - Heatmaps (Mapas de Calor): Passe um
HeatmapOptionspara renderizar dados de densidade acompanhando a curvatura da Terra! Você só precisa fornecer os pontos (points), pesos (weights) e raio (radius).
4. Ciclo Dia/Noite e Atmosfera 🌙
Quer deixar o globo fotorrealista? O GoodMapGlobe suporta dispersão atmosférica e posicionamento solar em tempo real!
GoodMapGlobe(
// ...
atmosphere: true, // Adiciona um brilho atmosférico na borda
dateTime: DateTime.now().toUtc(), // Renderiza precisamente as sombras do dia/noite baseadas na posição real do Sol!
)
Juntando tudo isso, você obtém um globo 3D de tirar o fôlego, interativo e que exibe perfeitamente dados geoespaciais complexos!
Transições Incríveis com liquid_swipe 💧
Carrosséis estáticos e transições comuns para telas de onboarding (boas-vindas) já estão ultrapassados. Se você quer impressionar o seu usuário logo na primeira tela, o pacote liquid_swipe cria uma transição fluida, interativa e de encher os olhos.
Neste tutorial, vamos criar um fluxo de onboarding usando esse efeito líquido!
1. Instalação
Como verificado no pub.dev, adicione o pacote ao seu pubspec.yaml:
dependencies:
liquid_swipe: ^3.1.0
(Confira sempre a versão mais atualizada no pub.dev)
E depois, rode flutter pub get.
2. Usando o Widget LiquidSwipe
Implementar é extremamente simples. Em vez de usar o PageView tradicional do Flutter, usamos o LiquidSwipe e passamos a ele uma lista com nossos containers de tela cheia.
Veja o código base:
import 'package:flutter/material.dart';
import 'package:liquid_swipe/liquid_swipe.dart';
class LiquidSwipeTutorialWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: LiquidSwipe(
// enableLoop permite deslizar da última página direto para a primeira!
enableLoop: true,
// O tipo de revelação da onda (circularReveal ou liquidReveal)
waveType: WaveType.liquidReveal,
pages: [
Container(
color: Colors.white,
child: Center(child: Text('Página 1', style: TextStyle(fontSize: 30))),
),
Container(
color: Colors.blueAccent.shade700,
child: Center(child: Text('Página 2', style: TextStyle(fontSize: 30, color: Colors.white))),
),
Container(
color: Colors.black87,
child: Center(child: Text('Página 3', style: TextStyle(fontSize: 30, color: Colors.white))),
),
],
),
);
}
}
3. Propriedades Importantes 🛠️
pages: Uma lista deWidgets que representam as páginas. Para que o efeito líquido funcione bem, esses widgets devem preencher toda a tela (como umContainercom uma cor sólida).enableLoop: Um booleano que, quando definido comotrue, permite que o usuário passe da última tela direto para a primeira em um loop contínuo.waveType: Define o estilo da onda. UsamosWaveType.circularReveal, que expande a transição na forma de um círculo. Você também pode usarWaveType.liquidReveal.
Dica de UX ✨
Garanta que as suas páginas tenham cores de fundo bastante contrastantes (ex: Branco -> Azul Escuro -> Preto). O efeito “liquid swipe” fica absurdamente mais bonito quando há um grande contraste de cores entre as telas, dando mais peso visual à transição!
Micro-Interações que Encantam 💙
As micro-interações são o segredo para uma excelente Experiência do Usuário (UX). A icônica animação de “coração do Twitter” ao curtir um post fornece um feedback visual imediato e muito satisfatório.
Graças ao pacote like_button da fluttercandies, você pode implementar exatamente essa animação com poucas linhas de código, customizando ícones, cores e partículas!
1. Instalação
Como verificado no pub.dev, adicione a dependência ao seu pubspec.yaml:
dependencies:
like_button: ^2.0.5 # Verifique sempre a versão mais recente
Rode flutter pub get para instalar.
2. Uso Básico
O pacote expõe o widget LikeButton. Você pode colocar qualquer ícone dentro dele, e o pacote cuidará automaticamente dos toques e das animações.
import 'package:flutter/material.dart';
import 'package:like_button/like_button.dart';
// Dentro do seu build:
LikeButton(
size: 80,
likeBuilder: (bool isLiked) {
return Icon(
Icons.favorite,
color: isLiked ? Colors.lightBlueAccent : Colors.grey,
size: 80,
);
},
)
3. Customizando as Partículas e as Bolhas ✨
A verdadeira mágica acontece quando você customiza o anel de expansão e as bolhas que explodem para combinar com as cores da sua marca!
Você pode passar propriedades como CircleColor e BubblesColor para ajustar os valores exatos:
LikeButton(
size: 80,
// Define o gradiente do anel que se expande
circleColor: const CircleColor(
start: Color(0xff00ddff),
end: Color(0xff0099cc)
),
// Define as cores dos pontos que explodem
bubblesColor: const BubblesColor(
dotPrimaryColor: Color(0xff33b5e5),
dotSecondaryColor: Color(0xff0099cc),
),
likeBuilder: (bool isLiked) {
return Icon(
Icons.flutter_dash, // Pode ser QUALQUER ícone!
color: isLiked ? Colors.lightBlueAccent : Colors.grey,
size: 80,
);
},
)
Dica de Ouro 💡
O LikeButton possui uma função onTap assíncrona. Você pode usá-la para fazer uma chamada de API para o seu backend. Se a chamada falhar, basta retornar false na função onTap, e o botão reverterá a sua animação elegantemente, sem precisar de nenhum gerenciamento de estado complexo!
Mais Ações, Menos Poluição ➕
Um Floating Action Button (FAB) comum é ótimo para uma única ação principal (como “Escrever Email” ou “Adicionar Item”). Mas e se você tiver várias ações importantes que o usuário pode querer realizar ao mesmo tempo?
Em vez de encher a sua UI de botões, você pode usar um FAB no estilo Speed Dial. O pacote flutter_expandable_fab permite criar facilmente um botão que se expande revelando múltiplas ações em uma animação incrível ao ser tocado.
1. Instalação
Como recomendado no pub.dev, adicione o pacote ao seu pubspec.yaml:
dependencies:
flutter_expandable_fab: ^2.1.0 # Verifique a versão mais atualizada no pub.dev
Rode flutter pub get no seu terminal.
2. Configurando o ExpandableFab
Configurar é muito fácil. Dentro do seu Scaffold, você substitui o FAB padrão pelo ExpandableFab.
Passo Fundamental: Você precisa definir o floatingActionButtonLocation do Scaffold como ExpandableFab.location para que o pacote consiga medir e posicionar os botões corretamente na tela.
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
// Dentro do seu Scaffold:
Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
type: ExpandableFabType.up, // A direção em que as opções vão "pular"
children: [
FloatingActionButton(
onPressed: () {},
child: Icon(Icons.email),
),
FloatingActionButton(
onPressed: () {},
child: Icon(Icons.image),
),
],
),
body: Container(),
)
3. Customizando os Botões de Abertura e Fechamento 🛠️
O pacote gerencia o estado aberto/fechado automaticamente. No entanto, você pode customizar exatamente a aparência do botão principal nessas duas fases.
No nosso código de exemplo, usamos o DefaultFloatingActionButtonBuilder para exibir um ícone de menu (Icons.menu) quando fechado, e um ícone de fechar (Icons.close) quando estiver aberto:
ExpandableFab(
openButtonBuilder: DefaultFloatingActionButtonBuilder(
fabSize: ExpandableFabSize.regular,
child: const Icon(Icons.menu),
),
closeButtonBuilder: DefaultFloatingActionButtonBuilder(
fabSize: ExpandableFabSize.regular,
child: const Icon(Icons.close),
),
children: [ ... ],
)
Dica de UI ✨
Você não está restrito ao ExpandableFabType.up! Você pode usar ExpandableFabType.side para jogar os botões horizontalmente ou até criar um layout em formato de leque! Experimente opções diferentes dependendo de onde o botão estiver posicionado na tela.
Expresse-se com animated_emoji 🎉
Emojis de texto estáticos são legais, mas se você quer adicionar uma camada de capricho e interatividade ao seu app, os emojis animados são a escolha perfeita!
O pacote animated_emoji permite renderizar Noto Animated Emojis de forma fluida no Flutter.
1. Instalação
Como validado na documentação oficial do pub.dev, adicione o seguinte ao seu pubspec.yaml:
dependencies:
animated_emoji: ^3.0.0 # Lembre-se de checar o pub.dev para a versão mais recente
Depois, rode flutter pub get.
2. Exibindo Emojis
Usar o pacote é muito fácil. Ele se comporta como um widget comum, mas já traz uma animação suave em loop por padrão.
Veja como exibir um conjunto de diferentes emojis animados:
import 'package:flutter/material.dart';
import 'package:animated_emoji/animated_emoji.dart';
class AnimatedEmojiDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 16,
children: const [
AnimatedEmoji(AnimatedEmojis.rocket, size: 65),
AnimatedEmoji(AnimatedEmojis.partyingFace, size: 65),
AnimatedEmoji(AnimatedEmojis.joy, size: 65),
],
);
}
}
3. Customização e Tons de Pele 🎨
O pacote animated_emoji suporta bastante flexibilidade!
- Tons de Pele: Você pode alterar facilmente o tom de pele de emojis suportados usando modificadores como
.dark,.mediumou.light.AnimatedEmoji(AnimatedEmojis.wave.mediumDark, size: 50) - Controle de Animação: Não quer que a animação fique em loop infinito? Basta passar
repeat: false.AnimatedEmoji(AnimatedEmojis.clown, repeat: false) - Controllers: Você pode até passar um
AnimationControllerpara acionar a animação do emoji apenas quando o usuário tocar ou passar o mouse por cima!
Use essa dica para adicionar micro-interações em telas vazias (empty states), aplicativos de chat ou diálogos de sucesso!
Dê Vida ao Seu App 🌌
Um fundo de cor sólida é funcional, mas um fundo animado faz o seu aplicativo parecer vivo. Seja uma onda calma fluindo para um app de meditação, ou uma estética cyberpunk para um dashboard de jogos, fundos dinâmicos definem o clima perfeito.
O pacote flutter_moving_background é uma biblioteca altamente otimizada que te dá exatamente isso, mantendo 60fps+ sem drenar a bateria.
1. Instalação
Como verificado no pub.dev, basta adicionar isto ao seu pubspec.yaml:
dependencies:
flutter_moving_background: ^0.2.1 # Confira sempre a versão mais recente
Depois, rode flutter pub get.
2. O Poder dos Presets (Pré-definições)
O pacote já vem com vários estilos maravilhosos prontos para uso, com configuração zero. Basta envolver o seu conteúdo com eles!
import 'package:flutter_moving_background/flutter_moving_background.dart';
// Estilo Pôr do Sol
MovingBackground.sunset(
child: YourContentWidget(),
);
// Estilo Cyberpunk
MovingBackground.cyberpunk(
child: YourContentWidget(),
);
3. Fundos Especializados 🌧️
Além dos presets, o pacote expõe widgets especializados incrivelmente poderosos:
WaveBackground: Cria ondas fluidas sobrepostas. Você pode customizar owaveCount(quantidade de ondas),amplitudeefrequency.RainBackground: Efeito de chuva com parallax. Você controla a quantidade de gotas (numberOfDrops), velocidade da queda (fallSpeed) e pode até adicionar rastros!BubbleBackground: Bolhas flutuantes com desfoque (blur). Perfeito para designs UI modernos e suaves (glassmorphism).ConstellationBackground: Partículas interligadas formando um efeito de constelação.
Exemplo: Ondas Customizadas
WaveBackground(
waveCount: 4,
speed: 1.2,
amplitude: 30.0,
colors: const [
Colors.blue,
Colors.teal,
Colors.cyan,
Colors.lightBlueAccent
],
backgroundColor: Colors.lightBlue.shade50,
child: Scaffold(
backgroundColor: Colors.transparent, // Deixe as ondas aparecerem!
body: Center(child: Text("Fluindo...")),
),
)
Dica de Performance ⚡️
O pacote foi desenhado para ser absurdamente rápido (usa apenas um Ticker e CustomPainter). No entanto, se a sua interface de usuário for muito complexa, certifique-se de usar construtores const onde possível. Isso garante que o Flutter não reconstrua sua árvore de widgets desnecessariamente enquanto o fundo é repintado a cada frame!
Animações “Orb” com o pacote orb_animations
Criar indicadores de estado dinâmicos (como a “Siri” ou o modo de voz do ChatGPT) do zero usando CustomPainter e Shaders pode ser complexo e propenso a problemas de performance. Felizmente, existe um pacote incrivelmente leve que resolve exatamente isto: o orb_animations.
Este pacote é perfeito para loading indicators, estados visuais em direto (como “a gravar áudio”), e feedback expressivo de UI.
1. Instalação
Basta adicionar o pacote ao teu pubspec.yaml:
dependencies:
orb_animations: ^1.0.0
E correr flutter pub get.
2. A Magia do ThinkingOrb
O widget principal chama-se ThinkingOrb. Ele não tem dependências externas pesadas e usa um único Ticker internamente para garantir 60fps (ou 120fps) constantes com o mínimo consumo de CPU.
Exemplo Base:
import 'package:orb_animations/orb_animations.dart';
ThinkingOrb(
state: OrbState.working, // O tipo de animação
size: OrbSize.size64, // O tamanho predefinido (64px ou 20px)
speed: 1.0, // Multiplicador de velocidade
)
3. Os Vários Estados
A parte mais fantástica deste pacote é que já inclui vários estados de animação perfeitamente afinados:
OrbState.working(A processar informação)OrbState.searching(A pesquisar/procurar)OrbState.solving(A resolver um problema logicamente)OrbState.listening(A ouvir áudio do utilizador)OrbState.composing(A gerar texto/resposta)OrbState.shaping(A desenhar formas)
Dica de Acessibilidade ♿️
O pacote é incrivelmente detalhado: se o utilizador tiver a opção de “Reduzir Movimento” (Reduced Motion) ativa nas definições do telemóvel (iOS ou Android), o ThinkingOrb deteta isso automaticamente (através do MediaQuery.disableAnimations) e desenha apenas um frame estático sem animação. Isto respeita as definições do utilizador de forma nativa!