iT邦幫忙

2024 iThome 鐵人賽

DAY 9
0
Mobile Development

從零開始學React Native系列 第 9

【從零開始學React Native】8. 創建Todo Tracker——改進首頁頁面

  • 分享至 

  • xImage
  •  

今天我們來稍微改進我們的首頁。首先創建Task的type。

src\types\index.ts

export type Task = {
    id: string;
    title: string;
    startDate: string | null;
    endDate: string | null;
    description: string;
    isDone: boolean;
    tags: Tags[];
    subTasks: Task[];
}

export type Tags = {
    title: string;
}


我們這裡創建隨機生成的generateRandomTasks函數。

const generateRandomTasks = (count: number): Task[] => {
  const generateRandomDate = (start: Date, end: Date) => {
    return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toISOString().split('T')[0];
  };

  const possibleTags: Tags[] = [
    { title: '工作' },
    { title: '個人' },
    { title: '學習' },
    { title: '娛樂' },
  ];

  const tasks: Task[] = [];
  for (let i = 0; i < count; i++) {
    const startDate = generateRandomDate(new Date(), new Date(Date.now() + 30 * 24 * 60 * 60 * 1000));
    const endDate = generateRandomDate(new Date(startDate), new Date(Date.now() + 60 * 24 * 60 * 60 * 1000));
    tasks.push({
      id: `task-${i + 1}`,
      title: `任務 ${i + 1}`,
      startDate,
      endDate,
      description: `隨機生成的第 ${i + 1} 筆內容。`,
      isDone: Math.random() < 0.3,
      tags: possibleTags.filter(() => Math.random() < 0.3),
      subTasks: [],
    });
  }
  return tasks;
};

我們使用FlatList來呈現我們的任務列表,並且透過TouchableOpacity以及style來創建我們的checkbox。

接著我們完成我們的頁面src\pages\home.page.tsx,我們這裡先創建50筆:

import React, { useState } from 'react';
import { View, Text, TextInput, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
import { Colors } from 'react-native/Libraries/NewAppScreen';
import { Task, Tags } from '../types';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 10,
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
    marginBottom: 10,
  },
  searchBar: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    borderRadius: 5,
    paddingHorizontal: 10,
    marginBottom: 10,
  },
  taskList: {
    flex: 1,
  },
  taskItem: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 10,
    borderBottomWidth: 1,
    borderBottomColor: '#ccc',
  },
  checkbox: {
    width: 24,
    height: 24,
    borderWidth: 2,
    borderColor: '#007AFF',
    borderRadius: 12,
    marginRight: 10,
    justifyContent: 'center',
    alignItems: 'center',
  },
  checkboxInner: {
    width: 12,
    height: 12,
    borderRadius: 6,
    backgroundColor: '#007AFF',
  },
  taskContent: {
    flex: 1,
  },
  taskTitle: {
    fontSize: 16,
    fontWeight: 'bold',
  },
  taskDates: {
    fontSize: 12,
    color: '#666',
  },
  taskDescription: {
    fontSize: 14,
    marginTop: 5,
  },
  taskTags: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    marginTop: 5,
  },
  tag: {
    backgroundColor: '#E0E0E0',
    borderRadius: 10,
    padding: 5,
    marginRight: 5,
    marginBottom: 5,
  },
  tagText: {
    fontSize: 12,
  },
});

type HomePageProps = {
  isDarkMode?: boolean;
};

const generateRandomTasks = (count: number): Task[] => {
  const generateRandomDate = (start: Date, end: Date) => {
    return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toISOString().split('T')[0];
  };

  const possibleTags: Tags[] = [
    { title: '工作' },
    { title: '個人' },
    { title: '學習' },
    { title: '娛樂' },
  ];

  const tasks: Task[] = [];
  for (let i = 0; i < count; i++) {
    const startDate = generateRandomDate(new Date(), new Date(Date.now() + 30 * 24 * 60 * 60 * 1000));
    const endDate = generateRandomDate(new Date(startDate), new Date(Date.now() + 60 * 24 * 60 * 60 * 1000));
    tasks.push({
      id: `task-${i + 1}`,
      title: `任務 ${i + 1}`,
      startDate,
      endDate,
      description: `隨機生成的第 ${i + 1} 筆內容。`,
      isDone: Math.random() < 0.3,
      tags: possibleTags.filter(() => Math.random() < 0.3),
      subTasks: [],
    });
  }
  return tasks;
};

const HomePage: React.FC<HomePageProps> = ({ isDarkMode = false }) => {
  const [tasks, setTasks] = useState<Task[]>(generateRandomTasks(50));

  const toggleTaskStatus = (id: string) => {
    setTasks(prevTasks =>
      prevTasks.map(task =>
        task.id === id ? { ...task, isDone: !task.isDone } : task
      )
    );
  };

  const renderTask = ({ item }: { item: Task }) => (
    <View
      style={[
        styles.taskItem,
        { backgroundColor: item.isDone ? '#E8F5E9' : 'transparent' }
      ]}
    >
      <TouchableOpacity onPress={() => toggleTaskStatus(item.id)}>
        <View style={styles.checkbox}>
          {item.isDone && <View style={styles.checkboxInner} />}
        </View>
      </TouchableOpacity>![https://ithelp.ithome.com.tw/upload/images/20240923/20108931TbGiOFWbV6.png](https://ithelp.ithome.com.tw/upload/images/20240923/20108931TbGiOFWbV6.png)
      <View style={styles.taskContent}>
        <Text style={styles.taskTitle}>{item.title}</Text>
        <Text style={styles.taskDates}>
          {item.startDate} - {item.endDate}
        </Text>
        <Text style={styles.taskDescription}>{item.description}</Text>
        <View style={styles.taskTags}>
          {item.tags.map((tag, index) => (
            <View key={index} style={styles.tag}>
              <Text style={styles.tagText}>{tag.title}</Text>
            </View>
          ))}
        </View>
      </View>
    </View>
  );

  return (
    <View style={styles.container}>
      <Text style={styles.title}>任務列表</Text>
      <TextInput
        style={styles.searchBar}
        placeholder="搜尋欄"
        placeholderTextColor={isDarkMode ? Colors.light : Colors.dark}
      />
      <FlatList
        style={styles.taskList}
        data={tasks}
        renderItem={renderTask}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

export default HomePage;

https://ithelp.ithome.com.tw/upload/images/20240923/201089319OLzG4axak.png

今天先簡易的修改一下首頁,明天會接續完成其他的頁面部分,並且引進狀態管理套件。


上一篇
【從零開始學React Native】7. 創建Todo Tracker——創建其他頁面
下一篇
【從零開始學React Native】9. 創建Todo Tracker——添加狀態管理和調整新增頁面
系列文
從零開始學React Native20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言