Home > Software engineering >  How can I toggle a dark theme for a stack of screens?
How can I toggle a dark theme for a stack of screens?

Time:03-18

I have included a snack expo here that reproduces the exact layout of my project. App.js -> stack of screens. I am trying to figure out the best way to toggle between light theme and dark theme. I do not care for the ios dark theme setting to trigger this, for now, I just want the button to trigger it.

I would like there to be a button displayed on my home screen that when pushed toggles to the theme it is not currently on. I originally wanted to use theme provider to set my colors for each theme and then call that color on each page rather than the hard coded color but have failed to do so.

When you have a stack of screens being called in a tab navigator, what is the best way to use a dark theme?

CodePudding user response:

You need to use the theme prop of the NavigationContainer but your are still responsible for providing the styling for each of your components.

That said, there are a couple of things we need to do here.

  1. Let the button change the theme prop of the NavigationContainer.
  2. Provide a custom styling (colors etc.) for all of your components depending of the theme.

We can solve the first problem by wrapping the NavigationContainer into a ContextProvider as follows.

import React, {useState} from 'react';
import {
  DefaultTheme,
  DarkTheme,
  NavigationContainer,
} from '@react-navigation/native';

export const ThemeContext = React.createContext();

export default function App() {
  const [theme, setTheme] = useState(DefaultTheme)
  
  return (
    <ThemeContext.Provider value={{theme, setTheme}}>
      <NavigationContainer theme={theme}>

      </NavigationContainer>
    </ThemeContext.Provider>
  );
}

In your Toggle component you can then change the theme dynamically as follows.

import {ThemeContext} from '../App'

import {
  DefaultTheme,
  DarkTheme,
} from '@react-navigation/native';

const Toggle = (props) => {
    const { setTheme, theme } = React.useContext(ThemeContext);

    return (
      <TouchableOpacity onPress={() => setTheme(theme === DefaultTheme ? DarkTheme : DefaultTheme)}>
        
      </TouchableOpacity>
    );
  }

You will notice that changing the theme will not change anything in your current setup. This is because you need to take care of this yourself.

This could be done using either the context again or the useTheme hook. For your HomeScreen this could be done as follows.

import { useTheme } from '@react-navigation/native';

const Home = () => {

    const { colors } = useTheme();

    return (
      <View style={{justifyContent: 'center', alignItems: 'center', flex: 1, backgroundColor: colors.background}}>
        <Toggle/>
        <Screen/>
      </View>
    );
  }

Toggling the button changes the theme now. You can access the default theme colors using the useTheme hook but you still need to apply it to your components. You could also provide a custom theme which is done similar.

CodePudding user response:

I would suggest doing something similar to this: https://www.section.io/engineering-education/how-to-control-dark-mode-in-react-native-using-redux/.

In the article the author uses React Native Paper to control what each of his screens look like. I personally have a theme.js file that has an if statement based on what my Redux state is set to. If it is to light mode, my global theme variables (e.g. background color, text color, etc.) are set to their respective light colors. When set to dark mode, my global theme variables are set to their respective dark colors.

  • Related