Idea
- Create native Splash Screen to be an image with the same background color as the animation
- Hide native Splash Screen as soon as JavaScript took control
- Create React Native modal to show animated splash screen
- Close this modal when everything is loaded
Create native splash screen
Create a splash screen activity in Android with theme
- Create SplashActivity MVP
class SplashActivity : AppCompatActivity(), SplashContract.SplashView {
private val presenter: SplashContract.Presenter = SplashPresenter(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
}
override fun openReactApp() {
Handler().postDelayed({
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
}, 3000)
}
override fun onResume() {
super.onResume()
presenter.onResume()
}
}
class SplashContract {
interface SplashView {
fun openReactApp()
}
interface Presenter {
fun onResume()
}
}
class SplashPresenter(private val view: SplashContract.SplashView) : SplashContract.Presenter {
override fun onResume() {
view.openReactApp()
}
}
- Create layout activity_splash.xml
You can set background color here android:background=”#000″
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
tools:context="com.your.project.splash.SplashActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
- Run Android app
You will still see the white blank screen before SplashActivity is loaded
To fix this, embed theme into SplashActivity in AndroidManifest.xml
Also, remove above android:background=”#000″ from layout xml file
...
<activity
android:name=".splash.SplashActivity"
android:theme="@style/AppTheme.Launcher"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
...
// src/main/res/values/styles.xml
<resources>
...
<style name="AppTheme.Launcher">
<item name="android:colorBackground">#000</item>
</style>
....
</resources>
Install react-native-splash-screen
Hide native splash screen in JS
import SplashScreen from 'react-native-splash-screen';
const App = () => {
useLayoutEffect(() => {
const timeId = setTimeout(() => {
SplashScreen.hide();
}, 100);
return () => {
clearTimeout(timeId);
};
}, []);
return (
<Provider store={store}>
<Splash />
<Navigation />
</Provider>
);
Create React Native modal to show animated splash screen
import React, { FC, useState } from 'react';
import LottieView from 'lottie-react-native';
import Assets from '../../../assets';
import { Modal, StyleSheet } from 'react-native';
import { useSelector } from 'react-redux';
import { name } from 'src/state/slice';
import { TGlobaState } from 'src/types';
interface Props {}
const Splash: FC<Props> = () => {
const [hasAnimationPlayedOnce, setHasAnimationPlayedOnce] = useState(false);
const handleAnimationFinish = () => {
setHasAnimationPlayedOnce(true);
};
const isAppInitialized = useSelector(
(state: TGlobaState) => state[name].isAppInitialized
);
const isModalVisible = !(isAppInitialized && hasAnimationPlayedOnce);
const onClose = () => {};
return (
<Modal
animationType="none"
transparent={true}
visible={isModalVisible}
onRequestClose={onClose}
>
<LottieView
style={styles.modal}
loop={false}
autoPlay
source={Assets.lottieFiles.planePath}
colorFilters={[{ keypath: 'Plane', color: 'rgb(255, 100, 0)' }]}
onAnimationFinish={handleAnimationFinish}
/>
</Modal>
);
};
const styles = StyleSheet.create({
modal: {
backgroundColor: '#3BACB6',
},
});
export default Splash;
Close this modal when everything is loaded
import React, { useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { useDispatch, useSelector } from 'react-redux';
import { name } from 'src/state/slice';
import { TGlobaState } from 'src/types';
import TabStack from './components/TabStack';
import LoginStack from './components/LoginStack';
import { storeIsAppInitialized } from 'src/state/slice';
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
const Routing: React.FC = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(storeIsAppInitialized(true));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const isLoggedIn = useSelector(
(state: TGlobaState) => state[name].isLoggedIn
);
return (
<NavigationContainer>
{isLoggedIn ? <TabStack /> : <LoginStack />}
</NavigationContainer>
);
};
export default Routing;
Leave a Reply