Daily Task Manager - 5025211024- Widian Sasi Disertasiani
Judul : Daily Task Manager
Deskripsi :
Daily Task Manager merupakan suatu aplikasi yang dirancang untuk membantu pengguna dalam mengatur tugas yang akan digunakan, termasuk menambahkan, menghapus dan menandai tugas yang telah diselesaikan.
Inofgrafis :
Design UI:
- Menambahkan tugas dengan:
- Judul Tanggal (picker)
- Jam & menit (dropdown)
- Warna kategori (pilihan warna)
- Menampilkan tugas berdasarkan tanggal yang dipilih
- Sorting berdasarkan Deadline atau Status
- Hapus tugas dengan gesture swipe kiri
- Tandai tugas selesai dengan checkbox
Pada aplikasi ini, kita mulai dengan membuat data model Task, yaitu sebuah data class di Kotlin yang digunakan untuk merepresentasikan satu entitas tugas. Setiap tugas memiliki empat atribut utama: title sebagai judul, deadline untuk tanggal dan waktu jatuh tempo, isDone sebagai status selesai atau belum, serta color untuk mewarnai tampilan tugas sesuai pilihan pengguna. Nilai default untuk status adalah false, dan warna default-nya adalah biru muda.
Selanjutnya, di dalam fungsi TaskManagerApp(), kita menggunakan Jetpack Compose untuk menyusun tampilan aplikasi. Kita mendeklarasikan banyak variabel dengan fungsi remember untuk menyimpan state aplikasi, seperti daftar tugas (taskList), input judul tugas (newTaskTitle), tanggal yang dipilih (selectedDate), jam dan menit (selectedHour, selectedMinute), serta warna yang dipilih (selectedColor). Variabel-variabel ini bersifat reaktif, artinya saat nilainya berubah, tampilan akan diperbarui secara otomatis.
Pengguna bisa mengisi form tugas melalui beberapa komponen input Compose. OutlinedTextField digunakan untuk input teks judul tugas. Untuk pemilihan tanggal, kita menggunakan DatePickerDialog yang akan muncul saat pengguna menekan ikon kalender. Jam dan menit dapat dipilih dari dropdown menu yang dibangun menggunakan fungsi DropdownMenuBox, di mana nilainya disusun dari angka 00 hingga 23 untuk jam dan 00 hingga 59 untuk menit. Pengguna juga bisa memilih warna tugas melalui deretan kotak warna yang bisa diklik.
Tugas ditambahkan saat tombol "Tambah Tugas" ditekan, dengan kondisi bahwa judul dan tanggal tidak boleh kosong. Fungsi onClick pada tombol akan menggabungkan data input menjadi objek Task, kemudian menambahkannya ke dalam taskList. Setelah itu, semua input akan di-reset agar siap untuk data tugas selanjutnya.
Untuk menyortir dan memfilter tugas, kode memanfaatkan fitur Kotlin seperti sortedBy dan filter. Tugas dapat diurutkan berdasarkan deadline atau isDone, sesuai pilihan pengguna pada dropdown "Sortir". Kemudian, hanya tugas yang memiliki tanggal sesuai dengan selectedViewDate (yang dipilih dari HorizontalDateSelector) yang akan ditampilkan di layar.
Tugas-tugas ini ditampilkan dalam bentuk kartu menggunakan komponen Card di dalam LazyColumn. Masing-masing kartu menampilkan judul, deadline, dan kotak centang untuk menandai apakah tugas sudah selesai atau belum. Jika pengguna mencentang kotak tersebut, status isDone dari tugas akan diperbarui melalui salinan data tugas (copy) dan digantikan pada posisi yang sama dalam taskList.
Aplikasi ini mendukung fitur swipe untuk menghapus tugas. Ketika pengguna menggeser kartu tugas ke kiri lebih dari 200 pixel, maka tugas tersebut akan dihapus dari daftar. Ini dilakukan dengan fungsi detectHorizontalDragGestures, di mana kita mencatat pergerakan horizontal pengguna, dan jika memenuhi syarat, tugas akan dihapus dari daftar secara otomatis.
package com.example.dailytaskmanager
import android.app.DatePickerDialog
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.*
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CalendarToday
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.example.dailytaskmanager.ui.theme.DailyTaskManagerTheme
import java.text.SimpleDateFormat
import java.util.*
data class Task(
val title: String,
val deadline: String,
var isDone: Boolean = false,
val color: Color = Color(0xFFBBDEFB)
)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DailyTaskManagerTheme {
Surface(modifier = Modifier.fillMaxSize()) {
TaskManagerApp()
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TaskManagerApp() {
var taskList by remember { mutableStateOf(listOf<Task>()) }
var newTaskTitle by remember { mutableStateOf("") }
var selectedColor by remember { mutableStateOf(Color(0xFFBBDEFB)) }
var sortBy by remember { mutableStateOf("Deadline") }
val availableColors = listOf(Color(0xFFBBDEFB), Color(0xFFC8E6C9), Color(0xFFFFCDD2), Color(0xFFFFF9C4))
val context = LocalContext.current
var selectedDate by remember { mutableStateOf("") }
var selectedHour by remember { mutableStateOf("00") }
var selectedMinute by remember { mutableStateOf("00") }
var selectedViewDate by remember {
mutableStateOf(SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()))
}
val hours = (0..23).map { String.format("%02d", it) }
val minutes = (0..59).map { String.format("%02d", it) }
val sortedList = when (sortBy) {
"Status" -> taskList.sortedBy { it.isDone }
else -> taskList.sortedBy { it.deadline }
}
val filteredList = sortedList.filter { it.deadline.startsWith(selectedViewDate) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Spacer(modifier = Modifier.height(8.dp))
HorizontalDateSelector(selectedViewDate) {
selectedViewDate = it
}
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = newTaskTitle,
onValueChange = { newTaskTitle = it },
label = { Text("Judul Tugas") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = selectedDate,
onValueChange = {},
label = { Text("Tanggal") },
enabled = false,
modifier = Modifier.weight(1f)
)
IconButton(onClick = {
val calendar = Calendar.getInstance()
DatePickerDialog(
context,
{ _, year, month, dayOfMonth ->
val date = Calendar.getInstance()
date.set(year, month, dayOfMonth)
selectedDate = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(date.time)
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
).show()
}) {
Icon(Icons.Default.CalendarToday, contentDescription = "Pilih Tanggal")
}
}
Spacer(modifier = Modifier.height(8.dp))
Row {
DropdownMenuBox(label = "Jam", selected = selectedHour, items = hours) {
selectedHour = it
}
Spacer(modifier = Modifier.width(16.dp))
DropdownMenuBox(label = "Menit", selected = selectedMinute, items = minutes) {
selectedMinute = it
}
}
Spacer(modifier = Modifier.height(8.dp))
Row {
availableColors.forEach { color ->
Box(
modifier = Modifier
.size(40.dp)
.padding(4.dp)
.clip(RoundedCornerShape(8.dp))
.background(color)
.border(
width = if (color == selectedColor) 2.dp else 0.dp,
color = Color.Black,
shape = RoundedCornerShape(4.dp)
)
.clickable { selectedColor = color }
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Sortir: ")
Spacer(modifier = Modifier.width(8.dp))
DropdownMenuSort(selected = sortBy, onSortSelected = { sortBy = it })
}
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = {
if (newTaskTitle.isNotBlank() && selectedDate.isNotBlank()) {
val deadline = "$selectedDate $selectedHour:$selectedMinute"
taskList = taskList + Task(newTaskTitle, deadline, false, selectedColor)
newTaskTitle = ""
selectedDate = ""
selectedHour = "00"
selectedMinute = "00"
}
},
modifier = Modifier.align(Alignment.End)
) {
Text("Tambah Tugas")
}
Spacer(modifier = Modifier.height(12.dp))
LazyColumn {
items(filteredList, key = { it.hashCode() }) { task ->
var offsetX by remember { mutableStateOf(0f) }
Box(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
.background(Color.Transparent)
) {
if (offsetX < -200f) {
Row(
modifier = Modifier
.matchParentSize()
.background(Color(0xFF800000)) // Maroon color for deletion
.padding(end = 20.dp),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
Icon(Icons.Default.Delete, contentDescription = null, tint = Color.White)
}
}
Card(
modifier = Modifier
.offset { IntOffset(offsetX.toInt(), 0) }
.pointerInput(Unit) {
detectHorizontalDragGestures(
onHorizontalDrag = { _, dragAmount ->
offsetX += dragAmount
},
onDragEnd = {
if (offsetX < -200f) {
taskList = taskList.toMutableList().apply { remove(task) }
}
offsetX = 0f
}
)
}
.fillMaxWidth()
.padding(vertical = 4.dp),
colors = CardDefaults.cardColors(containerColor = task.color),
elevation = CardDefaults.cardElevation(2.dp)
) {
Row(
modifier = Modifier
.padding(12.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(modifier = Modifier.weight(1f)) {
Text(task.title, style = MaterialTheme.typography.titleMedium)
Text("Deadline: ${task.deadline}", style = MaterialTheme.typography.bodySmall)
}
Checkbox(
checked = task.isDone,
onCheckedChange = {
taskList = taskList.toMutableList().apply {
val index = indexOf(task)
this[index] = this[index].copy(isDone = it)
}
}
)
}
}
}
}
}
}
}
@Composable
fun HorizontalDateSelector(
selectedDate: String,
onDateSelected: (String) -> Unit
) {
val calendar = Calendar.getInstance()
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val dateList = (0..6).map {
val date = formatter.format(calendar.time)
calendar.add(Calendar.DAY_OF_MONTH, 1)
date
}
calendar.time = Date()
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())
.padding(vertical = 8.dp)
) {
dateList.forEach { date ->
val day = SimpleDateFormat("dd", Locale.getDefault()).format(formatter.parse(date)!!)
val weekday = SimpleDateFormat("EEE", Locale.getDefault()).format(formatter.parse(date)!!)
val isSelected = date == selectedDate
val bgColor = if (isSelected) Color(0xFF495D92) else Color.Transparent
val textColor = if (isSelected) Color.White else Color.Black
val borderColor = Color.White
Column(
modifier = Modifier
.padding(horizontal = 6.dp)
.clip(RoundedCornerShape(8.dp))
.background(bgColor)
.border(BorderStroke(2.dp, borderColor), shape = RoundedCornerShape(8.dp))
.clickable { onDateSelected(date) }
.padding(vertical = 12.dp, horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = day,
color = textColor,
fontWeight = FontWeight.SemiBold
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = weekday,
color = textColor,
style = MaterialTheme.typography.bodySmall
)
}
}
}
}
@Composable
fun DropdownMenuBox(label: String, selected: String, items: List<String>, onSelected: (String) -> Unit) {
var expanded by remember { mutableStateOf(false) }
Column {
OutlinedButton(onClick = { expanded = true }) {
Text("$label: $selected")
}
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
items.forEach {
DropdownMenuItem(text = { Text(it) }, onClick = {
onSelected(it)
expanded = false
})
}
}
}
}
@Composable
fun DropdownMenuSort(selected: String, onSortSelected: (String) -> Unit) {
var expanded by remember { mutableStateOf(false) }
val options = listOf("Deadline", "Status")
Box {
OutlinedButton(onClick = { expanded = true }) {
Text(selected)
}
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
options.forEach { option ->
DropdownMenuItem(text = { Text(option) }, onClick = {
onSortSelected(option)
expanded = false
})
}
}
}
}
Link Download: Daily Task Manager
Source code: Code
Presentasi: Demo
Komentar
Posting Komentar