feat: 初步实现添加待办功能

有待后续进行国际化和无障碍适配
This commit is contained in:
Super12138 2025-01-11 22:22:32 +08:00
parent 0054652044
commit 52c9ce58ae
3 changed files with 226 additions and 7 deletions

View file

@ -16,11 +16,16 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@ -28,6 +33,7 @@ import androidx.compose.ui.unit.dp
import cn.super12138.todo.R
import cn.super12138.todo.logic.database.TodoEntity
import cn.super12138.todo.ui.viewmodels.MainViewModel
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -40,6 +46,10 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
}
}
var openBottomSheet by rememberSaveable { mutableStateOf(false) }
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val scope = rememberCoroutineScope()
Scaffold(
topBar = {
TopAppBar(
@ -61,12 +71,7 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
floatingActionButton = {
FloatingActionButton(
onClick = {
viewModel.addTodo(
TodoEntity(
content = "测试内容",
subject = "测试学科"
)
)
openBottomSheet = true
}
) {
Row(
@ -99,7 +104,9 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
ManagerFragment(
state = listState,
list = toDoList.value.filter { item -> !item.isCompleted },
onItemClick = {},
onItemClick = {
},
onItemChecked = { item ->
item.apply {
viewModel.updateTodo(
@ -116,6 +123,34 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
.weight(3f)
.fillMaxSize()
)
if (openBottomSheet) {
TodoBottomSheet(
sheetState = bottomSheetState,
onDismissRequest = {
openBottomSheet = false
},
onSave = { toDo ->
viewModel.addTodo(toDo)
scope
.launch { bottomSheetState.hide() }
.invokeOnCompletion {
if (!bottomSheetState.isVisible) {
openBottomSheet = false
}
}
},
onClose = {
scope
.launch { bottomSheetState.hide() }
.invokeOnCompletion {
if (!bottomSheetState.isVisible) {
openBottomSheet = false
}
}
}
)
}
}
}
}

View file

@ -0,0 +1,123 @@
package cn.super12138.todo.ui.pages.main
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.SheetState
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import cn.super12138.todo.R
import cn.super12138.todo.logic.database.TodoEntity
import cn.super12138.todo.ui.pages.main.components.FilterChipGroup
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TodoBottomSheet(
sheetState: SheetState,
onDismissRequest: () -> Unit,
onSave: (TodoEntity) -> Unit,
onClose: () -> Unit,
modifier: Modifier = Modifier
) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = onDismissRequest,
modifier = modifier
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(horizontal = 16.dp)
) {
Text(
text = stringResource(R.string.action_add_todo),
style = MaterialTheme.typography.headlineMedium
)
Spacer(Modifier.size(25.dp))
var text by rememberSaveable { mutableStateOf("") }
var isError by rememberSaveable { mutableStateOf(false) }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("待办内容") },
isError = isError,
supportingText = {
AnimatedVisibility(isError) {
Text("没有内容")
}
},
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.size(5.dp))
val subjects =
listOf(
"语文",
"数学",
"英语",
"生物",
"地理",
"物理",
"化学",
"道法",
"历史",
"其它"
)
var selectedItemIndex by rememberSaveable { mutableIntStateOf(0) }
FilterChipGroup(
items = subjects,
onSelectedChanged = {
selectedItemIndex = it
},
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.size(20.dp))
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
OutlinedButton(
onClick = onClose
) {
Text("取消")
}
FilledTonalButton(
onClick = {
if (text.trim().isEmpty()) {
isError = true
return@FilledTonalButton
}
isError = false
onSave(
TodoEntity(
content = text,
subject = subjects[selectedItemIndex]
)
)
}
) {
Text("添加")
}
}
Spacer(Modifier.size(30.dp))
}
}
}

View file

@ -0,0 +1,61 @@
package cn.super12138.todo.ui.pages.main.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
/**
* 部分参考https://github.com/Rhythamtech/FilterChipGroup-Compose-Android/blob/main/FilterChipGroup.kt
*/
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun FilterChipGroup(
items: List<String>,
defaultSelectedItemIndex: Int = 0,
onSelectedChanged: (Int) -> Unit = {},
modifier: Modifier = Modifier
) {
var selectedItemIndex by rememberSaveable { mutableIntStateOf(defaultSelectedItemIndex) }
FlowRow(modifier = modifier) {
items.forEachIndexed { index, item ->
FilterChip(
selected = items[selectedItemIndex] == items[index],
onClick = {
selectedItemIndex = index
onSelectedChanged(index)
},
leadingIcon = {
AnimatedVisibility(
visible = items[selectedItemIndex] == items[index]
) {
Icon(
imageVector = Icons.Outlined.Check,
contentDescription = "", // TODO: 添加字符串
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
}
},
label = {
Text(item)
},
modifier = Modifier.padding(horizontal = 5.dp)
)
}
}
}