feat: 初步实现添加待办功能
有待后续进行国际化和无障碍适配
This commit is contained in:
parent
0054652044
commit
52c9ce58ae
3 changed files with 226 additions and 7 deletions
|
|
@ -16,11 +16,16 @@ import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
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.R
|
||||||
import cn.super12138.todo.logic.database.TodoEntity
|
import cn.super12138.todo.logic.database.TodoEntity
|
||||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@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(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
|
@ -61,12 +71,7 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.addTodo(
|
openBottomSheet = true
|
||||||
TodoEntity(
|
|
||||||
content = "测试内容",
|
|
||||||
subject = "测试学科"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -99,7 +104,9 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||||
ManagerFragment(
|
ManagerFragment(
|
||||||
state = listState,
|
state = listState,
|
||||||
list = toDoList.value.filter { item -> !item.isCompleted },
|
list = toDoList.value.filter { item -> !item.isCompleted },
|
||||||
onItemClick = {},
|
onItemClick = {
|
||||||
|
|
||||||
|
},
|
||||||
onItemChecked = { item ->
|
onItemChecked = { item ->
|
||||||
item.apply {
|
item.apply {
|
||||||
viewModel.updateTodo(
|
viewModel.updateTodo(
|
||||||
|
|
@ -116,6 +123,34 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||||
.weight(3f)
|
.weight(3f)
|
||||||
.fillMaxSize()
|
.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue