開発環境
Expo Router 環境です。詳細は ⬇︎
app/ フォルダー構造
try🐶everything myproject$ tree app app ├── +html.tsx ├── [...missing].tsx ├── _layout.tsx └── index.tsx
src/ フォルダー構造
try🐶everything myproject$ tree src src ├── common │ ├── hooks │ │ ├── index.ts │ │ ├── themeContext.ts └── components ├── ExternalLink.tsx └── index.ts try🐶everything myproject$
実装する
ThemeContext を作成する
コンテキストを作成するには、コンポーネントの外部で createContext を呼び出します。
// src/common/hooks/themeContext.ts
import {createContext} from 'react'; export interface ThemeContextProps { preferredTheme: string; // <-- preferredTheme は任意 toggleTheme: () => {}; // <-- toggleTheme は任意 } export const ThemeContext = createContext({ // <-- ThemeContext は任意 preferredTheme: 'light', toggleTheme: () => {}, });
コンポーネントをラップする
コンポーネントをコンテキスト プロバイダーにラップして、内部のすべてのコンポーネントにこのコンテキストの値を指定します。
toggleTheme()
を実行する度に、preferredTheme
値がトグルされ、paperTheme
値も更新されます。
ThemeContext.Provider
の value
値として preferences
を渡します。
ラップする際の注意点は、Line 17,18 のように PaperProvider
もラップすることです。
// app/_layout.tsx
... import {ThemeContext} from 'common/hooks/themeContext'; ... export default function RootLayout() { const [preferredTheme, setTheme] = React.useState('light'); const toggleTheme = () => { setTheme(preferredTheme => (preferredTheme === 'light' ? 'dark' : 'light')); }; const preferences = React.useMemo( () => ({preferredTheme, toggleTheme: toggleTheme}), [preferredTheme, toggleTheme], ); const paperTheme = preferredTheme === 'dark' ? {...MD3DarkTheme, colors: jtheme.dark.colors} : {...MD3LightTheme, colors: jtheme.light.colors}; ... return ( <ThemeContext.Provider value={preferences}> <PaperProvider theme={paperTheme}> <RootLayoutNav /> </PaperProvider> </ThemeContext.Provider> ); } function RootLayoutNav() { return ( <Stack> <Stack.Screen name="index" /> ... </Stack> ); }
テーマをトグルする
メニューを実装するファイルで行ってください。
backgroundColor
は変わらないので Line 15
で設定を加えています。(仕様?不明!)
// app/index.tsx
// コピペで使用可能 import React, {memo} from 'react'; import {StyleSheet, View} from 'react-native'; import {Button, useTheme} from 'react-native-paper'; import {Ionicons} from '@expo/vector-icons'; import {ThemeContext} from 'common/hooks/themeContext'; const HomeScreen = () => { const theme = useTheme(); const {preferredTheme, toggleTheme} = React.useContext(ThemeContext); return ( <View style={[styles.container, {backgroundColor: theme.colors.background}]}> <View style={{position: 'absolute', top: 0, right: 0, margin: 20}}> {preferredTheme == 'dark' ? ( <Ionicons name="sunny" size={24} color={theme.colors.tertiary} onPress={toggleTheme} /> ) : ( <Ionicons name="moon" size={24} color={theme.colors.tertiary} onPress={toggleTheme} /> )} </View> <Button icon="camera" mode="contained" onPress={() => console.log('Pressed!')}> Press me </Button> </View> ); }; const styles = StyleSheet.create({ container: {flex: 1, alignItems: 'center', justifyContent: 'center'}, }); export default memo(HomeScreen);
ボタンを押しながら動作を確認できます。
参考文献
Theming // <– ⬆︎ コード内の jtheme.{dark|light}.colors を作成する方法
コメント