React Native アプリでダークモードを実装
React Native アプリでのダークモード実装の例です。
端末で設定しているテーマを取得して、それに応じてアプリの色設定も反映されるようにします。
React Native で用意されているAPIと、ライブラリの仕組みを使った簡単な実装方法を紹介します。
結論を先に言うと
パッケージを利用して provider を設定することと、各コンポーネントで色の呼び出し方を修正することで簡単に対応できます。
対応方法はいくつかありますが、今回は React Navigation
を用いたやり方と React Native Paper
を用いたやり方を紹介します。
React Navigation
を利用した場合は、設定できる色のキーが規定の物しか使えず自由度が少ないです。
React Native Paper
を利用した場合、自由に色のキーを追加できます。なので React Native Paper
の方がおすすめです。
概要
- 端末のテーマを取得する
- React Navigation でテーマを用意
- React Native Paper でテーマを用意
端末のテーマを取得する
端末のテーマを以下で取得します。light
または dark
の文字列が取得できます。
これを使って、テーマを出し分けることになります。
import { useColorScheme } from "react-native";
~~~
const scheme = useColorScheme(); // "light" or "dark"
~~~
Expo を利用している場合
もし Expo を利用して開発している場合は、追加の対応が必要です。
https://docs.expo.dev/guides/color-schemes/
app.json
で以下設定を追加することで、ユーザーの設定した外観設定が反映されるようになります。
この設定をしていない場合デフォルトで常に light
を返す状態になっています。
"expo": {
~~~
"userInterfaceStyle": "automatic",
~~~
}
React Navigation でテーマを用意
https://reactnavigation.org/docs/themes
provider を用意する
アプリのアクセスポイントとなる App.tsx
に provider
を用意します。
この時に theme
を渡すことで、内部の react-navigation
コンポーネントに theme
の設定値が反映されます。
渡す theme
を上記 useColorScheme
の値によって切り替えることでアプリのテーマ変更を反映できます。
加えて、各コンポーネントで theme
に設定されている色コードを取得できるので、それを使って自分で用意したコンポーネントの色も制御できます。
import { useColorScheme } from "react-native";
import {
NavigationContainer,
DefaultTheme,
} from "@react-navigation/native";
~~~
const scheme = useColorScheme();
const lightTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: Colors.primary,
accent: Colors.secondary,
},
};
const darkTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: Colors.darkPrimary,
accent: Colors.darkSecondary,
},
};
~~~
return (
<NavigationContainer theme={ scheme === "dark" ? darkTheme : ligheTheme }>
<View style={styles.container}>
<AppNavigator />
</View>
</NavigationContainer>
)
各コンポーネントで theme の色設定を取得する
コンポーネントでは useTheme
を使ってテーマの情報を取得できます。色の情報が格納されているので、それを取得してコンポーネントの style に反映します。
import { useTheme } from "@react-navigation/native";
~~~
const { colors } = useTheme();
~~~
return (
<View style={[styles.container, { backgroundColor : colors.primary }]}>
</View>
);
上記のように実装することで簡単にダークモードの実装が可能です。
しかしReact-Navigation
を使った場合、テーマの色として設定できるキーが決まっており、以下の6つのみです。
- primary
- background
- card
- text
- border
- notification
個人的にはこれだけでは足りず、もっと多く設定したいと感じました。 他に同様のことができるパッケージを探してみました。
React Native Paperでテーマを用意
同様のことを React-Native-Paper
でも可能なことがわかりました。
しかもこちらでは任意のキーでテーマに色を追加でき、色以外にもフォントやアニメーション設定などもテーマとして用意できます。
https://callstack.github.io/react-native-paper/theming.html
provider を用意する
React-Navigation
とほぼ同様の実装になります。
import { useColorScheme } from "react-native";
import { Provider as PaperProvider, DefaultTheme } from "react-native-paper";
~~~
const scheme = useColorScheme();
const lightTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: Colors.primary,
accent: Colors.secondary,
favorite: Colors.favorite,
},
};
const darkTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: Colors.darkPrimary,
accent: Colors.darkSecondary,
favorite: Colors.favorite,
},
};
~~~
return (
<PaperProvider theme={ scheme === "dark" ? darkTheme : ligheTheme }>
<View style={styles.container}>
<AppNavigator />
</View>
</PaperProvider>
)
各コンポーネントで theme の色設定を取得する
import { useTheme } from "react-native-paper";
~~~
const { colors } = useTheme();
~~~
return (
<View style={[styles.container, { backgroundColor : colors.favorite}]}>
</View>
);
React-Native-Paper
の場合は任意のキーで色を追加できます。
フォント関連のスタイルやアニメーションなどのスタイルも設定できます。こっちの方が便利ですね。
カスタムテーマの型を用意する
上記のように、任意キーの色やプロパティを設定した場合、TypeScript だと型のエラーが出てしまいます。
以下のように独自の型の定義を設定することで解決できます。App.tsx
に以下の記述を追加します。
declare global {
namespace ReactNativePaper {
interface ThemeColors {
favorite: string;
}
interface Theme {
shadow: {};
}
}
}
おわりに
React-Native-paper
を用いることで簡単にダークモードの実装を行うことができました。
実装の方法は他にもいくつかあるのですが、今回は簡単にできるものを紹介しました。