iOS 13 和 Android Q 都推出了深色模式,随着系统级别的支持,越来越多的主流应用也开始陆续退出深色模式。那么在 React Native 中如何实现主题功能呢?本文提供了一种思路,基于 React Hook 和 Context API。
添加主题配置文件
首先添加内置的主题配置文件,默认支持深色和浅色两种。
// 深色
const brandPrimary = '#de6454'
const white = '#fff'
const colors = {
// brand
brand_primary: brandPrimary,
// font
heading_color: 'rgba(255,255,255,0.85)', // 标题色
text_color: 'rgba(255,255,255,0.65)', // 主文本色
text_color_secondary: 'rgba(255,255,255,0.45)', // 次文本色
// navigation
header_tint_color: white,
header_text_color: white,
tabbar_bg: '#1d1e21',
header_bg: '#000102',
// background
page_bg: '#000102',
// button
btn_bg: brandPrimary,
btn_text: white,
}
export default {
colors,
}
我这里只配置了颜色,还可以配置图标、字体等。
创建 ThemeContext
首先创建一个对象保存主题配置信息,同时支持添加主题,这样可以做到下载主题配置文件并导入。
import defaultTheme from './default'
import dark from './dark'
const themes = {
default: defaultTheme,
dark,
}
export const addTheme = (key, value) => (themes[key] = value)
创建 ThemeContext 和 Provider,通过 Context 向子组件传递主题名称、主机配置以及 changeTheme 的函数。
export const ThemeContext = React.createContext()
export const ThemeContextProvider = ({ children }) => {
const [theme, changeTheme] = useState('default')
return (
<ThemeContext.Provider
value={{ theme: themes[theme], themeName: theme, changeTheme }}>
{children}
</ThemeContext.Provider>
)
}
添加 Provider
在 index.js
中将根组件放到 ThemeContextProvider
下。
function setup() {
// TODO: 全局的初始化操作,例如初始化 SDK
const Root = () => (
<ThemeContextProvider>
<App />
</ThemeContextProvider>
)
return Root
}
组件中使用
在组件内部通过 useContext
获取 Context 中的主题配置信息,在编写样式的时候使用主题内部配置的变量。如果想要切换主题,可以通过 Context 的 changeTheme
函数。
const Home = props => {
const { navigation } = props
const { themeName, theme, changeTheme } = useContext(ThemeContext)
useStatusBar(navigation, 'light-content')
const onSwitchTheme = () => {
if (themeName === 'default') {
changeTheme('dark')
} else {
changeTheme('default')
}
}
return (
<View style={styles.container}>
<Text style={[styles.title, { color: theme.colors.heading_color }]}>
Happy Chinese New Year!
</Text>
<Button
onPress={() => navigation.push('FullScreenBg')}
style={{ marginVertical: 24 }}>
Full screen background
</Button>
<Button onPress={onSwitchTheme}>
{themeName === 'default' ? 'Dark mode' : 'Light mode'}
</Button>
</View>
)
}
```![theme-1.gif](https://upload-images.jianshu.io/upload_images/9619819-c920bc1bb13c9eaa.gif?imageMogr2/auto-orient/strip)