React Native アプリでダークモードを実装2021/08/31

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.tsxprovider を用意します。 この時に 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 を用いることで簡単にダークモードの実装を行うことができました。 実装の方法は他にもいくつかあるのですが、今回は簡単にできるものを紹介しました。

  • このエントリーをはてなブックマークに追加