- Visão Geral e Filosofia
- Identidade Visual e Valores
- Estratégia de Conversão e Engajamento
- Sistema de Cores
- Tipografia
- Espaçamento e Layout
- Formas e Bordas
- Transições e Animações
- Efeitos Visuais e Profundidade
- Componentes e Padrões
- Hierarquia Visual e Navegação
- Estados e Feedback
- Microinterações e Impressões
- Acessibilidade
- Responsividade e Adaptação
- Dark Mode
- Imagens e Mídia
- Iconografia
- Guidelines Específicas por Feature
- Checklist de Implementação
Diretrizes de Design do App Flutter Arah
Versão: 1.0
Data: 2025-01-20
Status: 🎨 Diretrizes Oficiais de Design
Tipo: Design System e Guia de Estilo Completo
🎯 Visão Geral e Filosofia
O design system do Arah Flutter App deve guiar, harmonizar e acelerar o desenvolvimento, garantindo:
- ✅ Experiência fluida e natural (baixa fricção)
- ✅ Conversão eficiente (estratégias de captura de atenção aplicadas ao contexto territorial)
- ✅ Identidade visual única (preservando a estética estabelecida)
- ✅ Consistência (padrões claros em todas as telas)
- ✅ Acessibilidade (inclusivo e responsivo)
- ✅ Performance (design que não compromete performance)
O design do Arah é território-primeiro, comunidade-primeiro. Ele deve:
- Respeitar o contexto local: Cada território é único; o design deve se adaptar sem perder a identidade
- Priorizar a relevância: Informações territoriais devem se destacar naturalmente
- Facilitar conexão real: Design que aproxima pessoas do lugar onde vivem
- Reduzir fricção cognitiva: Interface limpa, hierarquia clara, ações intuitivas
- Cuidar sem manipular: Engajamento autêntico, não dependência
🎨 Identidade Visual e Valores
Valores Visuais Fundamentais
1. Território como Referência
- Elementos visuais conectados ao lugar (mapa, geolocalização, pins)
- Cores inspiradas na natureza (verde floresta, tons terrosos)
- Texturas sutis que remetem ao natural (sem saturação excessiva)
2. Baixa Excitação e Foco
- Paleta suave e harmoniosa (não gritante)
- Hierarquia clara (não sobrecarregada)
- Espaço em branco generoso
- Silêncio funcional (design que não compete com conteúdo)
3. Silêncio Funcional com Hierarquia Clara
- Informações prioritárias se destacam naturalmente
- Elementos secundários discretos
- Backgrounds neutros que não competem com conteúdo
- Transparências e blur para criar camadas visuais
4. Ação Consciente e Explícita
- CTAs claros e bem posicionados
- Estados visuais distintos (hover, active, disabled)
- Feedback imediato para todas as ações
- Confirmações para ações destrutivas
5. Evolução Lenta e Íntegra
- Mudanças incrementais e testadas
- Consistência ao longo do tempo
- Preservação de padrões estabelecidos
- Retrocompatibilidade visual
🚀 Estratégia de Conversão e Engajamento
Princípios de Captura de Atenção (Inspirados em Apps Top)
- Scroll infinito suave: Feed territorial sem fricção, transições imperceptíveis
- Gestos intuitivos: Swipe, pull-to-refresh, tap duplo para interação rápida
- Carregamento progressivo: Skeleton loaders, imagens com fade-in suave
- Navegação por gestos: Bottom navigation, swipe entre tabs
Aplicação no Arah:
- Feed territorial com scroll fluido (velocidade 60fps)
- Pull-to-refresh suave para atualizar conteúdo local
- Swipe em cards de post para ações rápidas (favoritar, compartilhar)
- Bottom navigation fixa para acesso rápido a Feed, Mapa, Eventos, Perfil
- Tipografia escalada: Tamanhos distintos para importância
- Espaçamento consistente: Grid de 8px para alinhamento perfeito
- Contraste inteligente: Cores primárias para CTAs, neutros para secundários
- Cards bem definidos: Bordas sutis, sombras suaves, espaçamento interno generoso
Aplicação no Arah:
- Posts com hierarquia clara: autor em destaque, conteúdo legível, ações discretas
- Cards de território com informações prioritárias (nome, localização, membros)
- Eventos com data/hora em destaque, localização clara
- Marketplace com preços destacados, imagens em destaque
- Animações sutis: Feedback visual para todas as ações
- Haptic feedback: Vibração discreta em ações importantes
- Estados visuais claros: Loading, success, error, disabled
- Confirmações discretas: Snackbars, toasts, badges
Aplicação no Arah:
- Like/curtida com animação de escala suave (0.9x → 1.0x → 0.95x → 1.0x)
- Badge de notificação com bounce sutil quando nova
- Pull-to-refresh com indicador de carregamento
- Confirmação de ações com snackbar discreto
- Mapa integrado: Visualização espacial imediata
- Pins visuais: Destaque para conteúdo georreferenciado
- Proximidade visual: Conteúdo próximo aparece primeiro
- Conexão com lugar: Elementos que remetem ao território (cores naturais, imagens locais)
Aplicação no Arah:
- Feed e mapa sincronizados (seleção em um reflete no outro)
- Pins coloridos por tipo (post, evento, alerta)
- Badge de "próximo a você" para conteúdo local
- Cores que remetem ao território (verde, terroso, azul céu)
🎨 Sistema de Cores
Paleta Principal (Preservando Identidade Atual)
A cor primária representa natureza, território, comunidade viva.
// Flutter Colors
class ArapongaColors {
// Verde Floresta (Primary)
static const Color forest50 = Color(0xFFF1F8F4); // Background suave
static const Color forest100 = Color(0xFFE2F1E8); // Background hover
static const Color forest200 = Color(0xFFC6E3D2); // Border suave
static const Color forest300 = Color(0xFF9FCEB4); // Disabled
static const Color forest400 = Color(0xFF6FB28C); // Secondary CTA
static const Color forest500 = Color(0xFF4F956F); // Primary CTA
static const Color forest600 = Color(0xFF377B57); // Primary hover
static const Color forest700 = Color(0xFF2B6246); // Primary pressed
static const Color forest800 = Color(0xFF214D37); // Text em light mode
static const Color forest900 = Color(0xFF173525); // Text emphasis
}
Uso:
- Primary CTA (botões principais, links importantes)
- Seleção ativa (tabs, filtros, checkboxes)
- Indicadores de sucesso (badges, confirmações)
- Destaque de elementos territoriais (pins no mapa, badges de morador)
Representa confiança, informação, conexão.
// Azul Céu (Secondary)
static const Color sky50 = Color(0xFFF0F9FF); // Background info
static const Color sky100 = Color(0xFFE0F2FE); // Background hover
static const Color sky200 = Color(0xFFBAE6FD); // Border info
static const Color sky300 = Color(0xFF7DD3FC); // Secondary info
static const Color sky400 = Color(0xFF38BDF8); // Info CTA
static const Color sky500 = Color(0xFF0EA5E9); // Info hover
static const Color sky600 = Color(0xFF0284C7); // Info pressed
static const Color sky700 = Color(0xFF0369A1); // Text info
static const Color sky800 = Color(0xFF075985); // Text emphasis
static const Color sky900 = Color(0xFF0C4A6E); // Text dark
Uso:
- Links e navegação
- Informações e alertas informativos
- Conteúdo de suporte (help, docs)
- Mapa e elementos geográficos
Representam solo, lugar, autenticidade.
// Tons Terrosos (Tertiary)
static const Color earth50 = Color(0xFFFAF7F4); // Background warm
static const Color earth100 = Color(0xFFF5EFE8); // Background hover
static const Color earth200 = Color(0xFFE8DCC9); // Border warm
static const Color earth300 = Color(0xFFD4C4A8); // Disabled warm
static const Color earth400 = Color(0xFFB8A082); // Secondary warm
static const Color earth500 = Color(0xFF9C8461); // Tertiary CTA
static const Color earth600 = Color(0xFF7A6649); // Tertiary hover
static const Color earth700 = Color(0xFF5D4C38); // Text warm
static const Color earth800 = Color(0xFF463A2C); // Text emphasis
static const Color earth900 = Color(0xFF2F2620); // Text dark
Uso:
- Elementos de marketplace
- Conteúdo de economia local
- Destaques de autenticidade
- Fundos aquecidos para storytelling
Paleta Semântica
static const Color success50 = Color(0xFFF0FDF4); // Background success
static const Color success100 = Color(0xFFDCFCE7); // Background hover
static const Color success500 = Color(0xFF22C55E); // Success CTA
static const Color success600 = Color(0xFF16A34A); // Success hover
static const Color success700 = Color(0xFF15803D); // Success text
Uso: Confirmações, ações bem-sucedidas, badges de verificação
static const Color warning50 = Color(0xFFFEFCE8); // Background warning
static const Color warning100 = Color(0xFFFEF9C3); // Background hover
static const Color warning500 = Color(0xFFEAB308); // Warning CTA
static const Color warning600 = Color(0xFFCA8A04); // Warning hover
static const Color warning700 = Color(0xFFA16207); // Warning text
Uso: Alertas não críticos, avisos, pendências
static const Color error50 = Color(0xFFFEF2F2); // Background error
static const Color error100 = Color(0xFFFEE2E2); // Background hover
static const Color error500 = Color(0xFFEF4444); // Error CTA
static const Color error600 = Color(0xFFDC2626); // Error hover
static const Color error700 = Color(0xFFB91C1C); // Error text
Uso: Erros, ações destrutivas, reportes críticos
Informação
// Usar sky500, sky600, sky700 (já definidos acima)
Uso: Informações neutras, dicas, help text
Paleta Neutra (Base para Light/Dark Mode)
static const Color gray50 = Color(0xFFFAFAFA); // Background base
static const Color gray100 = Color(0xFFF5F5F5); // Background elevated
static const Color gray200 = Color(0xFFE5E5E5); // Border subtle
static const Color gray300 = Color(0xFFD4D4D4); // Border
static const Color gray400 = Color(0xFFA3A3A3); // Text disabled
static const Color gray500 = Color(0xFF737373); // Text muted
static const Color gray600 = Color(0xFF525252); // Text secondary
static const Color gray700 = Color(0xFF404040); // Text primary
static const Color gray800 = Color(0xFF262626); // Text emphasis
static const Color gray900 = Color(0xFF171717); // Text dark
static const Color dark50 = Color(0xFF171717); // Background base
static const Color dark100 = Color(0xFF1F1F1F); // Background elevated
static const Color dark200 = Color(0xFF262626); // Border subtle
static const Color dark300 = Color(0xFF404040); // Border
static const Color dark400 = Color(0xFF525252); // Text disabled
static const Color dark500 = Color(0xFF737373); // Text muted
static const Color dark600 = Color(0xFFA3A3A3); // Text secondary
static const Color dark700 = Color(0xFFD4D4D4); // Text primary
static const Color dark800 = Color(0xFFE5E5E5); // Text emphasis
static const Color dark900 = Color(0xFFFAFAFA); // Text light
Diretrizes de Uso de Cores
Hierarquia de Cores
- Primary (Verde Floresta): Ações principais, CTAs, elementos territoriais
- Secondary (Azul Céu): Links, navegação, informações
- Tertiary (Tons Terrosos): Marketplace, economia local
- Semântica: Sucesso, aviso, erro, informação (uso contextual)
Regras de Contraste
- Text sobre Background: Mínimo 4.5:1 (WCAG AA), preferencial 7:1 (WCAG AAA)
- Text sobre CTA: Mínimo 4.5:1
- Ícones sobre Background: Mínimo 3:1
- Bordas sobre Background: Mínimo 3:1
Cores Proibidas (Evitar)
- ❌ Cores vibrantes sem contexto (rosa, roxo, amarelo saturado)
- ❌ Gradientes excessivos (usar apenas em backgrounds sutis)
- ❌ Cores que não respeitam contraste mínimo
- ❌ Paletas que não seguem a identidade (cores de redes sociais concorrentes)
✍️ Tipografia
Família de Fontes
class ArapongaTypography {
static const String primaryFont = 'Inter';
static const String systemFallback = '-apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif';
}
Características:
- Sem serifa (moderna, limpa, legível)
- Altura de linha generosa (1.5-1.7 para corpo)
- Largura variável (suporte a weight 400-700)
- Oldstyle numbers (para dados numéricos)
- Kerning e ligatures otimizados
Fonte Monospace (Código, Dados)
static const String monoFont = 'SF Mono, Menlo, Monaco, "Courier New", monospace';
Uso: Códigos, IDs, timestamps, dados técnicos
class ArapongaTextStyles {
// Display (Hero, Títulos Grandes)
static const TextStyle displayLarge = TextStyle(
fontSize: 72,
fontWeight: FontWeight.bold,
letterSpacing: -2,
height: 1.1,
);
static const TextStyle displayMedium = TextStyle(
fontSize: 56,
fontWeight: FontWeight.bold,
letterSpacing: -1.5,
height: 1.2,
);
static const TextStyle displaySmall = TextStyle(
fontSize: 44,
fontWeight: FontWeight.bold,
letterSpacing: -1,
height: 1.2,
);
// Headings (Seções, Cards)
static const TextStyle heading1 = TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
letterSpacing: -0.5,
height: 1.3,
);
static const TextStyle heading2 = TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
letterSpacing: -0.3,
height: 1.4,
);
static const TextStyle heading3 = TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
height: 1.4,
);
static const TextStyle heading4 = TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
letterSpacing: -0.1,
height: 1.4,
);
// Body (Conteúdo Principal)
static const TextStyle bodyLarge = TextStyle(
fontSize: 18,
fontWeight: FontWeight.normal,
letterSpacing: 0,
height: 1.6,
);
static const TextStyle bodyMedium = TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
letterSpacing: 0,
height: 1.6,
);
static const TextStyle bodySmall = TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
letterSpacing: 0,
height: 1.5,
);
// Labels (Formulários, Captions)
static const TextStyle labelLarge = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
height: 1.4,
);
static const TextStyle labelMedium = TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
height: 1.4,
);
static const TextStyle labelSmall = TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
height: 1.3,
);
// Captions (Metadados, Timestamps)
static const TextStyle caption = TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
letterSpacing: 0.2,
height: 1.4,
);
static const TextStyle overline = TextStyle(
fontSize: 10,
fontWeight: FontWeight.w700,
letterSpacing: 0.5,
height: 1.2,
textTransform: TextTransform.uppercase,
);
}
Diretrizes de Uso
Hierarquia Tipográfica
- Display: Apenas para hero sections, onboarding, tela inicial
- Headings: Seções, cards principais, títulos de telas
- Body: Conteúdo principal (posts, descrições, textos)
- Labels: Formulários, botões, campos de input
- Captions: Timestamps, metadados, informações secundárias
- Tamanho mínimo: 14px para body, 12px para captions
- Line height: 1.5-1.7 para body, 1.3-1.4 para headings
- Letter spacing: Negativo para headings grandes (-0.5 a -2), positivo para labels (0.1-0.3)
- Weight: Regular (400) para body, Semibold (600) para headings, Bold (700) para emphasis
📐 Espaçamento e Layout
class ArapongaSpacing {
static const double xs = 4; // 0.5 × 8px
static const double sm = 8; // 1 × 8px
static const double md = 16; // 2 × 8px
static const double lg = 24; // 3 × 8px
static const double xl = 32; // 4 × 8px
static const double xxl = 48; // 6 × 8px
static const double xxxl = 64; // 8 × 8px
}
Uso:
xs: Espaçamento interno de elementos pequenos (ícones, badges)sm: Espaçamento entre elementos relacionados (label + input)md: Espaçamento padrão entre elementos (cards, seções)lg: Espaçamento entre seções principaisxl: Espaçamento entre blocos grandes (hero, footer)xxl: Espaçamento em telas grandes, seções principais
class ArapongaPadding {
// Padding interno de cards
static const EdgeInsets cardPadding = EdgeInsets.all(16);
static const EdgeInsets cardPaddingLarge = EdgeInsets.all(24);
// Padding de telas
static const EdgeInsets screenPadding = EdgeInsets.symmetric(horizontal: 16);
static const EdgeInsets screenPaddingLarge = EdgeInsets.symmetric(horizontal: 24);
// Padding de inputs
static const EdgeInsets inputPadding = EdgeInsets.symmetric(horizontal: 16, vertical: 12);
// Padding de botões
static const EdgeInsets buttonPadding = EdgeInsets.symmetric(horizontal: 24, vertical: 12);
static const EdgeInsets buttonPaddingSmall = EdgeInsets.symmetric(horizontal: 16, vertical: 8);
}
class ArapongaBreakpoints {
static const double mobile = 360; // Mobile pequeno
static const double mobileLarge = 480; // Mobile grande
static const double tablet = 768; // Tablet
static const double tabletLarge = 1024; // Tablet grande
static const double desktop = 1280; // Desktop
}
Layout Strategy:
- Mobile (< 768px): Single column, bottom navigation
- Tablet (768px - 1024px): Two columns quando possível, drawer lateral
- Desktop (> 1024px): Multi-column, sidebar permanente
🔲 Formas e Bordas
class ArapongaRadius {
static const double none = 0;
static const double xs = 4; // Inputs, badges pequenos
static const double sm = 8; // Botões, cards pequenos
static const double md = 12; // Cards padrão
static const double lg = 16; // Cards grandes, modais
static const double xl = 24; // Hero sections, splash screens
static const double full = 9999; // Círculos, pills
}
Diretrizes:
- xs (4px): Inputs, campos de formulário
- sm (8px): Botões, chips, badges
- md (12px): Cards padrão, avatares
- lg (16px): Modais, drawers, cards grandes
- xl (24px): Telas de onboarding, hero sections
- full: Avatares circulares, pills de filtro
class ArapongaBorders {
static const double none = 0;
static const double thin = 0.5; // Dividers sutis
static const double base = 1; // Bordas padrão
static const double thick = 2; // Destaque, foco
}
Uso:
- thin (0.5px): Dividers entre seções, separadores de lista
- base (1px): Bordas de cards, inputs, botões
- thick (2px): Estado de foco, seleção ativa
🎬 Transições e Animações
Princípios de Animação
1. Natural e Orgânica
- Animações baseadas em física (easing curves naturais)
- Durações que respeitam percepção humana (não muito rápidas, não muito lentas)
- Movimento que faz sentido (elementos aparecem de onde faz sentido)
2. Funcional, Não Decorativa
- Cada animação serve a um propósito (feedback, orientação, hierarquia)
- Não distrai do conteúdo
- Melhora compreensão, não apenas entretenimento
3. Respeitosa
- Durações curtas (200-400ms para microinterações, 300-600ms para transições)
- Respeita preferências de acessibilidade (reduz motion quando solicitado)
- Não causa náusea ou desconforto
class ArapongaDurations {
static const Duration instant = Duration(milliseconds: 0);
static const Duration fast = Duration(milliseconds: 150); // Microinterações
static const Duration normal = Duration(milliseconds: 300); // Transições padrão
static const Duration slow = Duration(milliseconds: 500); // Transições complexas
static const Duration slower = Duration(milliseconds: 800); // Animações de entrada
}
class ArapongaCurves {
// Easing padrão (suave e natural)
static const Curve standard = Curves.easeInOutCubic; // cubic-bezier(0.4, 0, 0.2, 1)
// Entrada (elemento aparece)
static const Curve enter = Curves.easeOutCubic; // cubic-bezier(0, 0, 0.2, 1)
// Saída (elemento desaparece)
static const Curve exit = Curves.easeInCubic; // cubic-bezier(0.4, 0, 1, 1)
// Bounce suave (para feedback positivo)
static const Curve bounce = Curves.easeOutBack; // cubic-bezier(0.34, 1.56, 0.64, 1)
// Spring (para elementos que precisam de elasticidade)
static const Curve spring = Curves.easeOutElastic;
}
Animações Padrão
Fade In/Out
// Aparecer/desaparecer suave
AnimatedOpacity(
opacity: isVisible ? 1.0 : 0.0,
duration: ArapongaDurations.normal,
curve: ArapongaCurves.standard,
)
Uso: Mensagens, toasts, overlays, modais
Scale (Feedback de Toque)
// Escala suave ao tocar
AnimatedScale(
scale: isPressed ? 0.95 : 1.0,
duration: ArapongaDurations.fast,
curve: ArapongaCurves.standard,
)
Uso: Botões, cards clicáveis, ícones interativos
// Deslizar para cima/baixo
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.1),
end: Offset.zero,
).animate(CurvedAnimation(
parent: controller,
curve: ArapongaCurves.enter,
)),
child: child,
)
Uso: Navegação entre telas, modais, bottom sheets
Rotate (Loading, Refresh)
// Rotação contínua para loading
RotationTransition(
turns: Tween(begin: 0.0, end: 1.0).animate(controller),
child: icon,
)
Uso: Indicadores de carregamento, pull-to-refresh
Microinterações Específicas
Like/Curitiva (Instagram-style)
// Animação de like com bounce
AnimatedSequence([
ScaleTransition(scale: 0.9), // Comprimir
ScaleTransition(scale: 1.1), // Expandir
ScaleTransition(scale: 1.0), // Normalizar
])
Duração: 300ms
Curve: Bounce suave
Pull-to-Refresh
// Rotação + fade do indicador
AnimatedRotation(
rotation: pullDistance / 100,
child: AnimatedOpacity(
opacity: pullDistance > 50 ? 1.0 : pullDistance / 50,
child: refreshIcon,
),
)
Duração: Instantânea (seguindo gesto)
Badge de Notificação
// Bounce ao aparecer nova notificação
AnimatedSequence([
ScaleTransition(scale: 0.0),
ScaleTransition(scale: 1.2), // Overshoot
ScaleTransition(scale: 1.0), // Normalizar
])
Duração: 400ms
Curve: Bounce
💎 Efeitos Visuais e Profundidade
class ArapongaShadows {
// Sem sombra (nível 0)
static const List<BoxShadow> none = [];
// Sombra sutil (nível 1)
static const List<BoxShadow> xs = [
BoxShadow(
color: Color(0x0A000000), // 4% opacidade
blurRadius: 4,
offset: Offset(0, 2),
),
];
// Sombra leve (nível 2)
static const List<BoxShadow> sm = [
BoxShadow(
color: Color(0x14000000), // 8% opacidade
blurRadius: 8,
offset: Offset(0, 4),
),
];
// Sombra padrão (nível 3)
static const List<BoxShadow> md = [
BoxShadow(
color: Color(0x1A000000), // 10% opacidade
blurRadius: 16,
offset: Offset(0, 8),
),
];
// Sombra elevada (nível 4)
static const List<BoxShadow> lg = [
BoxShadow(
color: Color(0x24000000), // 14% opacidade
blurRadius: 24,
offset: Offset(0, 12),
),
];
// Sombra destacada (nível 5)
static const List<BoxShadow> xl = [
BoxShadow(
color: Color(0x33000000), // 20% opacidade
blurRadius: 32,
offset: Offset(0, 16),
),
];
}
Uso:
- xs: Dividers, separadores sutis
- sm: Cards hover, inputs focados
- md: Cards padrão, botões elevados
- lg: Modais, bottom sheets
- xl: Dialogs, overlays completos
class ArapongaGlass {
// Glass card padrão
static BoxDecoration glassCard({
Color? backgroundColor,
double? blur,
}) {
return BoxDecoration(
color: backgroundColor ?? Colors.white.withOpacity(0.88),
borderRadius: BorderRadius.circular(ArapongaRadius.lg),
border: Border.all(
color: Colors.white.withOpacity(0.7),
width: ArapongaBorders.base,
),
boxShadow: ArapongaShadows.md,
);
}
}
Aplicar blur:
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16),
child: Container(
decoration: ArapongaGlass.glassCard(),
),
)
Uso: Cards principais, modais, overlays
class ArapongaGradients {
// Background suave (inspirado em devportal)
static const LinearGradient backgroundSubtle = LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFF1F8F4), // forest50
Color(0xFFF0F9FF), // sky50
],
);
// Overlay escuro para modais
static const LinearGradient overlayDark = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0x80000000), // 50% preto
Color(0xCC000000), // 80% preto
],
);
}
Uso: Apenas backgrounds sutis, nunca em elementos interativos
🧩 Componentes e Padrões
Botões
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: ArapongaColors.forest500,
foregroundColor: Colors.white,
padding: ArapongaPadding.buttonPadding,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(ArapongaRadius.sm),
),
elevation: 0,
shadowColor: Colors.transparent,
),
onPressed: () {},
child: Text('Ação Principal', style: ArapongaTextStyles.labelLarge),
)
Estados:
- Normal:
forest500, sem elevação - Hover:
forest600, sombrasm - Pressed:
forest700, escala 0.98 - Disabled:
forest300, opacity 0.5
OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: ArapongaColors.forest600,
side: BorderSide(color: ArapongaColors.forest500, width: 1),
padding: ArapongaPadding.buttonPadding,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(ArapongaRadius.sm),
),
),
onPressed: () {},
child: Text('Ação Secundária', style: ArapongaTextStyles.labelLarge),
)
Cards
Container(
decoration: BoxDecoration(
color: Colors.white, // Light mode
borderRadius: BorderRadius.circular(ArapongaRadius.md),
border: Border.all(
color: ArapongaColors.gray200,
width: ArapongaBorders.base,
),
boxShadow: ArapongaShadows.sm,
),
padding: ArapongaPadding.cardPadding,
child: content,
)
Estados:
- Normal: Sombra
sm, borda sutil - Hover: Sombra
md, elevação +2px, transição suave - Pressed: Sombra
xs, elevação -1px
Glass Card (Preservando Identidade)
Container(
decoration: ArapongaGlass.glassCard(),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16),
child: Padding(
padding: ArapongaPadding.cardPaddingLarge,
child: content,
),
),
)
Inputs
TextField(
style: ArapongaTextStyles.bodyMedium,
decoration: InputDecoration(
labelText: 'Label',
hintText: 'Placeholder',
labelStyle: ArapongaTextStyles.labelMedium.copyWith(
color: ArapongaColors.gray600,
),
hintStyle: ArapongaTextStyles.bodyMedium.copyWith(
color: ArapongaColors.gray400,
),
filled: true,
fillColor: ArapongaColors.gray50,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(ArapongaRadius.xs),
borderSide: BorderSide(
color: ArapongaColors.gray300,
width: ArapongaBorders.base,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(ArapongaRadius.xs),
borderSide: BorderSide(
color: ArapongaColors.gray300,
width: ArapongaBorders.base,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(ArapongaRadius.xs),
borderSide: BorderSide(
color: ArapongaColors.forest500,
width: ArapongaBorders.thick,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(ArapongaRadius.xs),
borderSide: BorderSide(
color: ArapongaColors.error500,
width: ArapongaBorders.base,
),
),
contentPadding: ArapongaPadding.inputPadding,
),
)
Badges
Container(
padding: EdgeInsets.symmetric(
horizontal: ArapongaSpacing.sm,
vertical: ArapongaSpacing.xs,
),
decoration: BoxDecoration(
color: ArapongaColors.forest100,
borderRadius: BorderRadius.circular(ArapongaRadius.full),
),
child: Text(
'Badge',
style: ArapongaTextStyles.labelSmall.copyWith(
color: ArapongaColors.forest700,
),
),
)
Variações:
- Success:
success100background,success700text - Warning:
warning100background,warning700text - Error:
error100background,error700text - Info:
sky100background,sky700text
Princípios de Hierarquia
- Conteúdo Primeiro: Conteúdo territorial deve ser o foco
- Navegação Discreta: Navegação existe para apoiar, não competir
- Ações Claras: CTAs devem ser óbvios, mas não gritantes
- Informação Progressiva: Mostrar o essencial, expandir quando necessário
BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
selectedItemColor: ArapongaColors.forest500,
unselectedItemColor: ArapongaColors.gray400,
selectedLabelStyle: ArapongaTextStyles.labelSmall,
unselectedLabelStyle: ArapongaTextStyles.labelSmall,
elevation: 8,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home),
label: 'Feed',
),
BottomNavigationBarItem(
icon: Icon(Icons.map_outlined),
activeIcon: Icon(Icons.map),
label: 'Mapa',
),
BottomNavigationBarItem(
icon: Icon(Icons.event_outlined),
activeIcon: Icon(Icons.event),
label: 'Eventos',
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications_outlined),
activeIcon: Icon(Icons.notifications),
label: 'Alertas',
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
activeIcon: Icon(Icons.person),
label: 'Perfil',
),
],
)
Características:
- Fixa na parte inferior
- 5 itens principais (Feed, Mapa, Eventos, Alertas, Perfil)
- Ícones outline quando inativo, filled quando ativo
- Badge de notificação sobre ícone de Alertas
AppBar(
backgroundColor: Colors.white,
elevation: 0,
shadowColor: Colors.transparent,
title: Text(
'Título da Tela',
style: ArapongaTextStyles.heading3,
),
actions: [
IconButton(
icon: Icon(Icons.search),
color: ArapongaColors.gray700,
onPressed: () {},
),
// Outros actions
],
)
Características:
- Background branco (light mode) ou dark (dark mode)
- Sem elevação (flat)
- Título em
heading3 - Ações com ícones discretos
Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: ArapongaColors.forest500,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
radius: 30,
backgroundColor: Colors.white,
),
SizedBox(height: ArapongaSpacing.md),
Text(
'Nome do Usuário',
style: ArapongaTextStyles.heading4.copyWith(
color: Colors.white,
),
),
Text(
'@username',
style: ArapongaTextStyles.bodySmall.copyWith(
color: Colors.white.withOpacity(0.8),
),
),
],
),
),
ListTile(
leading: Icon(Icons.territory),
title: Text('Meus Territórios'),
onTap: () {},
),
// Outros itens
],
),
)
💬 Estados e Feedback
Loading States
Shimmer(
gradient: LinearGradient(
colors: [
ArapongaColors.gray200,
ArapongaColors.gray100,
ArapongaColors.gray200,
],
stops: [0.0, 0.5, 1.0],
),
child: Container(
height: 100,
decoration: BoxDecoration(
color: ArapongaColors.gray200,
borderRadius: BorderRadius.circular(ArapongaRadius.md),
),
),
)
Uso: Feed, cards, listas durante carregamento
Circular Progress
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(ArapongaColors.forest500),
strokeWidth: 3,
)
Uso: Botões com loading, ações que requerem espera
Linear Progress
LinearProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(ArapongaColors.forest500),
backgroundColor: ArapongaColors.gray200,
minHeight: 4,
)
Uso: Upload de arquivos, progresso de formulário
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.inbox_outlined,
size: 64,
color: ArapongaColors.gray400,
),
SizedBox(height: ArapongaSpacing.lg),
Text(
'Nenhum conteúdo ainda',
style: ArapongaTextStyles.heading3.copyWith(
color: ArapongaColors.gray700,
),
),
SizedBox(height: ArapongaSpacing.sm),
Text(
'Quando houver conteúdo do território, ele aparecerá aqui.',
style: ArapongaTextStyles.bodyMedium.copyWith(
color: ArapongaColors.gray500,
),
textAlign: TextAlign.center,
),
SizedBox(height: ArapongaSpacing.xl),
ElevatedButton(
onPressed: () {},
child: Text('Criar Primeiro Post'),
),
],
)
Características:
- Ícone grande e discreto
- Título claro
- Descrição útil
- CTA quando aplicável
Error States
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erro ao carregar conteúdo'),
backgroundColor: ArapongaColors.error500,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(ArapongaRadius.sm),
),
action: SnackBarAction(
label: 'Tentar Novamente',
textColor: Colors.white,
onPressed: () {},
),
),
)
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Erro'),
content: Text('Não foi possível completar a ação. Tente novamente.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Fechar'),
),
ElevatedButton(
onPressed: () {},
child: Text('Tentar Novamente'),
),
],
),
)
Success States
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(Icons.check_circle, color: Colors.white),
SizedBox(width: ArapongaSpacing.sm),
Text('Ação realizada com sucesso'),
],
),
backgroundColor: ArapongaColors.success500,
behavior: SnackBarBehavior.floating,
duration: Duration(seconds: 2),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(ArapongaRadius.sm),
),
),
)
✨ Microinterações e Impressões
// Sucesso (light impact)
HapticFeedback.lightImpact();
// Erro (medium impact)
HapticFeedback.mediumImpact();
// Confirmação importante (heavy impact)
HapticFeedback.heavyImpact();
Uso:
- Light: Likes, favoritos, toggle switches
- Medium: Ações importantes (criar post, confirmar)
- Heavy: Erros críticos, confirmações destrutivas
Gestos Nativos
Pull-to-Refresh
RefreshIndicator(
onRefresh: () async {
// Atualizar conteúdo
},
color: ArapongaColors.forest500,
child: ListView(
children: [...],
),
)
Slidable(
startActionPane: ActionPane(
motion: DrawerMotion(),
children: [
SlidableAction(
onPressed: (_) {},
backgroundColor: ArapongaColors.error500,
icon: Icons.delete,
label: 'Deletar',
),
],
),
child: Card(...),
)
Uso: Cards de post, itens de lista, ações rápidas
Transições de Tela
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => NextScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
transitionDuration: ArapongaDurations.normal,
)
Uso: Navegação entre telas relacionadas
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => NextScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0); // Da direita
const end = Offset.zero;
final tween = Tween(begin: begin, end: end);
final offsetAnimation = animation.drive(tween);
return SlideTransition(
position: offsetAnimation,
child: child,
);
},
transitionDuration: ArapongaDurations.normal,
)
Uso: Navegação hierárquica (detalhes, configurações)
♿ Acessibilidade
Contraste
- Text sobre Background: Mínimo 4.5:1 (WCAG AA), preferencial 7:1 (WCAG AAA)
- Large Text (18px+): Mínimo 3:1 (WCAG AA)
- UI Components (botões, inputs): Mínimo 3:1 (WCAG AA)
Tamanhos de Toque
- Área mínima de toque: 44×44px (iOS) / 48×48dp (Material)
- Espaçamento entre elementos clicáveis: Mínimo 8px
Semântica
Semantics(
label: 'Botão de criar post',
hint: 'Duplo toque para criar novo post',
button: true,
child: FloatingActionButton(...),
)
Redução de Movimento
// Respeitar preferência de acessibilidade
MediaQuery.of(context).disableAnimations
Aplicação: Desabilitar animações quando usuário prefere reduzir movimento
Tamanhos de Fonte Escaláveis
// Usar escalas relativas
Text(
'Conteúdo',
style: ArapongaTextStyles.bodyMedium.copyWith(
fontSize: MediaQuery.of(context).textScaleFactor * 16,
),
)
📱 Responsividade e Adaptação
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < ArapongaBreakpoints.tablet) {
// Mobile layout
return MobileLayout();
} else if (constraints.maxWidth < ArapongaBreakpoints.desktop) {
// Tablet layout
return TabletLayout();
} else {
// Desktop layout
return DesktopLayout();
}
},
)
GridView.builder(
gridDelegate: SliverGridDelegateWithAdaptiveCrossAxisCount(
crossAxisCount: MediaQuery.of(context).size.width < 600 ? 1 : 2,
mainAxisSpacing: ArapongaSpacing.md,
crossAxisSpacing: ArapongaSpacing.md,
childAspectRatio: 1.2,
),
itemBuilder: (context, index) => Card(...),
)
Padding Adaptativo
Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width < 600
? ArapongaSpacing.md
: ArapongaSpacing.xl,
),
child: content,
)
🌙 Dark Mode
class ArapongaDarkColors {
// Backgrounds
static const Color background = Color(0xFF0F1419); // dark100
static const Color surface = Color(0xFF141A21); // dark200
static const Color elevated = Color(0xFF1A2129); // dark300
// Text
static const Color textPrimary = Color(0xFFE8EDF2); // dark700
static const Color textSecondary = Color(0xFFB8C5D2); // dark600
static const Color textMuted = Color(0xFF8A97A4); // dark500
// Borders
static const Color border = Color(0xFF25303A); // dark400
static const Color borderSubtle = Color(0xFF1E2830); // dark300
// Accents (mantém cores primárias, ajusta luminosidade)
static const Color accent = Color(0xFF4DD4A8); // forest500 (mais luminoso)
static const Color accentHover = Color(0xFF5EE5B9); // forest400
}
ThemeData(
brightness: Brightness.dark,
primaryColor: ArapongaDarkColors.accent,
scaffoldBackgroundColor: ArapongaDarkColors.background,
cardColor: ArapongaDarkColors.surface,
textTheme: TextTheme(
bodyLarge: ArapongaTextStyles.bodyLarge.copyWith(
color: ArapongaDarkColors.textPrimary,
),
),
)
Transição Suave Dark/Light
AnimatedTheme(
data: isDarkMode ? darkTheme : lightTheme,
duration: ArapongaDurations.normal,
child: MaterialApp(...),
)
🖼️ Imagens e Mídia
CircleAvatar(
radius: 24,
backgroundColor: ArapongaColors.gray200,
backgroundImage: imageUrl != null ? NetworkImage(imageUrl) : null,
child: imageUrl == null
? Text(
initials,
style: ArapongaTextStyles.labelLarge,
)
: null,
)
Tamanhos:
- Small: 24px (comentários, listas)
- Medium: 40px (posts, cards)
- Large: 64px (perfil, detalhes)
CachedNetworkImage(
imageUrl: imageUrl,
fit: BoxFit.cover,
placeholder: (context, url) => Shimmer(...),
errorWidget: (context, url, error) => Container(
color: ArapongaColors.gray200,
child: Icon(Icons.broken_image, color: ArapongaColors.gray400),
),
)
Aspect Ratios:
- Post: 16:9 ou 4:3 (landscape)
- Story/Highlight: 9:16 (portrait)
- Perfil: 1:1 (square)
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 2,
crossAxisSpacing: 2,
),
itemBuilder: (context, index) => GestureDetector(
onTap: () => showImageViewer(images, index),
child: CachedNetworkImage(...),
),
)
🎯 Iconografia
class ArapongaIconSizes {
static const double xs = 16;
static const double sm = 20;
static const double md = 24;
static const double lg = 32;
static const double xl = 48;
}
Uso:
- xs: Badges, chips pequenos
- sm: Listas, cards compactos
- md: Botões, navegação (padrão)
- lg: Headers, destaques
- xl: Empty states, ilustrações
Estilo de Ícones
- Material Icons: Principal (consistente com Material Design)
- Outline quando inativo:
Icons.home_outlined - Filled quando ativo:
Icons.home - Cores semânticas: Usar cores da paleta, não apenas preto/cinza
Ícones Customizados (Quando Necessário)
- Manter estilo consistente (stroke width, corner radius)
- Tamanho mínimo: 24px para detalhes visíveis
- Exportar em múltiplas densidades (@1x, @2x, @3x)
📋 Guidelines Específicas por Feature
Hierarquia Visual:
- Post Card (elevação
md, paddinglg)- Header: Avatar + Nome + Timestamp (linha única)
- Conteúdo: Texto (body medium), imagens (16:9)
- Ações: Like, Comentário, Compartilhar (ícones discretos)
- Geolocalização: Badge "📍 Próximo" quando aplicável
Estados:
- Loading: Skeleton com 3 cards
- Empty: Ilustração + "Seja o primeiro a postar"
- Error: Retry button + mensagem
Interações:
- Tap no card → Abre detalhes
- Swipe left → Favoritar
- Swipe right → Compartilhar
- Long press → Menu contextual
Hierarquia Visual:
- Mapa (full screen, Google Maps)
- Pins Coloridos:
- Post: Verde (
forest500) - Evento: Azul (
sky500) - Alerta: Laranja (
warning500) - Asset: Terroso (
earth500)
- Post: Verde (
- Bottom Sheet (seleção de pin):
- Preview do conteúdo
- Ação "Ver Detalhes"
Interações:
- Tap no pin → Abre bottom sheet
- Drag do mapa → Atualiza pins visíveis
- Zoom → Agrupa pins próximos (clustering)
Hierarquia Visual:
- Card de Evento:
- Imagem de capa (16:9)
- Badge de data/hora (destaque)
- Título (heading3)
- Localização (ícone + endereço)
- Botão "Participar" (primary)
Estados:
- Próximo: Badge verde "Em breve"
- Acontecendo: Badge azul "Ao vivo"
- Passado: Opacidade reduzida, badge "Finalizado"
Marketplace
Hierarquia Visual:
- Card de Item:
- Imagem do produto (1:1)
- Título (heading4)
- Preço (heading3,
forest600) - Loja (caption)
- Botão "Contatar" (secondary)
Filtros:
- Chips horizontais scrolláveis
- Active:
forest500background, white text - Inactive:
gray200background,gray700text
✅ Checklist de Implementação
Por Componente
✅ Botão
- Estilos definidos (primary, secondary, text)
- Estados implementados (hover, pressed, disabled)
- Haptic feedback configurado
- Animações de toque (scale)
- Contraste verificado (WCAG AA)
- Tamanho mínimo de toque (44×44px)
✅ Card
- Elevação correta (shadow system)
- Padding consistente
- Border radius aplicado
- Estados de hover/press
- Transições suaves
✅ Input
- Estados definidos (normal, focused, error, disabled)
- Label e placeholder estilizados
- Feedback de validação
- Contraste de texto verificado
Por Tela
✅ Feed
- Skeleton loader
- Pull-to-refresh
- Paginação infinita
- Empty state
- Error state com retry
- Transições de navegação
✅ Mapa
- Integração com Google Maps
- Pins coloridos por tipo
- Clustering de pins
- Bottom sheet de detalhes
- Geolocalização do usuário
✅ Perfil
- Avatar editável
- Bio editável
- Lista de posts
- Estatísticas
- Configurações acessíveis
🎓 Referências e Inspiração
Apps Top de Captura de Atenção (Adaptadas)
- Instagram: Scroll infinito, gestos intuitivos, microinterações
- TikTok: Fluidez, feedback imediato, transições suaves
- Twitter/X: Hierarquia clara, tipografia escalada, espaçamento generoso
Princípios Aplicados no Arah
- Baixa fricção: Minimizar passos para ações comuns
- Fluidez: Animações 60fps, transições suaves
- Contexto territorial: Conteúdo local em destaque
- Autenticidade: Design que não manipula, apenas facilita
📝 Notas Finais
Preservação da Identidade
- ✅ Paleta verde floresta mantida
- ✅ Glass morphism preservado
- ✅ Watermark sutil do logo
- ✅ Tons terrosos para economia local
- ✅ Azul céu para informações
- Este documento deve evoluir com feedback dos usuários
- Mudanças incrementais, não revoluções
- Testes A/B para grandes mudanças
- Validação com comunidade antes de implementar
Status: 🎨 Diretrizes Oficiais de Design
Versão: 1.0
Última Atualização: 2025-01-20
Documentação relacionada: