feat: 添加引导流程和个人信息收集功能
- 在应用中集成引导流程,用户首次启动时显示欢迎页面和个人信息收集页面 - 使用 AsyncStorage 存储用户的引导状态和个人信息 - 在个人页面中添加重置引导流程的功能 - 更新依赖项,添加 @react-native-async-storage/async-storage 库以支持数据存储 - 修改布局以支持新页面的导航和显示
This commit is contained in:
233
app/onboarding/index.tsx
Normal file
233
app/onboarding/index.tsx
Normal file
@@ -0,0 +1,233 @@
|
||||
import { ThemedText } from '@/components/ThemedText';
|
||||
import { ThemedView } from '@/components/ThemedView';
|
||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||
import { useThemeColor } from '@/hooks/useThemeColor';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { router } from 'expo-router';
|
||||
import React from 'react';
|
||||
import {
|
||||
Dimensions,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
const { width, height } = Dimensions.get('window');
|
||||
|
||||
export default function WelcomeScreen() {
|
||||
const colorScheme = useColorScheme();
|
||||
const backgroundColor = useThemeColor({}, 'background');
|
||||
const primaryColor = useThemeColor({}, 'primary');
|
||||
const textColor = useThemeColor({}, 'text');
|
||||
|
||||
const handleGetStarted = () => {
|
||||
router.push('/onboarding/personal-info');
|
||||
};
|
||||
|
||||
const handleSkip = async () => {
|
||||
try {
|
||||
await AsyncStorage.setItem('@onboarding_completed', 'true');
|
||||
router.replace('/(tabs)');
|
||||
} catch (error) {
|
||||
console.error('保存引导状态失败:', error);
|
||||
router.replace('/(tabs)');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemedView style={[styles.container, { backgroundColor }]}>
|
||||
<StatusBar
|
||||
barStyle={colorScheme === 'dark' ? 'light-content' : 'dark-content'}
|
||||
backgroundColor={backgroundColor}
|
||||
/>
|
||||
|
||||
{/* 跳过按钮 */}
|
||||
<TouchableOpacity style={styles.skipButton} onPress={handleSkip}>
|
||||
<ThemedText style={[styles.skipText, { color: textColor }]}>
|
||||
跳过
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* 主要内容区域 */}
|
||||
<View style={styles.contentContainer}>
|
||||
{/* Logo 或插图区域 */}
|
||||
<View style={styles.imageContainer}>
|
||||
<View style={[styles.logoPlaceholder, { backgroundColor: primaryColor }]}>
|
||||
<Text style={styles.logoText}>🧘♀️</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 标题和描述 */}
|
||||
<View style={styles.textContainer}>
|
||||
<ThemedText type="title" style={styles.title}>
|
||||
欢迎来到数字普拉提
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.subtitle, { color: textColor + '90' }]}>
|
||||
让我们一起开始您的健康之旅{'\n'}
|
||||
个性化的普拉提体验正等着您
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
{/* 特色功能点 */}
|
||||
<View style={styles.featuresContainer}>
|
||||
{[
|
||||
{ icon: '📊', title: '个性化训练', desc: '根据您的身体状况定制训练计划' },
|
||||
{ icon: '🤖', title: 'AI 姿态分析', desc: '实时纠正您的动作姿态' },
|
||||
{ icon: '📈', title: '进度追踪', desc: '记录您的每一次进步' },
|
||||
].map((feature, index) => (
|
||||
<View key={index} style={styles.featureItem}>
|
||||
<Text style={styles.featureIcon}>{feature.icon}</Text>
|
||||
<View style={styles.featureTextContainer}>
|
||||
<ThemedText style={[styles.featureTitle, { color: textColor }]}>
|
||||
{feature.title}
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.featureDesc, { color: textColor + '70' }]}>
|
||||
{feature.desc}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 底部按钮 */}
|
||||
<View style={styles.buttonContainer}>
|
||||
<TouchableOpacity
|
||||
style={[styles.getStartedButton, { backgroundColor: primaryColor }]}
|
||||
onPress={handleGetStarted}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Text style={styles.getStartedButtonText}>开始体验</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity style={styles.laterButton} onPress={handleSkip}>
|
||||
<ThemedText style={[styles.laterButtonText, { color: textColor + '70' }]}>
|
||||
稍后再说
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</ThemedView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingTop: StatusBar.currentHeight || 44,
|
||||
},
|
||||
skipButton: {
|
||||
position: 'absolute',
|
||||
top: StatusBar.currentHeight ? StatusBar.currentHeight + 16 : 60,
|
||||
right: 20,
|
||||
zIndex: 10,
|
||||
padding: 8,
|
||||
},
|
||||
skipText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
},
|
||||
contentContainer: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 24,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
imageContainer: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 40,
|
||||
},
|
||||
logoPlaceholder: {
|
||||
width: 120,
|
||||
height: 120,
|
||||
borderRadius: 60,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 4,
|
||||
},
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 8,
|
||||
},
|
||||
logoText: {
|
||||
fontSize: 48,
|
||||
},
|
||||
textContainer: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 48,
|
||||
},
|
||||
title: {
|
||||
textAlign: 'center',
|
||||
marginBottom: 16,
|
||||
fontWeight: '700',
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
lineHeight: 24,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
featuresContainer: {
|
||||
marginBottom: 40,
|
||||
},
|
||||
featureItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 24,
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
featureIcon: {
|
||||
fontSize: 32,
|
||||
marginRight: 16,
|
||||
width: 40,
|
||||
textAlign: 'center',
|
||||
},
|
||||
featureTextContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
featureTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
marginBottom: 4,
|
||||
},
|
||||
featureDesc: {
|
||||
fontSize: 14,
|
||||
lineHeight: 20,
|
||||
},
|
||||
buttonContainer: {
|
||||
paddingHorizontal: 24,
|
||||
paddingBottom: 48,
|
||||
},
|
||||
getStartedButton: {
|
||||
height: 56,
|
||||
borderRadius: 16,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 4,
|
||||
},
|
||||
getStartedButtonText: {
|
||||
color: '#192126',
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
},
|
||||
laterButton: {
|
||||
height: 48,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
laterButtonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user