diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt index 4e6b3de..cc36f54 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt @@ -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 + } + } + } + ) + } } } } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/TodoBottomSheet.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/TodoBottomSheet.kt new file mode 100644 index 0000000..aa384b7 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/TodoBottomSheet.kt @@ -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)) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/ChipGroup.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/ChipGroup.kt new file mode 100644 index 0000000..e83019c --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/ChipGroup.kt @@ -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, + 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) + ) + } + } +} \ No newline at end of file