私が開発しているアプリLangJournalではDarkMode(ダークモード)に対応しております。本日はダークモードをReact Nativeでどのように実装すれば良いかを記載していきます。
最終的なゴールは、下記二つです。
・端末の設定によりダークモードとライトモードを自動で切り替えることができる
・ユーザがダークモードとライトモードをアプリ内で選ぶことができる
一つずつ解説していきます。
まずは、react-native-paperをインストールします。
yarn add react-native-paper
react-native-paperは色を保持するために使います。例えば、ライトモードの時のbackgroundは#fff、textは#000、ダークモードの時のbackgroundは#000、textは#fffといったような感じです。
同じことができるパッケージは複数あります。react-navigationも検索すると上位に出てきますが、こちらはオリジナルな変数を持てません。primary, backgroundなど決まった変数しか持てないので、使い勝手が悪いです。react-native-paperを使うのが無難でしょう。
以下は具体的なコードです。必要最小限なものだけ記載します。
import { useTheme } from 'react-native-paper';
const black = '#2a2a2a';
const white = '#fff';
const main = '#2D9CDB';
// ダークモード、ライトモード共有で使うものはまとめておくと分かりやすい
const commonColor = {
main,
// ここに他の色も追加していく。
};
// ライトテーマ
export const lightTheme = {
dark: false, // 一番最後にステータスバーの設定の時に使う
colors: {
...commonColor,
background: white,
primary: black,
// ここに他の色も追加していく。
},
};
// ダークテーマ
export const darkTheme = {
dark: true, // 一番最後にステータスバーの設定の時に使う
colors: {
...commonColor,
background: black,
primary: white,
// ここに他の色も追加していく。
},
};
// TypeScript用
export type AppTheme = typeof lightTheme | typeof darkTheme;
// useThemeのままだと、オリジナル変数を呼ぶときにTypeScriptでエラーになるので、
// useAppThemeを別途用意した方がいい
export const useAppTheme = () => useTheme<AppTheme>();
これでカラーの設定は完了です。
続いて、ダークモードのON/OFFを切り替えられる画面を作ります。私は下記のようなUIにしております。
LangJournalでは、「端末の設定を使う」「ライトモード」「ダークモード」の3つを用意しております。TwitterやNoteなどのアプリを確認したところ、上記の3つが設定できるものが多かったです。
実装方法は自由ですが、上記変数をReduxもしくはContextで持てるようにしましょう。コードはプロジェクトによって違いがありすぎるのと今回の記事のテーマとは関係ないので割愛します。
続いて、全体を囲うファイルを修正していきます。App.tsxなどで書いているプロジェクトが多いのではないでしょうか。
まずはコードから
import { useColorScheme } from 'react-native';
// 中略
const theme = useMemo(() => {
if (!localStatus.darkMode || localStatus.darkMode === 'device') {
// 端末の設定を使う を選んでいる時
if (scheme === 'light') {
return lightTheme;
} else {
return darkTheme;
}
} else if (localStatus.darkMode === 'light') {
return lightTheme;
} else {
return darkTheme;
}
}, [localStatus.darkMode, scheme]);
useColorSchemeは端末のダークモード、ライトモードを取得できます。
localStatus.darkModeは表示設定画面で設定した値です。私はReduxで持っているため上記のようなコードになっております。
続いて、全体をラップします。上記で書いたものと同じファイルに追記します。
import { useColorScheme } from 'react-native';
import { PaperProvider } from 'react-native-paper'; // 追加
// 中略
const theme = useMemo(() => {
if (!localStatus.darkMode || localStatus.darkMode === 'device') {
if (scheme === 'light') {
return lightTheme;
} else {
return darkTheme;
}
} else if (localStatus.darkMode === 'light') {
return lightTheme;
} else {
return darkTheme;
}
}, [localStatus.darkMode, scheme]);
<PaperProvider theme={theme}> // ここに追加
<NavigationContainer>
<Stack.Navigator>
// プロジェクトにより違うため省略
</Stack.Navigator>
</NavigationContainer>
</PaperProvider>
// ここに追加 // プロジェクトにより違うため省略
先ほど設定したthemeをPaperProviderに設定します。
ここまで実装が終われば、色をダークモードとライトモードで切り分けることができます。下記のように使います。
// 中略
import { useAppTheme } from '@/styles/colors';
const Text: React.FC = () => {
const theme = useAppTheme(); // このように呼び出す
return (
<Text
style={{color: color ? color : theme.colors.primary}} //このように指定する
>
Test
</Text>
);
};
あとは、地道に色を指定している箇所を書き換えていくだけです。できる限り共通コンポーネント化して、色を指定する箇所が少なくなるようにしましょう。
最後にStatusBarの設定です。StatusBarもダークモードで切り替える必要があります。
まずは、expo-status-barをインストールしてください。
expo install expo-status-bar
※Expoを使っていない場合は、react-nativeのStatusBarを使いましょう。以下は、Expoを使っている前提で説明していきますが、react-nativeのStatusBarに置き換えて実装してください。
import { useColorScheme } from 'react-native';
import { PaperProvider } from 'react-native-paper';
import { StatusBar } from 'expo-status-bar'; // 追加
// 中略
const theme = useMemo(() => {
if (!localStatus.darkMode || localStatus.darkMode === 'device') {
if (scheme === 'light') {
return lightTheme;
} else {
return darkTheme;
}
} else if (localStatus.darkMode === 'light') {
return lightTheme;
} else {
return darkTheme;
}
}, [localStatus.darkMode, scheme]);
<PaperProvider>
<NavigationContainer>
<StatusBar // ここに追加
backgroundColor={theme.colors.background}
style={theme.dark ? 'light' : 'dark'}
/>
<Stack.Navigator>
// 省略
</Stack.Navigator>
</NavigationContainer>
</PaperProvider>
theme.darkは、styles.tsで設定した値です。StatusBarのstyleにlight or darkを設定することで色を変えることができます。
以上で、ダークモードの設定完了です。
LangJournalは、日記を書くことで英語やフランス語などの外国語を学べるアプリです。英語学習に興味がある方や、私が開発したこのアプリに関心を持っている方は、ぜひインストールしてお試しください。
LangJournalのサイトはこちら