Reputation: 103
I have an app with around 6 screens, and wanted to write a consistent theme manager for them. I feel like I am most of the way there, but I'm struggling with the last little bit. How I want to have it is that there is a ThemesScreen
containing a list of themes, and clicking on each one changes it dynamically, and I can see that clicking on the button does change the name, but I'm struggling with getting that to pass along.
Here's my ThemeManager.js
:
import React, { createContext, useState } from "react";
import { current } from "../Styles/themes";
export const ThemeContext = React.createContext();
export const ThemeProvider = ({ children }) =>{
//light, dark
const[theme, setTheme] = React.useState("light");
const toggleTheme = () =>{
if(theme === 'light'){
setTheme("dark");
}
else{
setTheme("light");
}
console.log(theme)
}
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
)
}
Here's my ThemesScreen.js
:
import React, { useContext } from 'react';
import { Text, ScrollView, StyleSheet, Alert } from 'react-native';
import { RowItem, RowSeparator } from '../Styles/RowItem';
import ThemeProvider, { ThemeContext } from '../utils/ThemeManager';
function ThemeScreen(){
const theme = useContext(ThemeContext)
return (
<ThemeContext.Consumer>
<ScrollView style={styles.row}>
<RowItem
title="Light theme"
onPress={toggleTheme}/>
<RowSeparator />
<RowItem
title="Dark Theme"
onPress={() => alert('Dark!')}/>
<RowSeparator />
<RowItem
title="Material Blue"
onPress={() => alert('Material blue!')}/>
</ScrollView>
</ThemeContext.Consumer>
)
}
const styles = StyleSheet.create({
row: {
backgroundColor: theme.background,
},
});
export default ThemeScreen
And here's my Themes.js
export const LightTheme = {
dark: false,
colors: {
primary: 'rgb(255, 45, 85)',
background: 'f5f5f5',
card: 'rgb(255, 255, 255)',
text: '#333333',
border: 'rgba(175, 47, 47, .75)',
notification: 'rgba(175, 47, 47, .35)',
}
}
export const DarkTheme = {
dark: true,
colors: {
primary: 'rgb(255, 45, 85)',
background: 'rgb(0, 0, 0)',
card: 'rgb(255, 255, 255)',
text: 'rgb(28, 28, 30)',
border: 'rgb(199, 199, 204)',
notification: 'rgb(255, 69, 58)',
},
};
const current = LightTheme
export { current }
My original idea is that I can just set current whatever the name of the currently selected theme is, and then do something like the below code, since I have a lot of different screens that would save me time.
import { current } from './styles/themes.js;
...
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: current.background,
paddingTop: 25,
},
But I'm not quite sure how to do that, or if that will work. I'd appreciate any and all advice! Also if there's a much better way to do this, please tell me.
Upvotes: 1
Views: 938
Reputation: 14375
You can move your styles
into the body of your components:
import { RowItem, RowSeparator } from '../Styles/RowItem';
import ThemeProvider, { ThemeContext } from '../utils/ThemeManager';
function ThemeScreen(){
const theme = useContext(ThemeContext)
const styles = StyleSheet.create({
row: {
backgroundColor: theme.background,
},
});
return (
<ThemeContext.Consumer>
<ScrollView style={styles.row}>
<RowItem
title="Light theme"
onPress={toggleTheme}/>
<RowSeparator />
<RowItem
title="Dark Theme"
onPress={() => alert('Dark!')}/>
<RowSeparator />
<RowItem
title="Material Blue"
onPress={() => alert('Material blue!')}/>
</ScrollView>
</ThemeContext.Consumer>
)
}
export default ThemeScreen
I would also suggest to memoize your toggler and context to improve performance.
For theming your react-native
application, have you considered using styled-components
?
We use it in our cross platform projects and add all you need to create themized application.
It will also split the UX logic out of your components, right now you need to manually import the theme from the context, and you have no API for a styled-component like the one you expect.
yarn add styled-components
Create a styled component:
const Container = styled.View(({ theme }) => ({
backgroundColor: theme.colors.white,
}))
Add ./theme.js
:
export const theme = {
colors: {
white: '#ffffff',
}
}
And use the ThemeProvider
from styled-components/native
your App.tsx
:
+ import { ThemeProvider } from 'styled-components/native'
+ import { theme } from './theme'
- <App />
+ <ThemeProvider theme={theme}><App /></ThemeProvider>
It will still be possible to access the theme from within your component using useTheme()
hook, but I advise you to try to keep the stylesheet logic separated at maximum.
Upvotes: 1