Skip to content

Commit

Permalink
docs: theming with react navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
lukewalczak committed Oct 5, 2022
1 parent 0afacdc commit ef74b0d
Showing 1 changed file with 107 additions and 19 deletions.
126 changes: 107 additions & 19 deletions docs/pages/8.theming-with-react-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,51 @@ In this guide we will look into how to apply theming for an application using Re
Offering different theme options, especially dark/light ones, becomes increasingly a standard requirement of the modern mobile application. Fortunately, both React Navigation and React Native Paper support configurable theming out-of-the-box.
But how to make them work together?

## Themes adaptation

### Material Design 2

Fortunately, in Material Design 2, both React Navigation and React Native Paper offer very similar API when it comes to theming and theme color structure. It's possible to import them in light and dark variants from both.

```js
import {
DarkTheme as NavigationDarkTheme,
DefaultTheme as NavigationDefaultTheme,
} from '@react-navigation/native';

import {
MD2LightTheme,
MD2DarkTheme,
} from 'react-native-paper';
```

### Material Design 3

From v5, React Native Paper theme colors structure is following the Material Design 3 <i>(known as Material You)</i> colors system, which differs significantly from both previous Paper's theme and React Navigation theme.

However, to simplify adapting React Navigation theme to the latest Paper's colors structure, it's worth using a utility called `adaptNavigationTheme` – it accepts navigation compliant themes in both modes and returns their equivalents adjusted to Material Design 3.

```ts
import {
DarkTheme as NavigationDarkTheme,
DefaultTheme as NavigationDefaultTheme,
} from '@react-navigation/native';

const { LightTheme, DarkTheme } = adaptNavigationTheme({
lightTheme: NavigationDefaultTheme,
darkTheme: NavigationDarkTheme,
});
```

Library exports also Material Design 3 themes in both modes:

```js
import {
MD3LightTheme,
MD3DarkTheme,
} from 'react-native-paper';
```

## Combining theme objects

Both libraries require a wrapper to be used at the entry point of the application.
Expand Down Expand Up @@ -71,9 +116,7 @@ export default function App() {
}
```
Fortunately, both React Navigation and React Native Paper offer very similar API when it comes to theming. It's possible to import default themes in light and dark variants from both.
React Navigation and React Native Paper use the same name for default themes - `DefaultTheme` and `DarkTheme`, so we need to alias them at the imports.
Our goal here is to combine those two themes, so that we could control the theme for the entire application from a single place.
Expand All @@ -83,54 +126,103 @@ To make things easier we can use [deepmerge](https://www.npmjs.com/package/deepm
yarn add deepmerge
```
### Material Design 2
```js
import {
NavigationContainer,
DarkTheme as NavigationDarkTheme,
DefaultTheme as NavigationDefaultTheme,
} from '@react-navigation/native';
import {
DarkTheme as PaperDarkTheme,
DefaultTheme as PaperDefaultTheme,
Provider as PaperProvider,
MD2DarkTheme,
MD2LightTheme,
} from 'react-native-paper';
import merge from 'deepmerge';

const CombinedDefaultTheme = merge(PaperDefaultTheme, NavigationDefaultTheme);
const CombinedDarkTheme = merge(PaperDarkTheme, NavigationDarkTheme);
const CombinedDefaultTheme = merge(MD2DarkTheme, NavigationDefaultTheme);
const CombinedDarkTheme = merge(MD2LightTheme, NavigationDarkTheme);
```
Alternatively, we could merge those themes using vanilla JavaScript
### Material Design 3
```js
import {
NavigationContainer,
DarkTheme as NavigationDarkTheme,
DefaultTheme as NavigationDefaultTheme,
} from '@react-navigation/native';
import {
MD3DarkTheme,
MD3LightTheme,
} from 'react-native-paper';
import merge from 'deepmerge';

const { LightTheme, DarkTheme } = adaptNavigationTheme({
lightTheme: NavigationDefaultTheme,
darkTheme: NavigationDarkTheme,
});

const CombinedDefaultTheme = merge(MD2DarkTheme, LightTheme);
const CombinedDarkTheme = merge(MD2LightTheme, DarkTheme);
```
Alternatively, we could merge those themes using vanilla JavaScript:
### Material Design 2
```js
const CombinedDefaultTheme = {
...PaperDefaultTheme,
...MD2LightTheme,
...NavigationDefaultTheme,
colors: {
...PaperDefaultTheme.colors,
...MD2LightTheme.colors,
...NavigationDefaultTheme.colors,
},
};
const CombinedDarkTheme = {
...PaperDarkTheme,
...MD2DarkTheme,
...NavigationDarkTheme,
colors: {
...PaperDarkTheme.colors,
...MD2DarkTheme.colors,
...NavigationDarkTheme.colors,
},
};
```
### Material Design 3
```js
const { LightTheme, DarkTheme } = adaptNavigationTheme({
lightTheme: NavigationDefaultTheme,
darkTheme: NavigationDarkTheme,
});

const CombinedDefaultTheme = {
...MD3LightTheme,
...LightTheme,
colors: {
...MD3LightTheme.colors,
...LightTheme.colors,
},
};
const CombinedDarkTheme = {
...MD3DarkTheme,
...DarkTheme,
colors: {
...MD3DarkTheme.colors,
...DarkTheme.colors,
},
};
```
## Passing theme with Providers
After combining the themes, we will be able to control theming in both libraries from a single source, which will come in handy later.
Next, we need to pass merged themes into the Providers. For this part, we use the dark one - `CombinedDarkTheme`.
```js
const CombinedDefaultTheme = merge(PaperDefaultTheme, NavigationDefaultTheme);
const CombinedDarkTheme = merge(PaperDarkTheme, NavigationDarkTheme);

const Stack = createStackNavigator();

export default function App() {
Expand Down Expand Up @@ -184,9 +276,6 @@ import { PreferencesContext } from './PreferencesContext';

const Stack = createStackNavigator();

const CombinedDefaultTheme = merge(PaperDefaultTheme, NavigationDefaultTheme);
const CombinedDarkTheme = merge(PaperDarkTheme, NavigationDarkTheme);

export default function App() {
const [isThemeDark, setIsThemeDark] = React.useState(false);

Expand Down Expand Up @@ -242,7 +331,6 @@ const Header = ({ scene }) => {
<Appbar.Content title={scene.route?.name} />
<TouchableRipple onPress={() => toggleTheme()}>
<Switch
style={[{ backgroundColor: theme.colors.accent }]}
color={'red'}
value={isThemeDark}
/>
Expand Down

0 comments on commit ef74b0d

Please sign in to comment.