javascriptreact-nativereact-hooksandroid-dark-themereact-class-based-component

React-native dark theme in class based project


i am having trouble adding a dark-theme support to my react-native app.

theme-context.js

import React from 'react';

const themes = {
    dark: {
        backgroundColor: 'gray',
        backgroundCard: '#25282c',
        color: 'white'
    },
    light: {
        backgroundColor: 'yellow',
        backgroundCard: '#fff',
        color: 'black'
    }
}

const initialState = {
    dark: false,
    theme: themes.light,
    toggle: () => {}
}

const ThemeContext = React.createContext(initialState)

function ThemeProvider({children}) {
    const [dark, setDark] = React.useState(false) // Default theme is light

    // Toggle between dark and light modes
    const toggle = () => {
        setDark(!dark)
    }

    // Filter the styles based on the theme selected
    const theme = dark ? themes.dark : themes.light

    return(
        <ThemeContext.Provider value={{theme, dark, toggle}}>
            {children}
        </ThemeContext.Provider>
    )
}

export {ThemeProvider, ThemeContext}

App.js


 import 'react-native-gesture-handler';
 import React, { Component } from 'react';
 import {LogBox, SafeAreaView, StyleSheet, ScrollView, Button, View, Text, Image, Alert, TouchableOpacity, TouchableHighlight} from 'react-native';
 import { NavigationContainer } from '@react-navigation/native';
 import { createStackNavigator } from '@react-navigation/stack';
 import Home from './Home.js';
 import Settings from './Settings.js';
 import Icon from 'react-native-vector-icons/FontAwesome';
 import { ThemeProvider } from './theme-context'
 
 
 const Stack = createStackNavigator();
 
 function LogoTitle() {
   return (
     <Image
       style={{ width: 140, height: 50 }}
       source={require('./images/title.png')}
     />
   );
 }
 
 // ------------------App-----------------------------
 class App extends Component {
 
   render() {
     
     return (
    <ThemeProvider>
         <NavigationContainer>
           <Stack.Navigator
           screenOptions={{
             headerStyle: {
               backgroundColor: '#f4f4f4',
             },
             headerTintColor: 'gray',
             headerTitleStyle: {
               fontWeight: 'bold',
             },
           }}
         >
           <Stack.Screen
               name="Home"
               component={Home}
               options={({ navigation, route }) => ({
                 headerTitle: props => <LogoTitle {...props} />,
                 headerRight: () => (
                   <Icon.Button
                     name="cog"
                     backgroundColor="transparent"
                     size={25}
                     onPress={() => navigation.navigate('Settings')}
                     title="Settings"
                     color="gray"
                     />
                 ),
             })}
             />
             <Stack.Screen
               name="Settings"
               component={Settings}
             />
           </Stack.Navigator>
         </NavigationContainer>
    </ThemeProvider>
     );
   }
 }
 export default App;

home.js

 import 'react-native-gesture-handler';
 import React, { Component, useContext } from 'react';
 import {LogBox, SafeAreaView, StyleSheet, ScrollView, View} from 'react-native';
 import { Card } from 'react-native-elements';
 import CardOne from './Components/CardOne.js';
 import CardTwo from './Components/CardTwo.js';
 import CardThree from './Components/CardThree.js';
 import CardFour from './Components/CardFour.js';
 import { ThemeProvider } from './theme-context';

 
 
 // ------------------App-----------------------------
 class Home extends Component {
 
   constructor(props) {
       super(props);
       // ...
       }
   }
 
   render() {
     return (
      <ThemeProvider>
         <View>
           <SafeAreaView>
             <ScrollView >
                <CardOne/>
               <CardTwo/> 
               <CardThree/> 
               <CardFour /> 
             </ScrollView>
           </SafeAreaView>
         </View>
      </ThemeProvider>
     );
   }
 }
 
 
 export default Home;

next i want to useContext on one of my components for example

import React, { Component, useContext} from 'react';
import { Card } from 'react-native-elements';
import {StyleSheet, View, Text, Image, Alert, TouchableOpacity} from 'react-native';
import { ThemeContext } from 'ResCalc/theme-context';

const { dark, theme, toggle } = useContext(ThemeContext);

class CardOne extends Component {

    constructor(props) {
        super(props);
        ...
      }
    }


    render() {
        return (
            <Card containerStyle={styles.card}>
                <View>
                <Switch
                    onChange={toggle} value = {dark} />
                </View>
            </Card>
        );
    }
}


const styles = StyleSheet.create({
    card: {
        backgroundColor: theme.backgroundColor,
        borderColor: "#D1D1D6",
        borderWidth: 2, 
        borderRadius: 5,
        shadowOffset:{  width: 5,  height: 5, },
        shadowColor: '#D1D1D6',    // #D1D1D6
        shadowOpacity: 1.0,
        shadowRadius: 2,
        elevation: 3,
        
    }
  });

export default CardOne

i guess i have to use the useContext hook in a class based variant, but i just can't figure out how to do that.

hopefully someone can help me out on this :)


Solution

  • You can define your styles as a function and then inject your theme into it.

    const styles = (theme) => StyleSheet.create({
        card: {
            backgroundColor: theme.backgroundColor,
            borderColor: "#D1D1D6",
            borderWidth: 2, 
            borderRadius: 5,
            shadowOffset:{  width: 5,  height: 5, },
            shadowColor: '#D1D1D6',    // #D1D1D6
            shadowOpacity: 1.0,
            shadowRadius: 2,
            elevation: 3,
            
        }
      });
    

    In your component:

    class CardOne extends Component {
      static contextType = ThemeContext;
    
      render() {
         // Pass theme context to styles functiion
         return (
                <Card containerStyle={styles(this.context.theme).card}>
                    <View>
                    <Switch
                        onChange={toggle} value = {dark} />
                    </View>
                </Card>
            );
      }