import { StatusBar } from 'expo-status-bar';
import React, { useEffect, useState } from 'react';
import {
StyleSheet,
SafeAreaView,
View,
TextInput,
Text,
FlatList,
TouchableOpacity,
Alert,
Keyboard
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import AsyncStorage from '@react-native-async-storage/async-storage';
const COLORS = { primary: "#1f145c", white: "#fff" };
export default function App() {
const [todos, setTodos] = useState([])
useEffect(() => {
getTodoToUserDevice()
}, [])
//
useEffect(() => {
// saveTodotoUserDevice()
saveTodoToUserDevice(todos)
}, [todos])
const [textInput, setTextInput] = useState("")
// 新增todo
const addTodo = () => {
// console.log(textInput)
//
if (textInput == "") {
Alert.alert("錯誤", "請輸入資料")
} else {
const newTodo = {
id: Math.random(),
task: textInput,
completed: false
}
// console.log(newTodo)
setTodos([...todos, newTodo])
//清空輸入區
setTextInput("")
//關閉螢幕鍵盤
Keyboard.dismiss();
// console.log(todos)
}
}
// 刪除 todo
const deleteTodo = (todoId) => {
// console.log(todoId)
const newTodos = todos.filter(item => item.id != todoId)
setTodos(newTodos)
}
// 清空 todo
const clearTodo = () => {
Alert.alert("確認", "清空待辦事項!", [{
text: "Yes",
onPress: () => setTodos([]),
},
{ text: "No" }])
}
// 標示完成
const markTodoCompleted = (todoId) => {
//印出id
console.log(todoId)
const newTodos = todos.map((item) => {
if (item.id == todoId) {
return { ...item, completed: true }
}
return item
})
setTodos(newTodos)
}
//存檔功能
const saveTodoToUserDevice = async todos => {
try {
const stringifyTodos = JSON.stringify(todos);
await AsyncStorage.setItem('todos', stringifyTodos);
// console.log("todos= ", todos);
} catch (error) {
console.log(error);
}
};
//讀檔功能
const getTodoToUserDevice = async () => {
try {
const todos = await AsyncStorage.getItem("todos")
if (todos != null) {
setTodos(JSON.parse(todos))
}
} catch (error) {
console.log(error);
}
}
const ListItem = ({ mytodo }) => {
return <View style={styles.listItem}>
{/* 文字區 */}
<View style={{ flex: 1 }}>
<Text style={{ fontWeight: "bold", fontSize: 16, color: COLORS.primary, textDecorationLine: mytodo?.completed ? "line-through" : "none" }}>{mytodo?.task}</Text>
</View>
{/* 圖示區 */}
{
// 打勾的圖示
!mytodo?.completed && (
<TouchableOpacity
style={[styles.actionIcon]}
onPress={() => markTodoCompleted(mytodo?.id)}
>
<Icon
name="done"
size={20}
color={COLORS.white}
/>
</TouchableOpacity>
)
}
{/* 刪除的圖示 */}
<TouchableOpacity
style={[styles.actionIcon, { backgroundColor: "red" }]}
onPress={() => deleteTodo(mytodo?.id)}
>
<Icon
name="delete"
size={20}
color={COLORS.white}
/>
</TouchableOpacity>
</View>
}
return (
<SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}>
{/* 頁首 */}
<View style={styles.header}>
<Text style={{ fontWeight: "bold", fontSize: 20, color: COLORS.primary }}>待辦事項</Text>
{/* 圖示 */}
<Icon
name='delete'
size={26}
color="red"
onPress={clearTodo}
/>
</View>
{/* 中間展示區塊 */}
<FlatList
showsVerticalScrollIndicator={false}
contentContainerStyle={{ padding: 20, paddingBottom: 100 }}
data={todos}
renderItem={
({ item }) => <ListItem mytodo={item} />
}
/>
{/* 頁尾 */}
<View style={styles.footer}>
<View style={styles.inputContainer}>
<TextInput
placeholder='請輸入待辦事項'
onChangeText={(text) => setTextInput(text)}
value={textInput}
/>
</View>
<TouchableOpacity onPress={addTodo}>
<View style={styles.iconContainer}>
<Icon
name="add"
size={30}
color="white" />
</View>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
header: {
padding: 20,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
footer: {
position: "absolute",
bottom: 0,
backgroundColor: COLORS.white,
width: "100%",
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 20,
},
inputContainer: {
backgroundColor: COLORS.white,
elevation: 40,
flex: 1,
height: 50,
marginVertical: 20,
marginRight: 20,
borderRadius: 30,
paddingHorizontal: 20,
},
iconContainer: {
width: 50,
height: 50,
backgroundColor: COLORS.primary,
borderRadius: 25,
elevation: 40,
alignItems: "center",
justifyContent: "center",
},
listItem: {
padding: 20,
backgroundColor: COLORS.white,
flexDirection: "row",
elevation: 12,
borderRadius: 10,
marginVertical: 10,
},
actionIcon: {
width: 25,
height: 25,
backgroundColor: "green",
alignItems: "center",
justifyContent: "center",
marginLeft: 5,
borderRadius: 5
},
});
1. 使用 AsyncStorage 來存儲資料
2. 使用 FlatList 來展示資料區塊
3. ?. 問號點的用法 (可選鏈語法) ()
4. inline style 混合的用法
//inline style 混合用法
套用兩個(或以上) styles
<Text style={[styles.base, styles.background]} >Test </Text>
套用 單行+ styles
<Text style={[styles.base, {color: 'red'}]} >Test </Text>
// .?點問號的用法 (有看沒有懂...XD)
// 程式碼節錄
!mytodo?.completed && (
<TouchableOpacity
style={[styles.actionIcon]}
onPress={() => markTodoCompleted(mytodo?.id)}
>
<Icon
name="done"
size={20}
color={COLORS.white}
/>
</TouchableOpacity>
)
範例 Source Code :
git clone https://smilehsu@bitbucket.org/smilehsu/rn_example_todolist_2.git