majid lotfinia
majid lotfinia
خواندن ۸ دقیقه·۴ سال پیش

تم ها مختلف در ری اکت نیتیو - React Native Multi Theme

در این نوشته قصد داریم در مورد نحوه ایجاد تم های مختلف مثل light و dark در ری اکت نیتیو به صورت عملی صبحت کنیم. همونطور که میدونید هر دو سیستم عامل Android و IOS قابلیت تغییر تم بین حالت دارک و لایت رو دارا هستن، در نتیجه کاربران معمولا انتظار دارن که این قابلیت از سمت اپلیکیشن هایی که ما توسعه میدیم وجود داشته باشه. برای افزودن این قابلیت بهتره که تا آخر این مقاله با من همراه باشید.

خلاصه کاری که قراره انجام بدیم رو میتونید تو تصویر پایین ببیند و به کد اون تو این ریپازیتوری گیت هاب دسترسی داشته باشید.

خروجی نهایی
خروجی نهایی


پیش نیاز ها

اول از همه یه پروژه ری اکت نیتیو با دستور زیر ایجاد میکنیم:

npx react-native init rn_theming

گام بعدی پکیج react-navigation رو از طریق دستورالعمل های خودش نصب کنید. برای ذخیره حالت دارک یا لایت برای استفاده های بعدی اپلیکیشن هم از پکیج react-native-community/async-storage استفاده میکنیم

ساختار پروژه

تو این مرحله ساختار پروژه رو عوض میکنیم. من خودم معمولا از چنین ساختاری تو پروژه هام استفاده میکنم:

-src - components برای قرار دادن کامپوننت های پروژه - TabBar.js - StatusBar.js - navigation - index.js برای تعریف کردن صفحات - screens صفحات مختلف پروژه - Home.js صفحه اصلی - Setting.js صفحه تنظیمات - providers - themeProvider.js برای هوک های مربوط به تغییر تم - theme.json نگه داری مقادیر تم های مختلف App.js index.js فایل اصلی و اولیه پروژه

بعد از اینکه ساختار رو عوض کنیم یه ادیتی تو فایل index.js میزنیم و ایمپورت فایل App رو تو اون عوض میکنیم:

import App from './App'; to ----> import App from './src/App';

بخش Navigation

برای Navigation پروژه یه bottom tab navigator ایجاد میکنیم که داخل اون دو تب یکی برای home و دیگری برای setting باشه. tabBar مربوط به اون رو هم از کامپوننت TabBar که خودمون تو کامپوننت ها ساختیم استفاده میکنیم.

import React from 'react'; import {NavigationContainer} from '@react-navigation/native'; import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'; import HomeScreen from './../screens/Home'; import SettingScreen from './../screens/Setting'; import MyTabBar from './../components/TabBar' const Tab = createBottomTabNavigator(); export default function MainNavigation() { return ( <NavigationContainer> <Tab.Navigator tabBar={props => <MyTabBar {...props} />}> <Tab.Screen name=&quotHome&quot component={HomeScreen} /> <Tab.Screen name=&quotSettings&quot component={SettingScreen} /> </Tab.Navigator> </NavigationContainer> ); }

تم های مختلف پروژه

اول رنگای مختلفی که میخایم تو مد دارک و لایت استفاده کنیم رو با کلیدهای دلخواه به فرمت جیسون در میاریم و تو فایل themes.json میریزیم:

