Appearance.getColorScheme() returns 'light' or 'dark' (null without the native module), and addChangeListener() reports switches, including scheduled ones at sunset. In components, prefer the useColorScheme() hook, which re-renders when the scheme changes. Appearance.setColorScheme('dark') forces a scheme for the app and its native dialogs, and 'unspecified' follows the system again. React Native 0.87 36,878 renamed that value 'auto', which 0.86.3 (Expo SDK 57 6,418 ) silently ignores (Building a Dark Mode Toggle).
const scheme = useColorScheme(); // 'light' | 'dark', re-renders on change
const c = palette[scheme === 'dark' ? 'dark' : 'light'];
useEffect(() => {
const sub = Appearance.addChangeListener(({ colorScheme }) =>
console.log('colorScheme ->', colorScheme));
return () => sub.remove();
}, []);Here palette holds a light and a dark set of background, text and card colors. Switching the emulator with adb shell cmd uimode night yes, then no, logged the two changes and repainted the screen without a restart (left and middle screens of the figure in DevSettings):
LOG colorScheme -> dark LOG colorScheme -> light
The Android template's MainActivity declares uiMode in configChanges, so the activity is not recreated. Expo's userInterfaceStyle in app.json pins a scheme, but on Android it takes effect only with the expo-system-ui package installed; this project has none, so it followed the system despite "light".