名称: react-native-styling 用户可调用: false 描述: 用于使用StyleSheet、Flexbox布局、响应式设计和主题化来样式化React Native组件。涵盖平台特定样式和设计系统。 允许工具:
- 读取
- 写入
- 编辑
- Bash
- Grep
- Glob
React Native 样式设计
使用此技能当使用StyleSheet API、Flexbox布局样式化React Native组件,并创建响应式、平台感知的设计。
关键概念
StyleSheet API
使用StyleSheet创建优化样式:
import { View, Text, StyleSheet } from 'react-native';
export default function Card() {
return (
<View style={styles.container}>
<Text style={styles.title}>标题</Text>
<Text style={styles.body}>正文文本</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
backgroundColor: '#fff',
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3, // Android 阴影
},
title: {
fontSize: 18,
fontWeight: 'bold',
color: '#333',
marginBottom: 8,
},
body: {
fontSize: 14,
color: '#666',
lineHeight: 20,
},
});
Flexbox 布局
React Native 默认使用Flexbox:
import { View, StyleSheet } from 'react-native';
// 列布局(默认)
const styles = StyleSheet.create({
column: {
flex: 1,
flexDirection: 'column', // 默认
justifyContent: 'flex-start', // 默认
alignItems: 'stretch', // 默认
},
});
// 行布局
const rowStyles = StyleSheet.create({
row: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 16,
},
});
// 居中内容
const centeredStyles = StyleSheet.create({
centered: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
响应式设计
使用Dimensions进行响应式布局:
import { View, Dimensions, StyleSheet } from 'react-native';
const { width, height } = Dimensions.get('window');
const styles = StyleSheet.create({
container: {
width: width * 0.9, // 屏幕宽度的90%
height: height * 0.5, // 屏幕高度的50%
},
card: {
width: width > 768 ? 400 : width * 0.9, // 平板与手机对比
},
});
平台特定样式
应用平台特定样式:
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: Platform.OS === 'ios' ? 20 : 0,
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 4,
},
}),
},
text: {
fontFamily: Platform.select({
ios: 'System',
android: 'Roboto',
}),
},
});
最佳实践
使用 StyleSheet.create()
始终使用StyleSheet以提升性能:
// 不好 - 每次渲染都创建新对象
<View style={{ padding: 16, backgroundColor: '#fff' }}>
<Text>内容</Text>
</View>
// 好 - 使用StyleSheet优化
const styles = StyleSheet.create({
container: {
padding: 16,
backgroundColor: '#fff',
},
});
<View style={styles.container}>
<Text>内容</Text>
</View>
使用数组组合样式
使用数组组合样式:
import { View, Text, StyleSheet } from 'react-native';
function Button({ primary, disabled }: { primary?: boolean; disabled?: boolean }) {
return (
<View style={[
styles.button,
primary && styles.buttonPrimary,
disabled && styles.buttonDisabled,
]}>
<Text style={[
styles.buttonText,
primary && styles.buttonTextPrimary,
]}>
按下我
</Text>
</View>
);
}
const styles = StyleSheet.create({
button: {
padding: 12,
borderRadius: 8,
backgroundColor: '#e0e0e0',
},
buttonPrimary: {
backgroundColor: '#007AFF',
},
buttonDisabled: {
opacity: 0.5,
},
buttonText: {
textAlign: 'center',
color: '#333',
fontWeight: '600',
},
buttonTextPrimary: {
color: '#fff',
},
});
设计令牌
创建可重用的设计令牌:
// theme.ts
export const colors = {
primary: '#007AFF',
secondary: '#5856D6',
success: '#34C759',
error: '#FF3B30',
warning: '#FF9500',
background: '#FFFFFF',
surface: '#F2F2F7',
text: {
primary: '#000000',
secondary: '#3C3C43',
tertiary: '#8E8E93',
},
};
export const spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
};
export const typography = {
h1: {
fontSize: 32,
fontWeight: 'bold' as const,
lineHeight: 40,
},
h2: {
fontSize: 24,
fontWeight: 'bold' as const,
lineHeight: 32,
},
body: {
fontSize: 16,
lineHeight: 24,
},
caption: {
fontSize: 12,
lineHeight: 16,
},
};
export const shadows = {
small: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
},
medium: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 4,
elevation: 4,
},
large: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.2,
shadowRadius: 8,
elevation: 8,
},
};
// 使用
import { colors, spacing, typography, shadows } from './theme';
const styles = StyleSheet.create({
container: {
padding: spacing.md,
backgroundColor: colors.background,
},
title: {
...typography.h1,
color: colors.text.primary,
},
card: {
...shadows.medium,
borderRadius: 8,
},
});
主题上下文
实现主题切换:
import React, { createContext, useContext, useState } from 'react';
type Theme = {
colors: {
background: string;
text: string;
primary: string;
};
};
const lightTheme: Theme = {
colors: {
background: '#FFFFFF',
text: '#000000',
primary: '#007AFF',
},
};
const darkTheme: Theme = {
colors: {
background: '#000000',
text: '#FFFFFF',
primary: '#0A84FF',
},
};
const ThemeContext = createContext<{
theme: Theme;
toggleTheme: () => void;
}>({
theme: lightTheme,
toggleTheme: () => {},
});
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [isDark, setIsDark] = useState(false);
const toggleTheme = () => setIsDark(!isDark);
return (
<ThemeContext.Provider
value={{
theme: isDark ? darkTheme : lightTheme,
toggleTheme,
}}
>
{children}
</ThemeContext.Provider>
);
}
export const useTheme = () => useContext(ThemeContext);
// 使用
function MyComponent() {
const { theme } = useTheme();
return (
<View style={{ backgroundColor: theme.colors.background }}>
<Text style={{ color: theme.colors.text }}>主题化文本</Text>
</View>
);
}
常见模式
卡片组件与变体
import React from 'react';
import { View, Text, StyleSheet, ViewStyle } from 'react-native';
interface CardProps {
title: string;
children: React.ReactNode;
variant?: 'default' | 'outlined' | 'elevated';
}
export default function Card({ title, children, variant = 'default' }: CardProps) {
const variantStyle = variant === 'outlined'
? styles.outlined
: variant === 'elevated'
? styles.elevated
: styles.default;
return (
<View style={[styles.card, variantStyle]}>
<Text style={styles.title}>{title}</Text>
<View style={styles.content}>{children}</View>
</View>
);
}
const styles = StyleSheet.create({
card: {
borderRadius: 12,
padding: 16,
marginVertical: 8,
},
default: {
backgroundColor: '#F2F2F7',
},
outlined: {
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#C6C6C8',
},
elevated: {
backgroundColor: '#fff',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 4,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 12,
},
content: {
marginTop: 8,
},
});
响应式网格
import React from 'react';
import { View, Dimensions, StyleSheet } from 'react-native';
const { width } = Dimensions.get('window');
const columns = width > 768 ? 3 : 2;
const gap = 16;
const itemWidth = (width - (columns + 1) * gap) / columns;
function Grid({ items }: { items: React.ReactNode[] }) {
return (
<View style={styles.container}>
{items.map((item, index) => (
<View key={index} style={styles.item}>
{item}
</View>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
flexWrap: 'wrap',
padding: gap,
gap: gap,
},
item: {
width: itemWidth,
},
});
动画按钮
import React from 'react';
import {
Pressable,
Text,
StyleSheet,
Animated,
PressableStateCallbackType,
} from 'react-native';
interface ButtonProps {
title: string;
onPress: () => void;
}
export default function AnimatedButton({ title, onPress }: ButtonProps) {
const scale = new Animated.Value(1);
const handlePressIn = () => {
Animated.spring(scale, {
toValue: 0.95,
useNativeDriver: true,
}).start();
};
const handlePressOut = () => {
Animated.spring(scale, {
toValue: 1,
useNativeDriver: true,
}).start();
};
return (
<Pressable
onPress={onPress}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
>
{({ pressed }) => (
<Animated.View
style={[
styles.button,
{ transform: [{ scale }] },
pressed && styles.pressed,
]}
>
<Text style={styles.text}>{title}</Text>
</Animated.View>
)}
</Pressable>
);
}
const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: 'center',
},
pressed: {
backgroundColor: '#0051D5',
},
text: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});
反模式
不要使用百分比表示维度
// 不好 - 百分比不如预期工作
<View style={{ width: '50%' }}>
<Text>半宽</Text>
</View>
// 好 - 使用flex或特定维度
<View style={{ flex: 1 }}>
<Text>全宽</Text>
</View>
不要忘记平台特定阴影
// 不好 - 仅iOS阴影
<View style={{
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
}}>
<Text>卡片</Text>
</View>
// 好 - iOS和Android都包含
<View style={{
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 4, // Android
}}>
<Text>卡片</Text>
</View>
不要使用变换进行布局
// 不好 - 变换不影响布局
<View style={{ transform: [{ translateX: 100 }] }}>
<Text>已移动</Text>
</View>
// 好 - 使用边距/内边距进行布局
<View style={{ marginLeft: 100 }}>
<Text>已移动</Text>
</View>
不要硬编码颜色
// 不好 - 硬编码颜色
<View style={{ backgroundColor: '#007AFF' }}>
<Text style={{ color: '#FFFFFF' }}>文本</Text>
</View>
// 好 - 使用设计令牌
import { colors } from './theme';
<View style={{ backgroundColor: colors.primary }}>
<Text style={{ color: colors.text.onPrimary }}>文本</Text>
</View>
相关技能
- react-native-components: 样式化核心组件
- react-native-platform: 平台特定样式考虑
- react-native-performance: 优化样式性能