{

{ &quotlight&quot: {
&quotprimaryText&quot: &quot#0b132b&quot,
&quotbackgroundColor&quot: &quot#eff7f6&quot,
&quottabBarBackgroundColor&quot: &quot#e6ebe0&quot,
&quottabBarActiveColor&quot: &quot#02c39a&quot
},
&quotdark&quot: {
&quotprimaryText&quot: &quot#98c1d9&quot,
&quotbackgroundColor&quot: &quot#14213d&quot,
&quottabBarBackgroundColor&quot: &quot#2b2d42&quot,
&quottabBarActiveColor&quot: &quot#fcbf49&quot
}
}

هوک های مربوط به تغییر تم

حالا میریم سراغ فایل themeProvider که لاجیک اصلی تغییر تم رو تو اون قرار بدیم:

در ابتدا پکیج ها و کامپوننت های لازم رو ایمپورت میکنیم و متغیرهای ثابت مورد نیاز برای کل فایل رو تعریف میکنیم:

providers/themeProvider.js import React, { useContext, useState, useEffect } from 'react';
import AsyncStorage from '@react-native-community/async-storage';
import THEMES from './themes.json';
const STORAGE_KEY = 'THEME_ID'; کلید مربوطه به ذخیره وضعیت تم در حافظه برای استفاده های بعدی
const ThemeContext = React.createContext();

در ادامه فایل themeProvider یک context به نام ThemeContextProvider میسازیم و یک state به نام themeID تو اون تعریف می کنیم که این state رو در بقیه کامپوننت ها به اشتراک بزاره.

providers/themeProvider.js export const ThemeContextProvider = ({ children }) => {
const [themeID, setThemeID] = useState();
useEffect(() => {
(async () => {
const storedThemeID = await AsyncStorage.getItem(STORAGE_KEY);
if (storedThemeID) {
setThemeID(storedThemeID);
} else {
setThemeID('light');
}
})();
}, []);
return (
<ThemeContext.Provider value={{ themeID, setThemeID }}>
{!!themeID ? children : null}
</ThemeContext.Provider>
);
};

حالا برای استفاده از این context باید فایل App.js را به شکل زیر ویرایش کنیم:

App.js import React from 'react';
import {View, Text} from 'react-native';
import MainNavigation from './navigation'
import { ThemeContextProvider } from './providers/themeProvider';
function App() {
return (
<ThemeContextProvider>
<MainNavigation />
</ThemeContextProvider>
);
}
export default App;

تو این فایل کل navigation اپلیکیشنمون رو درون ThemeContextProvider قرار میدیم. دیگه حالا تو همه کامپوننت هامون به themeID و setThemeID دسترسی داریم و میتونیم مقادیر themeID رو استفاده یا عوض کنیم.

هنوز کارمون تموم نشده. بیاید با توجه به مفهموم HOC یه تغیری دیگه تو فایل themeProvider بدیم. در انتهای این فایل یه تابع دیگه به اسم withTheme اضافه میکنیم. کار این تابع اینه که یه Component رندر میکنه که مقادیر تم فعلی مثه لایت یا دارک رو در اختیار هر کامپوننتی که ازش استفاده کنه قرار میده

export function withTheme(Component) {
return props => {
function changeTheme (id) {emeID } = useContext(ThemeContext);
function changeTheme (id) { // تابعی که تم رو تغییر میده براساس اون چیزی که از ورودی میگیره
AsyncStorage.setItem(STORAGE_KEY, id);
setThemeID(id)
}
const getTheme = themeID => THEMES[themeID]; // تابعی که تم فعلی رو برمیگردونه
const switchTheme = () => { // تابعی که تم رو سوئیچ میکنه بین لایت و دارک
const newTheme = themeID === 'light' ? 'dark' : 'light';
changeTheme(newTheme)
};
const setTheme = (key) => { تابعی برای تغییر تم
changeTheme(newTheme))
};
return (
<Component
{...props}
themes={THEMES}
theme={getTheme(themeID)}
themeID={themeID}
setTheme={setTheme}
switchTheme={switchTheme} این بخش توابعی و متغیرهای مورد نیاز برای مدیریت تم رو در اختیار کامپوننت های قرار میده که از withTheme استفاده می کنند
/>
);
};
}


تعریف کامپوننت ها

حالا که کارمون با themeContext تموم شد کامپوننت های مختلف رو به شکل زیر تعریف می کنیم:

components -> TabBar.js import React from 'react';
import {BottomTabBar} from '@react-navigation/bottom-tabs';
import { withTheme } from './../providers/themeProvider';
const MyTabBar = props => {
return (
<BottomTabBar
{...props}
activeTintColor={props.theme.tabBarActiveColor}
inactiveTintColor={props.theme.primaryText}
style={{backgroundColor: props.theme.tabBarBackgroundColor, fontWeight: 'bold'}}
/>
);
};
export default withTheme(MyTabBar); components -> StatusBar.js import React from 'react';
import {StatusBar} from 'react-native';
import { withTheme } from './../providers/themeProvider';
function MyStatusBar({themeID}) {
const oppositeTheme = themeID === 'light' ? 'dark' : 'light'
return (
<StatusBar barStyle ={oppositeTheme + '-content'} hidden = {false} backgroundColor = &quot#00BCD4&quot translucent = {true}/>
);
};
export default withTheme(MyStatusBar);
screens -> Home.js import * as React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import { withTheme } from './../providers/themeProvider';
import MyStatusBar from './../components/StatusBar';
function HomeScreen({theme}) {
return (
<View style={{...styles.container, backgroundColor: theme.backgroundColor}}>
<MyStatusBar />
<Text style={{...styles.text, color: theme.primaryText}}>Welcome to change React Native Theme Changer Sample APP</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1, justifyContent: 'center', alignItems: 'center'
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center'
}
})
export default withTheme(HomeScreen);

صفحه setting که عملیات تغییر تم تو اون اتفاق میفته هم به شکل زیر خواهد بود:

screens -> Setting.js import * as React from 'react';
import {Text, View, StyleSheet, TouchableOpacity} from 'react-native';
import { withTheme } from './../providers/themeProvider';
import MyStatusBar from './../components/StatusBar';
function SettingsScreen({theme, setTheme, switchTheme, themeID}) {
return (
<View style={{...styles.container, backgroundColor: theme.backgroundColor}}>
<MyStatusBar />
<TouchableOpacity style={styles.button} onPress={() => switchTheme()}>
<Text style={{...styles.text, color: theme.primaryText}}>Switch Theme</Text>
</TouchableOpacity>
{themeID === 'light' && <TouchableOpacity style={styles.button} onPress={() => setTheme('dark')}>
<Text style={{...styles.text, color: theme.primaryText}}>Change To Dark</Text>
</TouchableOpacity> }
{themeID === 'dark' && <TouchableOpacity style={styles.button} onPress={() => setTheme('light')}>
<Text style={{...styles.text, color: theme.primaryText}}>Change to light</Text>
</TouchableOpacity>}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
button: {
borderWidth: 1,
borderRadius: 8,
borderColor: 'grey',
padding: 10,
margin: 20
},
text: {
fontSize: 20,
fontWeight: 'bold'
}
})
export default withTheme(SettingsScreen);


جمع بندی

در این مقاله اومدیم یه نمونه پروژه از نحوه ایجاد تم های مختلف مثل dark و light رو در React Native پیاده سازی کردیم. می تونید سورس کد پروژه رو اینجا ببینید.




reactnativecontexttheme
شاید از این پست‌ها خوشتان بیاید