React Native 动态切换主题

React Native 动态切换主题

创建主题配置和上下文

复制代码
				// ThemeContext.js
			import React, { Component, createContext } from 'react';
			import { Appearance, AsyncStorage } from 'react-native';
			
			// 主题配置
			const themes = {
			  light: {
			    mode: 'light',
			    primary: '#3498db',
			    secondary: '#f39c12',
			    background: '#ffffff',
			    cardBackground: '#f8f9fa',
			    text: '#333333',
			    textSecondary: '#666666',
			    border: '#e0e0e0',
			  },
			  dark: {
			    mode: 'dark',
			    primary: '#2ecc71',
			    secondary: '#e74c3c',
			    background: '#121212',
			    cardBackground: '#1e1e1e',
			    text: '#ffffff',
			    textSecondary: '#bbbbbb',
			    border: '#333333',
			  },
			  blue: {
			    mode: 'blue',
			    primary: '#2980b9',
			    secondary: '#3498db',
			    background: '#ecf0f1',
			    cardBackground: '#bdc3c7',
			    text: '#2c3e50',
			    textSecondary: '#7f8c8d',
			    border: '#95a5a6',
			  }
			};
			
			export const ThemeContext = createContext();
			
			export class ThemeProvider extends Component {
			  state = {
			    theme: themes.light // 默认主题
			  };
			
			  async componentDidMount() {
			    // 尝试加载保存的主题
			    try {
			      const savedTheme = await AsyncStorage.getItem('userTheme');
			      if (savedTheme && themes[savedTheme]) {
			        this.setState({ theme: themes[savedTheme] });
			      } else {
			        // 没有保存的主题则使用系统主题
			        const systemTheme = Appearance.getColorScheme() === 'dark' ? themes.dark : themes.light;
			        this.setState({ theme: systemTheme });
			      }
			    } catch (error) {
			      console.error('加载主题失败:', error);
			    }
			
			    // 监听系统主题变化
			    this.appearanceListener = Appearance.addChangeListener(({ colorScheme }) => {
			      if (!this.state.userSelectedTheme) { // 如果用户没有手动选择主题
			        this.setState({
			          theme: colorScheme === 'dark' ? themes.dark : themes.light
			        });
			      }
			    });
			  }
			
			  componentWillUnmount() {
			    if (this.appearanceListener) {
			      this.appearanceListener.remove();
			    }
			  }
			
			  toggleTheme = () => {
			    const newTheme = this.state.theme.mode === 'light' ? themes.dark : themes.light;
			    this.setTheme(newTheme.mode);
			  };
			
			  setTheme = async (themeName) => {
			    if (themes[themeName]) {
			      this.setState({ 
			        theme: themes[themeName],
			        userSelectedTheme: true 
			      });
			      try {
			        await AsyncStorage.setItem('userTheme', themeName);
			      } catch (error) {
			        console.error('保存主题失败:', error);
			      }
			    }
			  };
			
			  render() {
			    return (
			      <ThemeContext.Provider
			        value={{
			          theme: this.state.theme,
			          toggleTheme: this.toggleTheme,
			          setTheme: this.setTheme,
			          themes: Object.keys(themes) // 所有可用主题列表
			        }}
			      >
			        {this.props.children}
			      </ThemeContext.Provider>
			    );
			  }
			}

创建主题化高阶组件

复制代码
// withTheme.js
import React from 'react';
import { ThemeContext } from './ThemeContext';

export function withTheme(Component) {
  return function ThemedComponent(props) {
    return (
      <ThemeContext.Consumer>
        {context => <Component {...props} {...context} />}
      </ThemeContext.Consumer>
    );
  };
}

主应用组件

复制代码
// App.js
import React from 'react';
import { ThemeProvider } from './ThemeContext';
import MainScreen from './MainScreen';

export default class App extends React.Component {
  render() {
    return (
      <ThemeProvider>
        <MainScreen />
      </ThemeProvider>
    );
  }
}

主屏幕组件(类组件形式)

复制代码
// MainScreen.js
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { withTheme } from './withTheme';

class MainScreen extends React.Component {
  render() {
    const { theme, toggleTheme, setTheme, themes } = this.props;
    
    return (
      <View style={[styles.container, { backgroundColor: theme.background }]}>
        <Text style={[styles.title, { color: theme.text }]}>当前主题: {theme.mode}</Text>
        
        <TouchableOpacity 
          style={[styles.button, { backgroundColor: theme.primary }]}
          onPress={toggleTheme}
        >
          <Text style={styles.buttonText}>切换主题</Text>
        </TouchableOpacity>
        
        <View style={styles.themeOptions}>
          {themes.map(themeName => (
            <TouchableOpacity 
              key={themeName}
              style={[
                styles.themeButton, 
                { 
                  backgroundColor: themes[themeName].primary,
                  marginBottom: 10
                }
              ]}
              onPress={() => setTheme(themeName)}
            >
              <Text style={styles.buttonText}>{themeName}</Text>
            </TouchableOpacity>
          ))}
        </View>
        
        <View style={[styles.card, { backgroundColor: theme.cardBackground }]}>
          <Text style={{ color: theme.text }}>这是一个卡片示例</Text>
          <Text style={{ color: theme.textSecondary }}>次要文本颜色</Text>
          <View style={[styles.divider, { backgroundColor: theme.border }]} />
          <Text style={{ color: theme.text }}>边框颜色示例</Text>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    fontSize: 24,
    marginBottom: 30,
    fontWeight: 'bold',
  },
  button: {
    padding: 15,
    borderRadius: 8,
    marginBottom: 20,
    width: 200,
    alignItems: 'center',
  },
  themeButton: {
    padding: 12,
    borderRadius: 6,
    marginHorizontal: 5,
    width: 100,
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
  },
  themeOptions: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'center',
    marginTop: 20,
    marginBottom: 30,
  },
  card: {
    width: '90%',
    padding: 20,
    borderRadius: 10,
    marginTop: 20,
  },
  divider: {
    height: 1,
    width: '100%',
    marginVertical: 15,
  }
});

export default withTheme(MainScreen);
相关推荐
七灵微9 分钟前
ES6入门---第二单元 模块二:关于数组新增
前端·javascript·es6
娃哈哈哈哈呀20 分钟前
组件通信-mitt
前端·javascript·vue.js
纪元A梦1 小时前
华为OD机试真题——告警抑制(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
java·c语言·javascript·c++·python·华为od
ApiHug1 小时前
ApiHug SDK 1.3.5 Vue 框架 - 预览版
前端·javascript·vue.js·spring·vue·apihug·apismart
旺代2 小时前
React Router
前端·javascript·react.js
进取星辰2 小时前
18、状态库:中央魔法仓库——React 19 Zustand集成
前端·react.js·前端框架
小杨升级打怪中3 小时前
前端面经-VUE3篇(二)--vue3组件知识(二)依赖注入、异步组件、生命周期、组合式函数、插件
开发语言·前端·javascript
羊啊羊373 小时前
Vue 3 事件总线详解:构建组件间高效通信的桥梁
前端·javascript·vue.js
MYmayue3 小时前
vue3封装全局方法
前端·javascript·vue.js
GISer_Jing3 小时前
前端面试常问问题[From CV]
前端·javascript·面试