Skip to content

Commit 2e0cb32

Browse files
fix: Correctly implement Material 3 dynamic theming
This commit fixes the implementation of Material 3 dynamic theming by following the official `react-native-paper` documentation. Key changes: - Removed the `@pchmn/expo-material3-theme` dependency. - Updated `styles/theme.js` to correctly handle dark and light modes using `MD3LightTheme`, `MD3DarkTheme`, and `adaptNavigationTheme`. - Created a `PreferencesContext` to manage theme toggling. - Updated `App.js` to use `useColorScheme` and provide the correct theme to both `PaperProvider` and `NavigationContainer`. - Added a `Switch` to the `AccountScreen` to allow you to toggle between light and dark themes. - Installed `deepmerge` to combine theme objects.
1 parent 2a659d4 commit 2e0cb32

File tree

5 files changed

+68
-23
lines changed

5 files changed

+68
-23
lines changed

frontend/App.js

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
1+
import { NavigationContainer } from '@react-navigation/native';
12
import React from 'react';
2-
import AppNavigator from './navigation/AppNavigator';
3+
import { useColorScheme } from 'react-native';
34
import { PaperProvider } from 'react-native-paper';
45
import { AuthProvider } from './context/AuthContext';
5-
import { useAppTheme } from './styles/theme';
6-
7-
const Main = () => {
8-
const theme = useAppTheme();
9-
return (
10-
<PaperProvider theme={theme}>
11-
<AppNavigator />
12-
</PaperProvider>
13-
)
14-
}
6+
import AppNavigator from './navigation/AppNavigator';
7+
import { CombinedDarkTheme, CombinedDefaultTheme, PreferencesContext } from './styles/theme';
158

169
export default function App() {
10+
const colorScheme = useColorScheme();
11+
const [isThemeDark, setIsThemeDark] = React.useState(colorScheme === 'dark');
12+
13+
let theme = isThemeDark ? CombinedDarkTheme : CombinedDefaultTheme;
14+
15+
const toggleTheme = React.useCallback(() => {
16+
return setIsThemeDark(!isThemeDark);
17+
}, [isThemeDark]);
18+
19+
const preferences = React.useMemo(
20+
() => ({
21+
toggleTheme,
22+
isThemeDark,
23+
}),
24+
[toggleTheme, isThemeDark]
25+
);
26+
1727
return (
18-
<AuthProvider>
19-
<Main />
20-
</AuthProvider>
28+
<PreferencesContext.Provider value={preferences}>
29+
<AuthProvider>
30+
<PaperProvider theme={theme}>
31+
<NavigationContainer theme={theme}>
32+
<AppNavigator />
33+
</NavigationContainer>
34+
</PaperProvider>
35+
</AuthProvider>
36+
</PreferencesContext.Provider>
2137
);
2238
}

frontend/frontend/frontend/package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
},
1111
"dependencies": {
1212
"@expo/metro-runtime": "~5.0.4",
13-
"@pchmn/expo-material3-theme": "^1.0.0",
1413
"@react-native-async-storage/async-storage": "^2.2.0",
1514
"@react-navigation/bottom-tabs": "^7.4.4",
1615
"@react-navigation/native": "^7.1.16",
1716
"@react-navigation/native-stack": "^7.3.23",
1817
"axios": "^1.11.0",
18+
"deepmerge": "^4.3.1",
1919
"expo": "~53.0.20",
2020
"expo-status-bar": "~2.2.3",
2121
"react": "19.0.0",

frontend/screens/AccountScreen.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import React, { useContext } from 'react';
22
import { View, StyleSheet, Alert } from 'react-native';
3-
import { Text, Appbar, Avatar, List, Divider, useTheme } from 'react-native-paper';
3+
import { Text, Appbar, Avatar, List, Divider, useTheme, Switch } from 'react-native-paper';
44
import { AuthContext } from '../context/AuthContext';
5+
import { PreferencesContext } from '../styles/theme';
56

67
const AccountScreen = ({ navigation }) => {
78
const theme = useTheme();
89
const { user, logout } = useContext(AuthContext);
10+
const { toggleTheme, isThemeDark } = useContext(PreferencesContext);
911

1012
const handleLogout = () => {
1113
logout();
@@ -56,6 +58,12 @@ const AccountScreen = ({ navigation }) => {
5658
onPress={() => navigation.navigate('EditProfile')}
5759
/>
5860
<Divider />
61+
<List.Item
62+
title="Dark Mode"
63+
left={() => <List.Icon icon="theme-light-dark" />}
64+
right={() => <Switch value={isThemeDark} onValueChange={toggleTheme} />}
65+
/>
66+
<Divider />
5967
<List.Item
6068
title="Email Settings"
6169
left={() => <List.Icon icon="email-settings" />}

frontend/styles/theme.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
1+
import React from 'react';
12
import {
2-
MD3LightTheme as DefaultTheme,
3-
useTheme,
3+
NavigationContainer,
4+
DarkTheme as NavigationDarkTheme,
5+
DefaultTheme as NavigationDefaultTheme,
6+
} from '@react-navigation/native';
7+
import {
8+
MD3DarkTheme,
9+
MD3LightTheme,
10+
adaptNavigationTheme,
411
} from 'react-native-paper';
5-
import { useMaterial3Theme } from '@pchmn/expo-material3-theme';
12+
import merge from 'deepmerge';
13+
14+
export const PreferencesContext = React.createContext({
15+
toggleTheme: () => {},
16+
isThemeDark: false,
17+
});
18+
19+
const { LightTheme, DarkTheme } = adaptNavigationTheme({
20+
reactNavigationLight: NavigationDefaultTheme,
21+
reactNavigationDark: NavigationDarkTheme,
22+
});
623

7-
export const useAppTheme = () => {
8-
const { theme } = useMaterial3Theme({ fallback: DefaultTheme });
9-
return useTheme(theme);
10-
}
24+
export const CombinedDefaultTheme = merge(MD3LightTheme, LightTheme);
25+
export const CombinedDarkTheme = merge(MD3DarkTheme, DarkTheme);

0 commit comments

Comments
 (0)