以前Qiitaで紹介したアプリペペロミアにダークモードを実装してみたので、実装方法について紹介します。
■ React Native製、予定作成アプリ「ペペロミア」を公開しました
https://qiita.com/wheatandcat/items/3324dfd141729e46009f
対象のアプリ: ペペロミア
■ アプリ公式サイト
https://peperomia.app/
GitHub
■ Peperomia
https://github.com/wheatandcat/Peperomia
環境
- Expo SDK 34
実装してみた
動画にまとめました。
ペペロミアでダークモードを実装してみた https://t.co/dBmyHqNExw @YouTubeさんから
— 飯野陽平 (@wheatAndCat) September 19, 2019
対象のpull request
■ ダークモードを実装する
https://github.com/wheatandcat/Peperomia/pull/108
実装方法
デザイン面
カラーを黒ベースに変えていきました。通常時とダークモード時を並べると、こんな感じです。
カラー定義
以下のカラー設定を決めて各要素に設定
■ PeperomiaNative/src/config/theme.ts
https://github.com/wheatandcat/Peperomia/blob/e9a4beac729938b6f88366324dbb8873144736b7/PeperomiaNative/src/config/theme.ts#L149-L163
要素名 | 通常 | Darkモード |
---|---|---|
バックグラウンド | white | black |
バックグラウンド(サブ) | lightGray | darkGray |
テキスト | black | lightGray |
テキスト(サブ) | darkGray | gray |
アイコン | black | lightGray |
チップ | lightGray | darkGray |
チップテキスト | black | gray |
ヘッダーバックグラウンド | Green | black |
タブバーバックグラウンド | lightGray | darkGray |
基本的には上記の要素のみカラーを変更して、それ以外は通常時とダークモードでカラーに差分は無いようしました。
画像の変更
こんな感じになり画像と背景色が、かぶって画像が見えづらくなりました。
なので色を反転した画像を用意して、こんな感じになりました。(+ステータスバーのカラーも変えました)
これで良さ気です。
コード面
react-native-extended-stylesheetを使う
一般的にReactNativeのstyleの宣言はStyleSheetを使用すると思います。
StyleSheetは最初にstyleを宣言してしまうので、あとから変数でカラーの値を変更しても値は動的に変更されません。
なので、StyleSheetのラッパーであるreact-native-extended-stylesheetを使用してカラー設定をしていきます。
■ react-native-extended-stylesheet
https://github.com/vitalets/react-native-extended-stylesheet
こちらのライブラリは最初にカラー定義を設定して
import EStyleSheet from "react-native-extended-stylesheet";
const setLight = () => {
EStyleSheet.build({
$theme: "light",
$background: "#fff",
$text: "#333631"
});
}
const setDark = () => {
EStyleSheet.build({
$theme: "dark",
$background: "#333631",
$text: "#fff"
});
}
以下のようにstyleを指定します。
import React from "react";
import { View, Text } from "react-native";
import EStyleSheet from "react-native-extended-stylesheet";
export default () => (
<View style={styles.root}>
<Text style={styles.text}>テスト</Text>
</View>
)
const styles = EStyleSheet.create({
root: {
height: "100%",
backgroundColor: "$background", // ← EStyleSheet.buildで定義した変数
},
text: {
color: "$text", // ← EStyleSheet.buildで定義した変数
}
});
あとは、以下のメソッドを実行すれば通常(Light)モード
setLight()
以下のメソッドを実行すればダークモードとstyle内のカラー変更が可能になります。
setDark()
・・・と言いたいところですが、これだけでは完全には変わりません。
以下のドキュメントに記載している通り、一度レンダリングされてしまったコンポーネントに関してはEStyleSheet.buildを実行してもstyleの変更はされません。値が適応されるのは再レンダリングのタイミングです。
■ react-native-extended-stylesheetの反映タイミング
https://github.com/vitalets/react-native-extended-stylesheet#theming
なので、ドキュメントの通り一旦render部分をnullにして、再レンダリングをさせる必要があります。
ペペロミアでは、Providerでカラー設定の制御を持ち、一旦アプリケーションのレンダリングをnullにしてから再レンダリングする実装でダークモードを実現しています。
■ PeperomiaNative/src/containers/Theme.tsx (カラー設定を制御するProvider)
https://github.com/wheatandcat/Peperomia/blob/d1c9308e9ec2a27ca7cbd3afc865652106fcf784/PeperomiaNative/src/containers/Theme.tsx
ここまで実装すればダークモード完成です。
その他の使えそうな設定
今回は使用していませんが、各ライブラリにThemeの設定が存在するので、この辺を利用するともっと良い感じに実装できるかもしれません。
■ React Navigation Theme設定
https://reactnavigation.org/docs/en/themes.html
■ React Native Elements ThemeProvider設定
https://react-native-training.github.io/react-native-elements/docs/customization.html#using-themeprovider
まとめ
最初は1日くらいで出来るだろうと思いましたが、stylesheetで結構ハマって結局3日くらいかかってしまいました。
一応ネタのため実装してみましたが、正直まだReact Nativeでダークモードを実装するのは時期尚早な気がします。
iOS13とAndroid10のリリースで両方ともダークモードが実装され需用が高まるはずなので、もっと良い実装方法が増えそうな気がするので、今は待ったほうが良いかなーと実装して思いました。
(再レンダリングを明示的に書かなくてはいけないのは、余り良い実装な気もしないので。。。)
Expoでダークモードを使ってみる
まだダークモード搭載のアプリはリリースはしていませんが、Expoにはデプロイしているので以下から実機で起動すること可能です。