Help us understand the problem. What is going on with this article?

react-native-dark-mode でダークモードに対応する

はじめに

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

darklight が得られます。
オブジェクトのキーとして利用したい時に使えるでしょう。

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 の変更を購読することができます。

  1. 端末で設定アプリを開く
  2. 設定アプリでダークモードの設定を変更する
  3. アプリに戻る

3 でアプリが再びフォアグラウンド状態になった時に、mode の切り替えが取得できます。

import { eventEmitter } from 'react-native-dark-mode'

eventEmitter.on('currentModeChanged', newMode => {
    console.log('Switched to', newMode, 'mode')
})

さいごに

上記の1~3 の手順でアプリを切り替えた時、 TextInput コンポーネントの color が切り替わらなかった...
解決できたらまた書きます。

ariiyu
Woz Inc. 代表・開発者 / キッチハイク ASSOCIATE / React Native を利用したアプリ開発を行っています。
https://woz-inc.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした