feat: 将待办 BottomSheet 提取为单独页面

This commit is contained in:
Super12138 2025-01-16 22:09:46 +08:00
parent 9f3fd5f7ec
commit ca08c21ca5
6 changed files with 48 additions and 66 deletions

View file

@ -6,6 +6,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import cn.super12138.todo.ui.pages.editor.TodoEditorPage
import cn.super12138.todo.ui.pages.main.MainPage import cn.super12138.todo.ui.pages.main.MainPage
import cn.super12138.todo.ui.pages.settings.SettingsAbout import cn.super12138.todo.ui.pages.settings.SettingsAbout
import cn.super12138.todo.ui.pages.settings.SettingsAboutLicence import cn.super12138.todo.ui.pages.settings.SettingsAboutLicence
@ -51,10 +52,32 @@ fun TodoNavigation(
composable(TodoScreen.Main.name) { composable(TodoScreen.Main.name) {
MainPage( MainPage(
viewModel = viewModel, viewModel = viewModel,
toTodoEditPage = { navController.navigate(TodoScreen.TodoEditor.name) },
toSettingsPage = { navController.navigate(TodoScreen.SettingsMain.name) } toSettingsPage = { navController.navigate(TodoScreen.SettingsMain.name) }
) )
} }
composable(TodoScreen.TodoEditor.name) {
TodoEditorPage(
toDo = viewModel.selectedEditTodo,
onSave = {
viewModel.addTodo(it)
navController.navigateUp()
},
onDelete = {
if (viewModel.selectedEditTodo !== null) {
viewModel.deleteTodo(viewModel.selectedEditTodo!!)
viewModel.setEditTodoItem(null)
}
navController.navigateUp()
},
onNavigateUp = {
viewModel.setEditTodoItem(null)
navController.navigateUp()
}
)
}
composable(TodoScreen.SettingsMain.name) { composable(TodoScreen.SettingsMain.name) {
SettingsMain( SettingsMain(
onNavigateUp = { navController.navigateUp() }, onNavigateUp = { navController.navigateUp() },

View file

@ -2,6 +2,7 @@ package cn.super12138.todo.ui.navigation
enum class TodoScreen { enum class TodoScreen {
Main, Main,
TodoEditor,
SettingsMain, SettingsMain,
SettingsAbout, SettingsAbout,
SettingsAboutLicence SettingsAboutLicence

View file

@ -1,10 +1,11 @@
package cn.super12138.todo.ui.pages.main package cn.super12138.todo.ui.pages.editor
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@ -14,11 +15,9 @@ import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme 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.Text
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
@ -26,7 +25,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -36,38 +34,32 @@ import cn.super12138.todo.logic.database.TodoEntity
import cn.super12138.todo.logic.model.Subjects import cn.super12138.todo.logic.model.Subjects
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.components.FilterChipGroup import cn.super12138.todo.ui.components.FilterChipGroup
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun TodoBottomSheet( fun TodoEditorPage(
sheetState: SheetState,
onDismissRequest: () -> Unit,
toDo: TodoEntity? = null, toDo: TodoEntity? = null,
onSave: (TodoEntity) -> Unit, onSave: (TodoEntity) -> Unit,
onClose: () -> Unit,
onDelete: () -> Unit, onDelete: () -> Unit,
onNavigateUp: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
ModalBottomSheet( LargeTopAppBarScaffold(
sheetState = sheetState, title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task),
onDismissRequest = onDismissRequest, scrollBehavior = scrollBehavior,
onBack = onNavigateUp,
modifier = modifier modifier = modifier
) { ) { innerPadding ->
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.padding(innerPadding)
.padding(horizontal = TodoDefaults.screenPadding) .padding(horizontal = TodoDefaults.screenPadding)
.fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
Text(
text = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task),
style = MaterialTheme.typography.headlineMedium
)
Spacer(Modifier.size(20.dp))
var text by rememberSaveable { mutableStateOf(toDo?.content ?: "") } var text by rememberSaveable { mutableStateOf(toDo?.content ?: "") }
var isError by rememberSaveable { mutableStateOf(false) } var isError by rememberSaveable { mutableStateOf(false) }
TextField( TextField(
@ -91,6 +83,10 @@ fun TodoBottomSheet(
} }
} }
var selectedItemIndex by rememberSaveable { mutableIntStateOf(toDo?.subject ?: 0) } var selectedItemIndex by rememberSaveable { mutableIntStateOf(toDo?.subject ?: 0) }
Text(
text = stringResource(R.string.label_subject),
style = MaterialTheme.typography.titleMedium
)
FilterChipGroup( FilterChipGroup(
items = subjects, items = subjects,
defaultSelectedItemIndex = toDo?.subject ?: 0, defaultSelectedItemIndex = toDo?.subject ?: 0,
@ -102,15 +98,12 @@ fun TodoBottomSheet(
Spacer(Modifier.size(20.dp)) Spacer(Modifier.size(20.dp))
// 操作按钮
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
OutlinedButton(onClick = onClose) {
Text(stringResource(R.string.action_cancel))
}
if (toDo !== null) { if (toDo !== null) {
FilledTonalButton( FilledTonalButton(
onClick = { onClick = {
onDelete() onDelete()
onClose()
}, },
colors = ButtonColors( colors = ButtonColors(
containerColor = MaterialTheme.colorScheme.errorContainer, containerColor = MaterialTheme.colorScheme.errorContainer,
@ -139,13 +132,11 @@ fun TodoBottomSheet(
id = toDo?.id ?: 0 id = toDo?.id ?: 0
) )
) )
onClose()
} }
) { ) {
Text(stringResource(R.string.action_save)) Text(stringResource(R.string.action_save))
} }
} }
Spacer(Modifier.size(30.dp))
} }
} }
} }

View file

@ -15,17 +15,14 @@ import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContent import androidx.compose.foundation.layout.safeContent
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
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.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.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -35,12 +32,11 @@ import cn.super12138.todo.ui.pages.main.components.TodoDeleteConfirmDialog
import cn.super12138.todo.ui.pages.main.components.TodoFAB import cn.super12138.todo.ui.pages.main.components.TodoFAB
import cn.super12138.todo.ui.pages.main.components.TodoTopAppBar import cn.super12138.todo.ui.pages.main.components.TodoTopAppBar
import cn.super12138.todo.ui.viewmodels.MainViewModel import cn.super12138.todo.ui.viewmodels.MainViewModel
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun MainPage( fun MainPage(
viewModel: MainViewModel, viewModel: MainViewModel,
toTodoEditPage: () -> Unit,
toSettingsPage: () -> Unit, toSettingsPage: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
@ -52,12 +48,8 @@ fun MainPage(
} }
} }
val selectedEditTodoItem = viewModel.selectedEditTodo
val selectedTodoIds = viewModel.selectedTodoIds.collectAsState() val selectedTodoIds = viewModel.selectedTodoIds.collectAsState()
var showBottomSheet by rememberSaveable { mutableStateOf(false) }
var showDeleteConfirmDialog by rememberSaveable { mutableStateOf(false) } var showDeleteConfirmDialog by rememberSaveable { mutableStateOf(false) }
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val scope = rememberCoroutineScope()
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
@ -95,7 +87,7 @@ fun MainPage(
) { ) {
TodoFAB( TodoFAB(
expanded = isExpanded, expanded = isExpanded,
onClick = { showBottomSheet = true } onClick = toTodoEditPage
) )
} }
}, },
@ -117,8 +109,8 @@ fun MainPage(
list = filteredTodoList, list = filteredTodoList,
onItemClick = { item -> onItemClick = { item ->
if (isSelectedIdsEmpty) { if (isSelectedIdsEmpty) {
showBottomSheet = true
viewModel.setEditTodoItem(item) viewModel.setEditTodoItem(item)
toTodoEditPage()
} else { } else {
viewModel.toggleTodoSelection(item) viewModel.toggleTodoSelection(item)
} }
@ -159,7 +151,6 @@ fun MainPage(
list = filteredTodoList, list = filteredTodoList,
onItemClick = { item -> onItemClick = { item ->
if (isSelectedIdsEmpty) { if (isSelectedIdsEmpty) {
showBottomSheet = true
viewModel.setEditTodoItem(item) viewModel.setEditTodoItem(item)
} else { } else {
viewModel.toggleTodoSelection(item) viewModel.toggleTodoSelection(item)
@ -189,32 +180,6 @@ fun MainPage(
} }
} }
} }
if (showBottomSheet) {
TodoBottomSheet(
sheetState = bottomSheetState,
onDismissRequest = {
showBottomSheet = false
viewModel.setEditTodoItem(null)
},
toDo = selectedEditTodoItem,
onSave = { toDo ->
viewModel.addTodo(toDo)
},
onClose = {
viewModel.setEditTodoItem(null)
scope
.launch { bottomSheetState.hide() }
.invokeOnCompletion {
if (!bottomSheetState.isVisible) {
showBottomSheet = false
}
}
},
onDelete = {
if (selectedEditTodoItem !== null) viewModel.deleteTodo(selectedEditTodoItem)
}
)
}
if (showDeleteConfirmDialog) { if (showDeleteConfirmDialog) {
TodoDeleteConfirmDialog( TodoDeleteConfirmDialog(
onConfirm = { onConfirm = {

View file

@ -39,4 +39,5 @@
<string name="pref_developer">开发者</string> <string name="pref_developer">开发者</string>
<string name="pref_licence">开放源代码许可</string> <string name="pref_licence">开放源代码许可</string>
<string name="pref_licence_desc">查看应用使用的开源库及其许可</string> <string name="pref_licence_desc">查看应用使用的开源库及其许可</string>
<string name="label_subject">学科</string>
</resources> </resources>

View file

@ -40,4 +40,5 @@
<string name="pref_developer">Developer</string> <string name="pref_developer">Developer</string>
<string name="pref_licence">Open Source Licences</string> <string name="pref_licence">Open Source Licences</string>
<string name="pref_licence_desc">Check the open source libraries used by the application and their licences.</string> <string name="pref_licence_desc">Check the open source libraries used by the application and their licences.</string>
<string name="label_subject">Subject</string>
</resources> </resources>