235 lines
5.8 KiB
Markdown
235 lines
5.8 KiB
Markdown
# Theme System Documentation
|
|
|
|
## Tổng quan
|
|
|
|
Hệ thống theme đã được cấu hình để hỗ trợ Light Mode, Dark Mode và System Mode (tự động theo hệ thống). Theme được lưu trữ trong AsyncStorage và sẽ được khôi phục khi khởi động lại ứng dụng.
|
|
|
|
## Cấu trúc Theme
|
|
|
|
### 1. Colors Configuration (`constants/theme.ts`)
|
|
|
|
```typescript
|
|
export const Colors = {
|
|
light: {
|
|
text: "#11181C",
|
|
textSecondary: "#687076",
|
|
background: "#fff",
|
|
backgroundSecondary: "#f5f5f5",
|
|
surface: "#ffffff",
|
|
surfaceSecondary: "#f8f9fa",
|
|
primary: "#007AFF",
|
|
secondary: "#5AC8FA",
|
|
success: "#34C759",
|
|
warning: "#FF9500",
|
|
error: "#FF3B30",
|
|
// ... more colors
|
|
},
|
|
dark: {
|
|
text: "#ECEDEE",
|
|
textSecondary: "#8E8E93",
|
|
background: "#000000",
|
|
backgroundSecondary: "#1C1C1E",
|
|
surface: "#1C1C1E",
|
|
surfaceSecondary: "#2C2C2E",
|
|
primary: "#0A84FF",
|
|
secondary: "#64D2FF",
|
|
success: "#30D158",
|
|
warning: "#FF9F0A",
|
|
error: "#FF453A",
|
|
// ... more colors
|
|
},
|
|
};
|
|
```
|
|
|
|
### 2. Theme Context (`hooks/use-theme-context.tsx`)
|
|
|
|
Cung cấp theme state và functions cho toàn bộ app:
|
|
|
|
```typescript
|
|
interface ThemeContextType {
|
|
themeMode: ThemeMode; // 'light' | 'dark' | 'system'
|
|
colorScheme: ColorScheme; // 'light' | 'dark'
|
|
colors: typeof Colors.light;
|
|
setThemeMode: (mode: ThemeMode) => Promise<void>;
|
|
getColor: (colorName: ColorName) => string;
|
|
}
|
|
```
|
|
|
|
## Cách sử dụng Theme
|
|
|
|
### 1. Sử dụng Themed Components
|
|
|
|
```tsx
|
|
import { ThemedText } from "@/components/themed-text";
|
|
import { ThemedView } from "@/components/themed-view";
|
|
|
|
function MyComponent() {
|
|
return (
|
|
<ThemedView>
|
|
<ThemedText type="title">Title Text</ThemedText>
|
|
<ThemedText type="default">Regular Text</ThemedText>
|
|
</ThemedView>
|
|
);
|
|
}
|
|
```
|
|
|
|
### 2. Sử dụng Theme Hook
|
|
|
|
```tsx
|
|
import { useThemeContext } from "@/hooks/use-theme-context";
|
|
|
|
function MyComponent() {
|
|
const { colors, colorScheme, setThemeMode } = useThemeContext();
|
|
|
|
return (
|
|
<View style={{ backgroundColor: colors.background }}>
|
|
<Text style={{ color: colors.text }}>Current theme: {colorScheme}</Text>
|
|
</View>
|
|
);
|
|
}
|
|
```
|
|
|
|
### 3. Sử dụng App Theme Hook (Recommended)
|
|
|
|
```tsx
|
|
import { useAppTheme } from "@/hooks/use-app-theme";
|
|
|
|
function MyComponent() {
|
|
const { colors, styles, utils } = useAppTheme();
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<TouchableOpacity style={styles.primaryButton}>
|
|
<Text style={styles.primaryButtonText}>Button</Text>
|
|
</TouchableOpacity>
|
|
|
|
<View
|
|
style={[
|
|
styles.surface,
|
|
{
|
|
backgroundColor: utils.getOpacityColor("primary", 0.1),
|
|
},
|
|
]}
|
|
>
|
|
<Text style={{ color: colors.text }}>
|
|
Theme is {utils.isDark ? "Dark" : "Light"}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
```
|
|
|
|
### 4. Sử dụng useThemeColor Hook
|
|
|
|
```tsx
|
|
import { useThemeColor } from "@/hooks/use-theme-color";
|
|
|
|
function MyComponent() {
|
|
// Override colors for specific themes
|
|
const backgroundColor = useThemeColor(
|
|
{ light: "#ffffff", dark: "#1C1C1E" },
|
|
"surface"
|
|
);
|
|
|
|
const textColor = useThemeColor({}, "text");
|
|
|
|
return (
|
|
<View style={{ backgroundColor }}>
|
|
<Text style={{ color: textColor }}>Text</Text>
|
|
</View>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Theme Toggle Component
|
|
|
|
Sử dụng `ThemeToggle` component để cho phép user chọn theme:
|
|
|
|
```tsx
|
|
import { ThemeToggle } from "@/components/theme-toggle";
|
|
|
|
function SettingsScreen() {
|
|
return (
|
|
<View>
|
|
<ThemeToggle />
|
|
</View>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Available Styles từ useAppTheme
|
|
|
|
```typescript
|
|
const { styles } = useAppTheme();
|
|
|
|
// Container styles
|
|
styles.container; // Flex 1 container với background
|
|
styles.surface; // Card surface với padding
|
|
styles.card; // Card với shadow và border radius
|
|
|
|
// Button styles
|
|
styles.primaryButton; // Primary button style
|
|
styles.secondaryButton; // Secondary button với border
|
|
styles.primaryButtonText; // White text cho primary button
|
|
styles.secondaryButtonText; // Theme text cho secondary button
|
|
|
|
// Input styles
|
|
styles.textInput; // Text input với border và padding
|
|
|
|
// Status styles
|
|
styles.successContainer; // Success status container
|
|
styles.warningContainer; // Warning status container
|
|
styles.errorContainer; // Error status container
|
|
|
|
// Utility
|
|
styles.separator; // Line separator
|
|
```
|
|
|
|
## Theme Utilities
|
|
|
|
```typescript
|
|
const { utils } = useAppTheme();
|
|
|
|
utils.isDark; // boolean - kiểm tra dark mode
|
|
utils.isLight; // boolean - kiểm tra light mode
|
|
utils.toggleTheme(); // function - toggle giữa light/dark
|
|
utils.getOpacityColor(colorName, opacity); // Tạo màu với opacity
|
|
```
|
|
|
|
## Lưu trữ Theme Preference
|
|
|
|
Theme preference được tự động lưu trong AsyncStorage với key `'theme_mode'`. Khi app khởi động, theme sẽ được khôi phục từ storage.
|
|
|
|
## Best Practices
|
|
|
|
1. **Sử dụng `useAppTheme`** thay vì access colors trực tiếp
|
|
2. **Sử dụng pre-defined styles** từ `useAppTheme().styles`
|
|
3. **Kiểm tra theme** bằng `utils.isDark` thay vì check colorScheme
|
|
4. **Sử dụng opacity colors** cho backgrounds: `utils.getOpacityColor('primary', 0.1)`
|
|
5. **Tận dụng ThemedText và ThemedView** cho các component đơn giản
|
|
|
|
## Migration từ theme cũ
|
|
|
|
Nếu bạn đang sử dụng theme cũ:
|
|
|
|
```tsx
|
|
// Cũ
|
|
const colorScheme = useColorScheme();
|
|
const backgroundColor = colorScheme === "dark" ? "#000" : "#fff";
|
|
|
|
// Mới
|
|
const { colors } = useAppTheme();
|
|
const backgroundColor = colors.background;
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
1. **Theme không được lưu**: Kiểm tra AsyncStorage permissions
|
|
2. **Flash khi khởi động**: ThemeProvider sẽ chờ load theme trước khi render
|
|
3. **Colors không đúng**: Đảm bảo component được wrap trong ThemeProvider
|
|
|
|
## Examples
|
|
|
|
Xem `components/theme-example.tsx` để biết các cách sử dụng theme khác nhau.
|