開発環境
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 を作成する方法



コメント