React-navigation switch theme toggle
Asked Answered
S

1

6

i've implemented theming support into my app with react-navigation, as you can see below. i am using the system theme settings, and my app follows this rule this is working great, but there's one thing left on my to-do list, a option to toggle light/dark theme inside my app, keep this selection, and store it into the user defaults or something like that..

i followed the official docs (https://reactnavigation.org/docs/themes/) but they don't mention how to switch themes manually. in my testing i always got a message that the theme prop is read only and cannot be changed manually. so how to do that? any help'd be greatly appreciated ;)

App.js

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { AppearanceProvider, useColorScheme, useTheme } from 'react-native-appearance';

const Stack = createStackNavigator();

// ------------------App-----------------------------
export default function App() {

    const scheme = useColorScheme();

    const MyDarkTheme = {
    dark: true,
    ...},
    const LightTheme = {
    dark: false,
    ...}
    
    return (
      <AppearanceProvider>
        <NavigationContainer theme={scheme === "dark" ? MyDarkTheme : LightTheme}>
          <Stack.Navigator>
          <Stack.Screen>
            name="home"
          ...
          <Stack.Screen
            name="Settings"
            component={Settings}
            />
          </Stack.Navigator>
        </NavigationContainer>
      </AppearanceProvider>
    );
  }

in my Components:

import React, { useState, useEffect} from 'react';
import { Card } from 'react-native-elements';
import { useTheme} from '@react-navigation/native';

function CardOne(props) {

    const { colors } = useTheme(); // works
    const theme = useTheme();

return (
        <Card containerStyle={{backgroundColor: colors.cardBackgroundColor, borderColor: colors.borderColor, borderWidth: 2, borderRadius: 5}}>
        ...
        </Card>
        );
}  
export default CardOne;

i really someone can help me out ;)

Selfrespect answered 28/6, 2021 at 9:49 Comment(2)
Are you using context or any type of state management to store the current theme ?Wryneck
no not yet, i am not sure how to do that..i just converted my classes to functions, i remember that i used "Context" before, because i had to and could not use the "useTheme" hook,..i am really curious about your approach (y)Selfrespect
W
12

You can use the Context and do something like below, basically maintain the theme in state at App.js and update value via context.

export const ThemeContext = React.createContext();

export default function App() {
  const [theme, setTheme] = useState('Light');

  const themeData = { theme, setTheme };
  return (
    <ThemeContext.Provider value={themeData}>
      <NavigationContainer theme={theme == 'Light' ? DefaultTheme : DarkTheme}>
        <Drawer.Navigator initialRouteName="Root">
          <Drawer.Screen name="Home" component={HomeScreen} />
          <Drawer.Screen name="Root" component={Root} />
        </Drawer.Navigator>
      </NavigationContainer>
    </ThemeContext.Provider>
  );
}

You can switch the theme from a screen like below

function ProfileScreen({ navigation }) {
  const { setTheme, theme } = React.useContext(ThemeContext);
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Profile Screen</Text>
      <Button
        title="Switch Theme"
        onPress={() => setTheme(theme === 'Light' ? 'Dark' : 'Light')}
      />
    </View>
  );
}

Sample code https://snack.expo.io/@guruparan/5b84d0

Wryneck answered 28/6, 2021 at 11:22 Comment(2)
that's one awesome approach, i am starting to understand, but i am stuck on the settings page where 'Context._context' evaluating fails when i try to use the context from the app.js file.. - 'undefinied is not an object (evaluating 'Context._context)Selfrespect
i found a solution :) i moved the ThemeContext creation to its own file and imported this file in the components files...works like a charm - thank you very much!Selfrespect

© 2022 - 2024 — McMap. All rights reserved.