src/App.tsx
import { useState } from 'react'
import {
FlatList,
Platform,
Pressable,
SafeAreaView,
StyleSheet,
Text,
TextInput,
View,
useWindowDimensions,
} from 'react-native'
import { GoalRow, type Goal } from './components/GoalRow'
import { colors, spacing } from './theme'
const STARTER_GOALS: Goal[] = [
{ id: '1', title: 'Sketch the onboarding screen', done: true },
{ id: '2', title: 'Wire up the FlatList', done: false },
{ id: '3', title: 'Try it on a phone with Expo', done: false },
]
export default function App() {
const [goals, setGoals] = useState(STARTER_GOALS)
const [draft, setDraft] = useState('')
const { width } = useWindowDimensions()
const doneCount = goals.filter((goal) => goal.done).length
const addGoal = () => {
const title = draft.trim()
if (!title) return
setGoals((current) => [...current, { id: String(Date.now()), title, done: false }])
setDraft('')
}
const toggleGoal = (id: string) =>
setGoals((current) =>
current.map((goal) => (goal.id === id ? { ...goal, done: !goal.done } : goal)),
)
return (
<SafeAreaView style={styles.screen}>
<View style={styles.header}>
<Text style={styles.eyebrow}>
React Native · {Platform.OS} · {Math.round(width)}px wide
</Text>
<Text style={styles.title}>Plan your day</Text>
<Text style={styles.subtitle}>
{doneCount} of {goals.length} done
</Text>
</View>
<View style={styles.composer}>
<TextInput
style={styles.input}
placeholder="Add a goal"
placeholderTextColor={colors.muted}
value={draft}
onChangeText={setDraft}
onSubmitEditing={addGoal}
returnKeyType="done"
/>
<Pressable
accessibilityRole="button"
onPress={addGoal}
style={({ pressed }) => [styles.addButton, pressed && styles.addButtonPressed]}
>
<Text style={styles.addButtonText}>Add goal</Text>
</Pressable>
</View>
<FlatList
data={goals}
keyExtractor={(goal) => goal.id}
contentContainerStyle={styles.list}
renderItem={({ item }) => <GoalRow goal={item} onToggle={() => toggleGoal(item.id)} />}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
screen: {
flex: 1,
backgroundColor: colors.background,
},
header: {
paddingHorizontal: spacing.lg,
paddingTop: spacing.xl,
paddingBottom: spacing.md,
},
eyebrow: {
color: colors.accent,
fontSize: 12,
fontWeight: '700',
letterSpacing: 1.2,
textTransform: 'uppercase',
},
title: {
marginTop: spacing.xs,
color: colors.text,
fontSize: 34,
fontWeight: '800',
letterSpacing: -0.5,
},
subtitle: {
marginTop: spacing.xs,
color: colors.muted,
fontSize: 16,
},
composer: {
flexDirection: 'row',
gap: spacing.sm,
paddingHorizontal: spacing.lg,
paddingBottom: spacing.md,
},
input: {
flex: 1,
height: 44,
paddingHorizontal: spacing.md,
borderRadius: 12,
borderWidth: 1,
borderColor: colors.border,
backgroundColor: colors.card,
color: colors.text,
fontSize: 16,
},
addButton: {
justifyContent: 'center',
paddingHorizontal: spacing.md,
borderRadius: 12,
backgroundColor: colors.accent,
},
addButtonPressed: {
opacity: 0.8,
},
addButtonText: {
color: '#ffffff',
fontSize: 15,
fontWeight: '700',
},
list: {
paddingHorizontal: spacing.lg,
paddingBottom: spacing.xl,
gap: spacing.sm,
},
})