はじめに
react-native-dark-mode
というパッケージを使うことで、React Native アプリのダークモード対応をすることができた話です。
この記事ではiOS アプリについて書きます。 react-native-dark-mode
はAndroid についても対応しており、公式のドキュメントに導入方法が記載されています。
codemotionapps/react-native-dark-mode: Detect dark mode in React Native
所感
react-native-dark-mode
を利用することで、開発中のアプリの実装を大きく変更することなく、ダークモードを導入することができそうです。このライブラリはReact Hooks API を使って実装されており、ライブラリを利用する側は基本的にuseXxx
を使っていけばよいです。
React Native 公式っぽい書き方でスタイルを記述してきたプロジェクトであれば、スムーズに導入できそうな雰囲気があります。
(本記事の末尾に未対応の部分について書きましたが、がっつりプロダクションで使っていけそうかどうかは、もうちょっと検証したいです。)
検証環境
- React Native v0.62.2
- react-native-dark-mode v0.2.2
インストール
$ npm install react-native-dark-mode
$ cd ios && pod install && cd ../
使い方
ほぼ公式のドキュメントの通りの内容ですが、少しコメントも書きます。
useDarkMode
端末がダークモードになっている場合は true
が得られます。
import { useDarkMode } from 'react-native-dark-mode'
function Component() {
const isDarkMode = useDarkMode() // true or false
return <View style={{ backgroundColor: isDarkMode ? 'black' : 'white' }} />
}
useDarkModeContext
dark
か light
が得られます。
オブジェクトのキーとして利用したい時に使えるでしょう。
import { useDarkModeContext } from 'react-native-dark-mode'
const backgroundColors = {
light: 'white',
dark: 'black',
}
function Component() {
const mode = useDarkModeContext() // 'light' or 'dark'
const backgroundColor = backgroundColors[mode]
return <View style={{ backgroundColor }} />
}
DynamicStyleSheet, DynamicValue, useDynamicStyleSheet
React Native 標準のStyleSheet.create
と書き方がほぼ同じですが、 DynamicValue
を利用することによって、light mode の場合の値とdark mode の場合の値を定義することが出来ます。
スタイルを利用するコンポーネントで useDynamicStyleSheet
を利用して、動的なスタイルを利用できるようにします。
import { DynamicStyleSheet, DynamicValue, useDynamicStyleSheet } from 'react-native-dark-mode'
const dynamicStyles = new DynamicStyleSheet({
container: {
backgroundColor: new DynamicValue('white', 'black'), // dark mode なら黒背景
flex: 1,
},
text: {
color: new DynamicValue('black', 'white'), // dark mode なら白文字
textAlign: 'center',
},
})
function Component() {
const styles = useDynamicStyleSheet(dynamicStyles)
return (
<View style={styles.container}>
<Text style={styles.text}>My text</Text>
</View>
)
}
useDynamicValue
関数内で動的な値を使いたい場合に利用します。
light mode の場合の値とdark mode の場合の値を定義することが出来ます。
画像やアイコン名を動的に切り替えたい場合に使えます。
import { DynamicValue, useDynamicValue } from 'react-native-dark-mode'
const lightLogo = require('./light.png')
const darkLogo = require('./dark.png')
const logoUri = new DynamicValue(lightLogo, darkLogo)
function Logo() {
const source = useDynamicValue(logoUri)
return <Image source={source} />
}
import { useDynamicValue } from 'react-native-dark-mode'
function Input() {
const placeholderColor = useDynamicValue('black', 'white')
return <TextInput placeholderTextColor={placeholderColor} />
}
DarkModeProvider
特定のmode を子コンポーネントに渡す時に使えます。
import { DarkModeProvider } from 'react-native-dark-mode'
function MyScreen() {
return (
<>
{/* will be rendered using dark theme */}
<DarkModeProvider mode="dark">
<Component />
</DarkModeProvider>
{/* will be rendered using light theme */}
<DarkModeProvider mode="light">
<Component />
</DarkModeProvider>
{/* will be rendered using current theme */}
<Component />
</>
)
}
パフォーマンス改善のため、アプリ全体を DarkModeProvider
でラップすることが推奨されています。
ラップしなかった場合は、実行時にDebugger の警告が出る場合があります。
function App() {
return (
<DarkModeProvider>
{/* ... */}
</DarkModeProvider>
)
}
initialMode
アプリ開始時のmode (light か dark) を取得します。
import { initialMode } from 'react-native-dark-mode'
console.log('App started in', initialMode, 'mode')
eventEmitter
mode の変更を購読することができます。
- 端末で設定アプリを開く
- 設定アプリでダークモードの設定を変更する
- アプリに戻る
3 でアプリが再びフォアグラウンド状態になった時に、mode の切り替えが取得できます。
import { eventEmitter } from 'react-native-dark-mode'
eventEmitter.on('currentModeChanged', newMode => {
console.log('Switched to', newMode, 'mode')
})
さいごに
上記の1~3 の手順でアプリを切り替えた時、 TextInput
コンポーネントの color
が切り替わらなかった...
解決できたらまた書きます。