homeresume
 
   

Android app development with React Native

November 10, 20225 min read

This post covers the main notes needed from bootstrapping the app to publishing to the Play store.

Prerequisites

  • experience with React
  • installed Android studio

Bootstrapping the app

Run the following commands

npx react-native init <app_name>
cd <app_name>

Running the app on the device via USB

USB debugging has to be enabled on the device. Run the following commands

npx react-native start
npx react-native run-android

Installation can be done via the following command

npx react-native run-android --variant=release

App name

It can be changed in android/app/src/main/res/values/strings.xml file

App Icon Generator can be used for generating the images. Downloaded images should be stored in mipmap (android/app/src/main/res/mipmap-*hdpi/ic_launcher.png) folders.

Splash screen

A splash screen is the first thing user sees after opening the app, it usually shows an app logo with optional animations. More details are covered in Splash screen with React Native post

Bottom navigation bar

react-native-paper provides a bottom navigation bar component, and route keys are mapped with the components. react-native-vector-icons is needed for the proper vector rendering, a list of available icons can be found here

npm i react-native-paper react-native-vector-icons
// App.js
import React, { useState } from 'react';
import { StyleSheet } from 'react-native';
import { BottomNavigation, Text } from 'react-native-paper';
const HomeRoute = () => <Text style={style.text}>Home</Text>;
const SettingsRoute = () => <Text style={style.text}>Settings</Text>;
const style = StyleSheet.create({
text: {
textAlign: 'center'
}
});
const App = () => {
const [index, setIndex] = useState(0);
const [routes] = useState([
{
key: 'home',
title: 'Home',
icon: 'home'
},
{
key: 'settings',
title: 'Settings',
icon: 'settings-helper'
}
]);
const renderScene = BottomNavigation.SceneMap({
home: HomeRoute,
settings: SettingsRoute
});
return (
<BottomNavigation
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
/>
);
};
export default App;

file: android/app/build.gradle

apply plugin: "com.android.application"
import com.android.build.OutputFile
import org.apache.tools.ant.taskdefs.condition.Os
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" // <-- ADD THIS
// ...

SVG files

react-native-svg library can be used for handling SVG files.

import React from 'react';
import { SvgXml } from 'react-native-svg';
export function Logo() {
const xml = `<svg>...</svg>`;
return <SvgXml xml={xml} />;
}

State management

React provides Context to deal with state management without external libraries.

Context setup with app wrapper

// src/context/index.js
import { createContext, useContext, useMemo, useReducer } from 'react';
import { appReducer, initialState } from './reducer';
const appContext = createContext(initialState);
export function AppWrapper({ children }) {
const [state, dispatch] = useReducer(appReducer, initialState);
const contextValue = useMemo(() => {
return { state, dispatch };
}, [state, dispatch]);
return (
<appContext.Provider value={contextValue}>{children}</appContext.Provider>
);
}
export function useAppContext() {
return useContext(appContext);
}

Reducer setup

// src/context/reducer.js
import { INCREMENT_COUNTER } from './constants';
export const initialState = {
counter: 0
};
export const appReducer = (state, action) => {
switch (action.type) {
case INCREMENT_COUNTER: {
return {
...state,
counter: state.counter + 1
};
}
default:
return state;
}
};
// src/context/constants.js
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';

Wrapped app with Context and its usage

// App.js
import React, { useEffect, useState } from 'react';
import { StyleSheet } from 'react-native';
import SplashScreen from 'react-native-splash-screen';
import { BottomNavigation, Button, Text } from 'react-native-paper';
import { AppWrapper, useAppContext } from './src/context';
import { INCREMENT_COUNTER } from './src/context/constants';
const HomeRoute = () => {
const { state } = useAppContext();
return <Text style={style.text}>counter: {state.counter}</Text>;
};
const SettingsRoute = () => {
const { dispatch } = useAppContext();
const onPress = () => {
dispatch({ type: INCREMENT_COUNTER });
};
return <Button onPress={onPress}>Increment counter</Button>;
};
const style = StyleSheet.create({
text: {
textAlign: 'center'
}
});
const App = () => {
const [index, setIndex] = useState(0);
const [routes] = useState([
{
key: 'home',
title: 'Home',
icon: 'home'
},
{
key: 'settings',
title: 'Settings',
icon: 'settings-helper'
}
]);
const renderScene = BottomNavigation.SceneMap({
home: HomeRoute,
settings: SettingsRoute
});
useEffect(() => SplashScreen.hide(), []);
return (
<AppWrapper>
<BottomNavigation
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
/>
</AppWrapper>
);
};
export default App;

Error tracing

Sentry can be used for it

Prerequisites

  • React Native project created

Setup

Run the following commands

npm i @sentry/react-native
npx @sentry/wizard -i reactNative -p android

Analytics

It is useful to have more insights about app usage, like custom events, screen views, numbers of installations/uninstallations, etc. React Native Firebase provides analytics as one of the services.

Prerequisites

  • created Firebase project

Setup

Create an Android app within created Firebase project. The package name should be the same as the one specified in the Android manifest (android/app/src/main/AndroidManifest.xml). Download google-service.json file and place it inside android/app folder.

Extend the following files

file: android/app/build.gradle

apply plugin: "com.android.application"
apply plugin: "com.google.gms.google-services" <-- ADD THIS

file: android/build.gradle

buildscript {
// ...
dependencies {
// ...
classpath("com.google.gms:google-services:4.3.14") <-- ADD THIS
}
}
// ...

Run the following commands

npm i @react-native-firebase/app @react-native-firebase/analytics

Usage

Screen views can be logged by the following change

// App.js
import analytics from '@react-native-firebase/analytics';
// ...
const App = () => {
// ...
const onIndexChange = (i) => {
if (index === i) {
return;
}
setIndex(i);
analytics()
.logScreenView({
screen_class: routes[i].key,
screen_name: routes[i].key
})
.catch(() => {});
};
// ...
return (
<AppWrapper>
<BottomNavigation
navigationState={{ index, routes }}
onIndexChange={onIndexChange}
renderScene={renderScene}
/>
</AppWrapper>
);
};
// ...

Custom events can be logged by the following code

// src/utils/analytics.js
import analytics from '@react-native-firebase/analytics';
export const trackCustomEvent = async (eventName, params) => {
analytics()
.logEvent(eventName, params)
.catch(() => {});
};

Publishing to the Play store

Prerequisites

  • Verified account
  • Paid one-time fee (25\$)

 

© 2022