feat : add theme context for dark and light mode
continuous-integration/drone/push Build is failing Details

pull/20/head
Rémi REGNAULT 1 year ago
parent b46f72d984
commit f24803afae

@ -8,6 +8,7 @@ import HomeStackScreen from './navigation/HomeStackScreen';
import ProfilesStackScreen from './navigation/ProfileStackScreen';
import CookingStackScreen from './navigation/CookingStackScreen';
import BottomBar from './navigation/BottomBar';
import { ThemeProvider } from './theme/ThemeContext';
import HomeIcon from './assets/images/home.png';
import ProfileIcon from './assets/images/person_icon.png';
@ -19,12 +20,14 @@ const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator initialRouteName='Home' tabBar={ (props) => <BottomBar {...props}/> }>
<Tab.Screen name='Profile' component={ProfilesStackScreen} options={{ headerShown: false }} />
<Tab.Screen name='Home' component={HomeStackScreen} options={{ headerShown: false }}/>
<Tab.Screen name='Cooking' component={CookingStackScreen} options={{ headerShown: false }}/>
</Tab.Navigator>
</NavigationContainer>
<ThemeProvider>
<NavigationContainer>
<Tab.Navigator initialRouteName='Home' tabBar={ (props) => <BottomBar {...props}/> }>
<Tab.Screen name='Profile' component={ProfilesStackScreen} options={{ headerShown: false }} />
<Tab.Screen name='Home' component={HomeStackScreen} options={{ headerShown: false }}/>
<Tab.Screen name='Cooking' component={CookingStackScreen} options={{ headerShown: false }}/>
</Tab.Navigator>
</NavigationContainer>
</ThemeProvider>
);
}

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { View, Text, TouchableOpacity, Image, Pressable } from 'react-native';
import { GestureResponderEvent, StyleSheet } from 'react-native';
import {Appearance } from 'react-native';
@ -10,44 +10,64 @@ import ProfileIcon from '../assets/images/person_icon.png';
import CookingIcon from '../assets/images/cook.png';
import LightIcon from '../assets/images/update.png';
import DarkIcon from '../assets/images/validate.png';
import ThemeContext from '../theme/ThemeContext';
export default function BottomBar({ state, descriptors, navigation }) {
const [colorScheme, setScheme] = useState<ColorSchemeName | string>(
Appearance.getColorScheme(),
);
const [iconThemeButton, setThemeIconButton] = useState(( colorScheme === 'dark' ) ? LightIcon : DarkIcon)
const [textThemeButton, setTextThemeButton] = useState(( colorScheme === 'dark' ) ? 'Light' : 'Dark');
useEffect(() => {
const subscription = Appearance.addChangeListener(
(preferences: AppearancePreferences) => {
const {colorScheme: scheme} = preferences;
setScheme(scheme);
},
);
return () => subscription?.remove();
}, [setScheme]);
const {theme, toggleTheme} = useContext(ThemeContext)
const [iconThemeButton, setThemeIconButton] = useState(( theme === 'dark' ) ? LightIcon : DarkIcon)
const [textThemeButton, setTextThemeButton] = useState(( theme === 'dark' ) ? 'Light' : 'Dark');
const onThemeButtonPress = (event: GestureResponderEvent) => {
if (textThemeButton === "Light") {
setThemeIconButton(ProfileIcon);
setThemeIconButton(DarkIcon);
setTextThemeButton("Dark");
//Appearance.setColorScheme('light')
toggleTheme('light');
} else {
setThemeIconButton(HomeIcon);
setThemeIconButton(LightIcon);
setTextThemeButton("Light");
//Appearance.setColorScheme('dark')
toggleTheme('dark')
}
console.log('TextThemeButton is now: ' + textThemeButton);
}
const styles = StyleSheet.create({
BottomBarMainContainer: {
position: 'absolute',
bottom: 0,
right: 0,
left: 0,
height: 70,
backgroundColor: theme === 'dark' ? "#3F3C42": "transparent"
},
BottomBarBlurContainer: {
flexDirection: 'row',
alignItems: 'center',
alignContent: 'space-around',
padding: 2,
borderBlockColor: theme === 'light' ? '#F2F0E4' : '#222222',
borderWidth: 3,
borderLeftColor: theme === 'light'? '#F2F0E4' : '#222222',
borderLeftWidth: 3,
borderRightColor: theme === 'light'? '#F2F0E4' : '#222222',
borderRightWidth: 3
},
BottomBarIcon: {
width: 35,
height: 35
},
BottomBarElementContainer: {
flexDirection: 'column',
alignItems: 'center',
margin: 3
}
})
return (
<View style={styles.BottomBarMainContainer}>
<BlurView
style={[StyleSheet.absoluteFill, styles.BottomBarBlurContainer]}
tint='dark'
intensity={50}
intensity={theme === 'light' ? 50 : 0}
>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
@ -90,7 +110,7 @@ export default function BottomBar({ state, descriptors, navigation }) {
onPress={onPress}
style={[styles.BottomBarElementContainer, { flex: 1 }]}
>
<Image source={icon} style={[styles.BottomBarIcon, {tintColor: isFocused ? '#59BDCD': '#F2F0E4'}]} />
<Image source={icon} style={[styles.BottomBarIcon, {tintColor: isFocused ? (theme === 'light' ? '#59BDCD': '#8DB4D9'): '#F2F0E4'}]} />
<Text style={{ color: isFocused ? '#59BDCD' : '#F2F0E4' }}>
{label}
</Text>
@ -106,35 +126,4 @@ export default function BottomBar({ state, descriptors, navigation }) {
</BlurView>
</View>
);
}
const styles = StyleSheet.create({
BottomBarMainContainer: {
position: 'absolute',
bottom: 0,
right: 0,
left: 0,
height: 70
},
BottomBarBlurContainer: {
flexDirection: 'row',
alignItems: 'center',
alignContent: 'space-around',
padding: 2,
borderBlockColor: '#F2F0E4',
borderWidth: 3,
borderLeftColor: '#F2F0E4',
borderLeftWidth: 3,
borderRightColor: '#F2F0E4',
borderRightWidth: 3
},
BottomBarIcon: {
width: 35,
height: 35
},
BottomBarElementContainer: {
flexDirection: 'column',
alignItems: 'center',
margin: 3
}
})
}

@ -9,6 +9,7 @@
"version": "1.0.0",
"dependencies": {
"@expo/webpack-config": "^19.0.0",
"@react-native-async-storage/async-storage": "^1.19.7",
"@react-navigation/bottom-tabs": "^6.5.11",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
@ -3942,6 +3943,17 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@react-native-async-storage/async-storage": {
"version": "1.19.7",
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.19.7.tgz",
"integrity": "sha512-zvfhOWWnjzeP4exfgNwDUot7MQafEEJnbjjNsVKWRxRuQT521GLSHUJQHjMTLg19QPT+HbsGjL7rJzKkmXuq7w==",
"dependencies": {
"merge-options": "^3.0.4"
},
"peerDependencies": {
"react-native": "^0.0.0-0 || >=0.60 <1.0"
}
},
"node_modules/@react-native-community/cli": {
"version": "11.3.7",
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.7.tgz",
@ -12269,6 +12281,25 @@
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
},
"node_modules/merge-options": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
"integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
"dependencies": {
"is-plain-obj": "^2.1.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/merge-options/node_modules/is-plain-obj": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"engines": {
"node": ">=8"
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@ -20455,6 +20486,14 @@
}
}
},
"@react-native-async-storage/async-storage": {
"version": "1.19.7",
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.19.7.tgz",
"integrity": "sha512-zvfhOWWnjzeP4exfgNwDUot7MQafEEJnbjjNsVKWRxRuQT521GLSHUJQHjMTLg19QPT+HbsGjL7rJzKkmXuq7w==",
"requires": {
"merge-options": "^3.0.4"
}
},
"@react-native-community/cli": {
"version": "11.3.7",
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.7.tgz",
@ -26667,6 +26706,21 @@
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
},
"merge-options": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
"integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
"requires": {
"is-plain-obj": "^2.1.0"
},
"dependencies": {
"is-plain-obj": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="
}
}
},
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",

@ -10,6 +10,7 @@
},
"dependencies": {
"@expo/webpack-config": "^19.0.0",
"@react-native-async-storage/async-storage": "^1.19.7",
"@react-navigation/bottom-tabs": "^6.5.11",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",

@ -0,0 +1,42 @@
import React, {createContext, useState, useEffect} from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
interface ThemeContextType {
theme: string,
toggleTheme: (any)=> void
};
const ThemeContext = createContext<ThemeContextType | null>(null);
export const ThemeProvider = ({children}) => {
const [theme, setTheme] = useState('light');
useEffect(() => {
// Load saved theme from storage
const getTheme = async () => {
try {
const savedTheme = await AsyncStorage.getItem('theme');
if (savedTheme) {
setTheme(savedTheme);
}
} catch (error) {
console.log('Error loading theme:', error);
}
};
getTheme();
}, []);
const toggleTheme = newTheme => {
setTheme(newTheme);
AsyncStorage.setItem('theme', newTheme)
};
return (
<ThemeContext.Provider value={{theme, toggleTheme}}>
{children}
</ThemeContext.Provider>
);
};
export default ThemeContext;
Loading…
Cancel
Save