Vídeo móvel e arrastar e largar em React Native: guia UX
Há uma diferença entre um aplicativo que funciona e um aplicativo que dá vontade de usar. Essa diferença está nos detalhes: a fluidez de uma animação, a reatividade de um gesto, o tempo de carregamento de uma tela. Em outubro de 2025, passei duas semanas trabalhando em três recursos que não tinham nada em comum, exceto um objetivo: tornar o TAMSIV moderno e agradável de usar.
Captura de vídeo estilo TikTok. Arrastar e soltar para reorganizar as abas. Carrossel infinito no feed. Três projetos distintos, uma única obsessão: a sensação de fluidez.
Pontos chave a reter:
- A compressão de vídeo é essencial no celular — os arquivos brutos pesam dezenas de MB
- O arrastar e soltar das abas usa obrigatoriamente react-native-gesture-handler, não componentes nativos
- Um carrossel infinito bem feito requer o pré-carregamento de elementos adjacentes
- Salvar a ordem das abas no banco de dados permite a persistência entre dispositivos
- As microinterações são o que diferencia um aplicativo "correto" de um aplicativo "notável"
Por que integrar a captura de vídeo em um aplicativo de produtividade?
A pergunta parece estranha à primeira vista. Um aplicativo de gerenciamento de tarefas não precisa de vídeo, certo? Na verdade, sim. Quando tu crias um memorando de voz com TAMSIV, às vezes queres anexar um anexo visual — uma foto de um documento, um vídeo de um protótipo, uma gravação de tela.
Eu já havia implementado os anexos (fotos e documentos). Mas faltava o vídeo. E quando comecei a prototipar a interface, percebi que uma captura de vídeo clássica — o componente de câmera bruto do Android — era horrível. Sem estilo, sem fluidez, uma experiência digna de 2015.
A inspiração veio de onde tu esperas: TikTok. Não para os vídeos curtos, mas para a interface de captura. Botão central, pré-visualização em tela cheia, controles mínimos. Tornou-se o padrão UX que os usuários esperam em 2026.
Como gerenciar a compressão de vídeo no celular?
Este é o maior desafio técnico da captura de vídeo. Os vídeos brutos de um smartphone moderno pesam dezenas de megabytes por alguns segundos. Um iPhone 15 grava em HEVC 4K a 60fps — isso pode chegar a 400 MB por minuto. Mesmo em 1080p, um vídeo de 30 segundos pesa facilmente 50 MB.
Para um aplicativo de produtividade, isso é inaceitável. Os anexos são armazenados no Supabase Storage, e cada MB de egresso custa dinheiro. Aliás, detalhei os problemas de egresso no artigo sobre a redução de custos do Supabase.
A solução: configurar a captura para limitar a resolução e o bitrate desde o início. Em vez de capturar em 4K e depois comprimir (o que consome CPU e bateria), configuro o componente da câmera para capturar diretamente em um formato leve:
- Resolução: 720p máximo (suficiente para anexos)
- Bitrate: limitado a 2 Mbps
- Formato: H.264 para compatibilidade universal
- Duração máxima: 60 segundos (limite voluntário para evitar arquivos enormes)
Com esses parâmetros, um vídeo de 30 segundos pesa aproximadamente 3 a 5 MB em vez de 50 MB. Um ganho de fator 10 que torna o armazenamento e a transferência viáveis.
Qual é a armadilha da tela cheia no Android?
A pré-visualização em tela cheia da câmera parece trivial. Não é. No Android, a proporção da tela (geralmente 19.5:9 ou 20:9) não corresponde à proporção da câmera (16:9 ou 4:3). Resultado: ou tu exibes barras pretas, ou tu cortas a imagem, ou tu esticas.
Optei pelo corte com um leve zoom — o que o TikTok faz. A imagem preenche toda a tela, mas as bordas são ligeiramente cortadas. O usuário não percebe. A sensação de imersão é preservada.
A segunda armadilha: o gerenciamento da orientação. O TAMSIV força o modo retrato para a captura de vídeo (coerente com o uso móvel), mas é preciso interceptar as mudanças de orientação do dispositivo para evitar que o vídeo seja gravado de cabeça para baixo. Um bug clássico que levei meio dia para rastrear.
Como implementar o arrastar e soltar das abas?
O TAMSIV tem 6 abas principais: Ditafone, Feed, Agenda, Grupos, Social, Perfil. A ordem padrão serve para a maioria dos usuários, mas alguns preferem outra organização. Implementei a reorganização por arrastar e soltar: toque longo em uma aba, arraste, solte.
A implementação usa react-native-gesture-handler (obrigatório no React Native para gestos complexos) combinado com react-native-reanimated para animações fluidas.
A grande armadilha: gesture-handler obrigatório
Esta é uma armadilha na qual perdi horas. Em um GestureDetector, é obrigatório usar os componentes de react-native-gesture-handler — TouchableOpacity, FlatList, ScrollView — e nunca os de react-native. Os componentes nativos simplesmente não recebem os eventos de toque em um contexto de gesture-handler.
O bug é traiçoeiro: tudo compila, não há nenhum aviso, mas o arrastar e soltar não funciona. Tu passas horas procurando um problema de lógica quando é apenas um import errado. Tornou-se uma regra cardinal do projeto: em qualquer componente que use gestos, verificar os imports.
A persistência entre dispositivos
A ordem das abas é salva no banco de dados em userProfile.mainTabsOrder. Não é apenas um AsyncStorage local — se o usuário fizer login em outro dispositivo ou reinstalar o aplicativo, ele encontrará sua configuração. Este detalhe parece menor, mas contribui para a sensação de um aplicativo "inteligente" que te conhece.
O formato é simples: um array JSON de identificadores de abas. ["dictaphone", "agenda", "feed", "groups", "social", "profile"]. Ao carregar, o aplicativo lê este array e reorganiza a navegação de acordo. Se uma aba estiver faltando (por exemplo, após a adição de um recurso), ela é adicionada no final.
Como funciona o carrossel infinito no feed?
O feed do TAMSIV exibe um fluxo de elementos: tarefas, memorandos, eventos, atividade de gamificação. Quando tu clicas em um elemento, queres poder navegar para o anterior ou o seguinte sem voltar à lista. Este é o padrão do carrossel infinito.
A implementação baseia-se em um componente FlatList horizontal (de react-native-gesture-handler, claro) com um sistema de pré-carregamento de elementos adjacentes. Quando tu visualizas o elemento N, os elementos N-1 e N+1 já estão carregados na memória e renderizados fora da tela.
O resultado: sem spinner, sem carregamento visível. A ilusão de um conteúdo sempre pronto. O deslize é instantâneo. É esse tipo de detalhe invisível que faz a diferença entre um aplicativo que usamos por obrigação e um aplicativo que usamos por prazer.
O desafio da memória
O pré-carregamento tem um custo: a memória. Se cada elemento do feed contiver imagens ou vídeos, carregar 3 elementos ao mesmo tempo (atual + 2 adjacentes) pode consumir muita RAM. Implementei um sistema de reciclagem: os elementos distantes (N-3, N+3 e além) são liberados da memória. Apenas a janela deslizante de 5 elementos é mantida.
Este padrão está diretamente ligado ao sistema de cache que construí mais tarde. As URLs assinadas do Supabase que expiram após 60 minutos tornam o pré-carregamento ainda mais crítico: as URLs devem estar frescas quando o usuário desliza.
Por que as microinterações são tão importantes?
Esses três recursos — captura de vídeo, arrastar e soltar, carrossel — são invisíveis individualmente. Ninguém baixa um aplicativo pelo seu arrastar e soltar de abas. Mas, coletivamente, eles criam uma sensação de fluidez que faz toda a diferença.
Os estudos de UX do Nielsen Norman Group mostram que a percepção de qualidade de um aplicativo é mais influenciada pelas microinterações do que pelos recursos principais. Um tempo de resposta de 100ms é percebido como instantâneo. Acima de 300ms, o usuário começa a sentir um atraso.
É exatamente a filosofia que segui para o botão de IA animado: cada interação deve parecer "viva". A animação do botão de gravação de vídeo, o feedback háptico ao arrastar e soltar, o encaixe magnético do carrossel — tudo contribui para essa sensação.
Medí o impacto dessas mudanças com as análises do Firebase. O tempo médio gasto no aplicativo aumentou após a implantação desses recursos. Não porque eles adicionam funcionalidades, mas porque tornam o uso mais agradável. Os usuários permanecem mais tempo quando o aplicativo é fluido.
Quais ferramentas usar para animações fluidas no React Native?
Para obter animações a 60fps no celular, tu não podes usar a API Animated básica do React Native. Tudo deve passar pela thread UI nativa, não pela thread JavaScript. Aqui está a pilha que uso:
- react-native-reanimated: as animações rodam na thread nativa, sem travamentos
- react-native-gesture-handler: gerenciamento de gestos diretamente na thread nativa
- LayoutAnimation: para transições de layout simples (adição/remoção de elementos)
A combinação reanimated + gesture-handler tornou-se o padrão de fato para animações React Native em 2026. Se tu ainda não as usas, migra. A diferença de fluidez é imediata e massiva.
Detalhei o uso desta pilha no artigo sobre a pesquisa contextual com deslize, onde o gesto de deslizar para trás para fechar as telas de detalhes usa exatamente o mesmo padrão.
O que esses detalhes me ensinaram sobre o desenvolvimento móvel
Essas duas semanas de trabalho nos "detalhes" foram das mais satisfatórias do projeto. Não porque os recursos são impressionantes — eles não são — mas porque têm um impacto desproporcional na experiência do usuário.
É uma lição que aprendi à custa própria: como desenvolvedor solo, a tentação é sempre correr atrás do próximo grande recurso. Mas os usuários não veem os recursos. Eles sentem a experiência. E a experiência é a soma de centenas de microdecisões: o atraso de uma animação, o tamanho de uma área de toque, o feedback de um gesto.
Isso se alinha com o que explico no artigo sobre o onboarding: a primeira impressão é crucial, e ela se manifesta nesses detalhes. Um onboarding fluido com animações cuidadosas transmite confiança. Um onboarding travado com transições bruscas dá vontade de desinstalar.
FAQ
Por que limitar a duração dos vídeos a 60 segundos?
Para manter os arquivos leves e o armazenamento viável. A 2 Mbps em 720p, 60 segundos representam aproximadamente 8 MB. Além disso, os tempos de upload e os custos de armazenamento tornam-se problemáticos para um aplicativo de produtividade que não é focado em vídeo.
O arrastar e soltar das abas funciona no iOS?
Sim. A implementação usa react-native-gesture-handler e react-native-reanimated, que são multiplataforma. O mesmo código funciona no Android e iOS sem modificação. Apenas os ajustes de feedback háptico são específicos para cada plataforma.
O carrossel infinito não consome muita memória?
Não, graças ao sistema de reciclagem. Apenas 5 elementos são mantidos na memória (o elemento atual + 2 de cada lado). Os elementos mais distantes são liberados. Em dispositivos com pouca RAM, a janela pode ser reduzida para 3 elementos.
É possível desativar a personalização das abas?
A ordem padrão pode ser restaurada a qualquer momento através das configurações do aplicativo. Um botão "Redefinir" retorna à ordem original. A ordem personalizada também é removida se o usuário sair da conta.
Por que não usar Lottie para as animações?
Lottie é excelente para animações ilustrativas (ícones animados, telas de splash), mas não é adequado para animações interativas controladas por gestos. react-native-reanimated permite vincular diretamente o gesto à animação, quadro a quadro, o que Lottie não consegue fazer.