Splash screen animada no Expo e React Native
Por equipe Charmotion · Atualizado em

No Expo, mantenha a splash screen nativa estática, chame SplashScreen.preventAutoHideAsync() na inicialização, renderize a sua intro com a mesma cor de fundo e chame hideAsync() quando a intro estiver pronta para ser desenhada. A intro pode ser um arquivo Rive, um vídeo ou Lottie. O Rive precisa de um development build em vez do Expo Go, e o Expo recomenda conferir a splash em um build de release.
Principais pontos
- A splash do Expo é configurada no momento do build (cor, imagem, largura, modo escuro) e só tem um fade embutido.
- Chame
preventAutoHideAsync()no escopo global e depois oculte a splash quando o primeiro frame da sua intro estiver pronto. - Use uma única cor hex no
backgroundColorda splash, na view raiz e na tela de intro. - Rive e outras bibliotecas nativas de animação precisam de um development build; o Expo Go mostra o seu ícone no lugar da splash.
- Um arquivo de abertura do Charmotion não dispara evento de fim: espere 2,6 s e siga em frente.
Já tem um personagem ou logo? Veja seu personagem em telas reais de app: telas de splash, onboarding, carregamento e estado vazio. É grátis, roda no navegador e não envia nada.
Dá para animar a própria splash screen do Expo?
Não. A splash nativa é definida pelo config plugin expo-splash-screen com backgroundColor, image, imageWidth, resizeMode e uma variante dark, e essas configurações são compiladas no binário (Expo). Por baixo, ela é a launch screen do iOS e a splash screen do Android, com os mesmos limites.
{
"expo": {
"backgroundColor": "#0E1116",
"plugins": [
["expo-splash-screen", {
"backgroundColor": "#0E1116",
"image": "./assets/icon-rest.png",
"imageWidth": 160,
"dark": { "backgroundColor": "#0E1116", "image": "./assets/icon-rest.png" }
}]
]
}
}
O único movimento que o Expo oferece é um fade de saída: SplashScreen.setOptions({ duration, fade }), em que fade funciona só no iOS e a duração padrão é 400 ms. Qualquer coisa além disso, como o seu ícone surgindo na tela, precisa ser um componente React renderizado logo depois da splash, que é o que fazem os próprios exemplos de splash animada do Expo (Expo Blog).
Como funcionam preventAutoHideAsync e hideAsync?
preventAutoHideAsync() mantém a splash nativa visível até você chamar hideAsync() (ou hide()). A documentação do Expo recomenda chamá-la no escopo global, sem await, para que rode antes da primeira renderização. Depois, esconda a splash no momento em que a intro consegue desenhar o primeiro frame, nem antes, nem depois de um atraso arbitrário.
// app/_layout.tsx (Expo Router)
import * as SplashScreen from "expo-splash-screen";
import { useState } from "react";
import { Stack } from "expo-router";
import { LaunchIntro } from "../components/LaunchIntro";
SplashScreen.preventAutoHideAsync();
SplashScreen.setOptions({ duration: 200, fade: true });
export default function RootLayout() {
const [showApp, setShowApp] = useState(false);
if (!showApp) return <LaunchIntro onDone={() => setShowApp(true)} />;
return <Stack />;
}
Aqui, showApp é um state React seu, não um evento do arquivo de animação. É o componente de intro que decide quando esconder a splash e quando chamar onDone.
Como evitar o flash branco entre a splash e a intro?
Um flash branco significa que algo de outra cor foi desenhado entre a splash nativa e a sua intro. Elimine todas as lacunas:
- Uma única cor hex em tudo. Use o mesmo valor no
backgroundColordo plugin da splash, nobackgroundColorraiz do app e no container da intro. A configuração do app no Expo descreve a cor raiz como o fundo "atrás de qualquer uma das suas views React" e observa que ela exigeexpo-system-uino iOS (Expo). - Modo escuro também. Defina as opções
darkda splash e escolha o fundo da intro a partir do mesmo esquema de cores. - Esconda no momento certo. Chame
hideAsync()depois que o arquivo de animação tiver carregado, não na montagem. Se esconder antes, você vê por um instante uma árvore React vazia. - Mesma posição. Renderize o ícone da intro centralizado e no
imageWidthda splash, para que a transição não dê salto.
O exemplo de splash em vídeo do Evan Bacon aplica a mesma regra com preto: fundos pretos no app, na splash e na splash do iOS, a splash nativa mantida até o vídeo carregar, e então o vídeo toca e a splash some (GitHub).
A intro deve ser em Rive, vídeo ou Lottie?
| Opção | Ideal para | Pontos de atenção |
|---|---|---|
Rive (@rive-app/react-native) | Um arquivo vetorial pequeno cuja intro passa para um mascote interativo (loading, success, error) | Módulo nativo: exige development build; o guia de Expo do Rive indica SDK 53+ e iOS 15.1+ |
Vídeo (expo-video) | Intros detalhadas, 3D ou raster; revelações em tela cheia | Arquivo mais pesado; sem interação. Mais simples com um clipe opaco cujo fundo tenha a mesma cor da splash |
Lottie (lottie-react-native) | Uma animação vetorial linear que um designer já exportou | Toca do início ao fim; mudar para outros estados exige trocar arquivos ou segmentos |
Se o mesmo personagem deve continuar vivo no app depois da abertura, o Rive evita um segundo asset. Se a intro é uma cena cinematográfica única, vídeo exige menos código. Em qualquer caso, o padrão de transição não muda. Veja Lottie vs Rive vs vídeo para splash screen para o comparativo completo e Rive no Expo para configurar o development build.
Como tocar uma intro de abertura em Rive no Expo?
Este componente mantém a splash nativa até o arquivo carregar, toca a state machine de abertura, respeita o Reduzir Movimento e chama onDone após a duração da intro. Ele usa o runtime atual do Rive para React Native (Rive) e o AccessibilityInfo do React Native (React Native).
import { useEffect, useState } from "react";
import { AccessibilityInfo, Image, View } from "react-native";
import * as SplashScreen from "expo-splash-screen";
import { RiveView, useRiveFile } from "@rive-app/react-native";
const BG = "#0E1116"; // same hex as the splash backgroundColor
const INTRO_MS = 2600; // LaunchStateMachine fires no end event
export function LaunchIntro({ onDone }: { onDone: () => void }) {
const { riveFile } = useRiveFile(require("../assets/launch.riv"));
const [reduce, setReduce] = useState<boolean | null>(null);
useEffect(() => {
AccessibilityInfo.isReduceMotionEnabled().then(setReduce);
// Safety net: never trap the user if the file fails to load.
const t = setTimeout(() => { SplashScreen.hideAsync(); onDone(); }, 4000);
return () => clearTimeout(t);
}, []);
useEffect(() => {
if (reduce === null) return;
if (reduce) { SplashScreen.hideAsync(); onDone(); return; }
if (!riveFile) return;
SplashScreen.hideAsync();
const t = setTimeout(onDone, INTRO_MS);
return () => clearTimeout(t);
}, [reduce, riveFile]);
return (
<View style={{ flex: 1, backgroundColor: BG, alignItems: "center", justifyContent: "center" }}>
{riveFile && !reduce ? (
<RiveView file={riveFile} stateMachineName="LaunchStateMachine"
autoPlay={true} style={{ width: 160, height: 160 }} />
) : (
<Image source={require("../assets/icon-rest.png")} style={{ width: 160, height: 160 }} />
)}
</View>
);
}
Registre riv como extensão de asset no metro.config.js para que o require funcione, como descrito em Rive no Expo. Para uma intro em vídeo, troque a view do Rive por um player expo-video (Expo) e esconda a splash quando o player estiver pronto para tocar.
Um conjunto do Charmotion inclui este arquivo de abertura: LaunchStateMachine toca uma vez uma intro de 2,6 segundos do seu ícone ou mascote e termina na pose de repouso. O Charmotion exporta Rive e vídeo, não Lottie.
Por que minha splash não fica certa no Expo Go ou no dev build?
Porque nenhum dos dois é o resultado real. A documentação do Expo diz que o Expo Go mostra o ícone do app no lugar da splash screen e que as development builds também não refletem totalmente as configurações do config plugin, por isso recomenda testar a splash em uma release build.
- Expo Go: sem splash personalizada e sem Rive, porque o pacote do Rive contém código nativo (Expo).
- Development build: boa para construir o componente de intro; não é uma prévia fiel da splash.
- Release build ou preview build: o único lugar para avaliar a correspondência de cor e o tempo da transição. Teste uma inicialização a frio, modo claro e escuro, e o Reduzir Movimento ativado.
Depois de mudar as configurações da splash no app.json, recompile o app nativo (npx expo prebuild --clean para builds locais); a splash fica embutida no binário e não é atualizada com um reload de JavaScript.
Como adicionar uma splash screen animada no Expo
- Configure uma splash estática. Defina
backgroundColor,image,imageWidthedarkno pluginexpo-splash-screen, e o mesmobackgroundColorraiz. - Mantenha a splash. Chame
SplashScreen.preventAutoHideAsync()no escopo global do seu layout raiz. - Renderize a intro. Mostre um componente de intro sobre o mesmo fundo, com o ícone no tamanho da splash, usando Rive,
expo-videoou Lottie. - Esconda e espere. Chame
hideAsync()assim que a intro puder ser desenhada e continue depois da duração dela (2,6 s para um arquivo de abertura do Charmotion), com um timeout de segurança. - Teste uma release build. Verifique a inicialização a frio, o modo escuro e o Reduzir Movimento em uma release build ou preview build, não no Expo Go.
Perguntas frequentes
Posso usar um GIF como imagem da splash no Expo?
A imagem da splash é exibida pela launch screen nativa, que mostra uma imagem estática. Use um PNG estático na splash e toque qualquer animação, seja GIF, vídeo ou arquivo Rive, na primeira tela React com a mesma cor de fundo.
O preventAutoHideAsync funciona no Expo Go?
A função existe, mas o Expo Go mostra o ícone do app no lugar da splash configurada, então não dá para avaliar a transição ali. Use uma release build para ver como a splash e a intro ficam juntas.
Como saber quando uma intro de abertura em Rive terminou no React Native?
Se o arquivo dispara um evento próprio, escute esse evento. A LaunchStateMachine do Charmotion não dispara nenhum: ela toca a intro de 2,6 segundos uma vez e termina na pose de repouso, então inicie um timer de 2.600 ms quando o arquivo carregar e continue quando ele acabar.
Fontes
- SplashScreen (expo-splash-screen), Expo
- app.json / app.config.js reference (backgroundColor), Expo
- How to add an animated splash screen with Expo Custom Assets, Expo Blog
- expo-splash-video-example, Evan Bacon on GitHub
- Introduction to development builds, Expo
- Adding Rive to Expo, Rive
- React Native runtime, Rive
- AccessibilityInfo, React Native
- Video (expo-video), Expo
Guias relacionados
- Rive no Expo: exige development build (não o Expo Go)
- Como adicionar animação a um app React Native
- Lottie vs Rive no React Native e no Flutter (2026)
- Guia de animação de abertura e splash screen para apps
- Lottie vs Rive vs vídeo para animação de splash screen
- Prompts de IA para adicionar uma splash screen ou um mascote de carregamento animado