feat: 使用导航方式2 & 添加转场动画
This commit is contained in:
parent
4c95c7e26e
commit
2b28b446eb
25 changed files with 413 additions and 307 deletions
|
|
@ -38,7 +38,7 @@ android {
|
||||||
applicationId = "cn.super12138.todo"
|
applicationId = "cn.super12138.todo"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 941
|
versionCode = 950
|
||||||
versionName = "2.3.3"
|
versionName = "2.3.3"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ object Constants {
|
||||||
const val GITHUB_REPO = "https://github.com/Super12138/ToDo/"
|
const val GITHUB_REPO = "https://github.com/Super12138/ToDo/"
|
||||||
|
|
||||||
const val KEY_TODO_FAB_TRANSITION = "todo_fab"
|
const val KEY_TODO_FAB_TRANSITION = "todo_fab"
|
||||||
// const val KEY_TODO_CONTENT_TRANSITION = "todo_content"
|
const val KEY_TODO_ITEM_TRANSITION = "todo_item_id"
|
||||||
// const val KEY_TODO_CATEGORY_TRANSITION = "todo_category"
|
|
||||||
|
|
||||||
const val DB_NAME = "todo"
|
const val DB_NAME = "todo"
|
||||||
const val DB_TABLE_NAME = "todo"
|
const val DB_TABLE_NAME = "todo"
|
||||||
|
|
|
||||||
18
app/src/main/kotlin/cn/super12138/todo/logic/IRepository.kt
Normal file
18
app/src/main/kotlin/cn/super12138/todo/logic/IRepository.kt
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package cn.super12138.todo.logic
|
||||||
|
|
||||||
|
import cn.super12138.todo.logic.database.TodoEntity
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface IRepository {
|
||||||
|
suspend fun insertTodo(toDo: TodoEntity)
|
||||||
|
|
||||||
|
fun getAllTodos(): Flow<List<TodoEntity>>
|
||||||
|
|
||||||
|
suspend fun updateTodo(toDo: TodoEntity)
|
||||||
|
|
||||||
|
suspend fun deleteTodo(toDo: TodoEntity)
|
||||||
|
|
||||||
|
suspend fun deleteTodoFromIds(toDoItems: List<Int>)
|
||||||
|
|
||||||
|
// suspend fun deleteAllTodo()
|
||||||
|
}
|
||||||
|
|
@ -4,29 +4,29 @@ import cn.super12138.todo.TodoApp
|
||||||
import cn.super12138.todo.logic.database.TodoEntity
|
import cn.super12138.todo.logic.database.TodoEntity
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
object Repository {
|
object Repository : IRepository {
|
||||||
private val db get() = TodoApp.db
|
private val db get() = TodoApp.db
|
||||||
private val toDoDao = db.toDoDao()
|
private val toDoDao = db.toDoDao()
|
||||||
|
|
||||||
suspend fun insertTodo(toDo: TodoEntity) {
|
override suspend fun insertTodo(toDo: TodoEntity) {
|
||||||
toDoDao.insert(toDo)
|
toDoDao.insert(toDo)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllTodos(): Flow<List<TodoEntity>> = toDoDao.getAll()
|
override fun getAllTodos(): Flow<List<TodoEntity>> = toDoDao.getAll()
|
||||||
|
|
||||||
suspend fun updateTodo(toDo: TodoEntity) {
|
override suspend fun updateTodo(toDo: TodoEntity) {
|
||||||
toDoDao.update(toDo)
|
toDoDao.update(toDo)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteTodo(toDo: TodoEntity) {
|
override suspend fun deleteTodo(toDo: TodoEntity) {
|
||||||
toDoDao.delete(toDo)
|
toDoDao.delete(toDo)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteTodoFromIds(toDoItems: List<Int>) {
|
override suspend fun deleteTodoFromIds(toDoItems: List<Int>) {
|
||||||
toDoDao.deleteFromIds(toDoItems.toSet())
|
toDoDao.deleteFromIds(toDoItems.toSet())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*suspend fun deleteAllTodo() {
|
/*override suspend fun deleteAllTodo() {
|
||||||
toDoDao.deleteAllTodo()
|
toDoDao.deleteAllTodo()
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ import cn.super12138.todo.R
|
||||||
|
|
||||||
enum class ContrastLevel(
|
enum class ContrastLevel(
|
||||||
val value: Float,
|
val value: Float,
|
||||||
@StringRes val nameRes: Int
|
@param:StringRes val nameRes: Int
|
||||||
) {
|
) {
|
||||||
VeryLow(value = -1f, nameRes = R.string.contrast_very_low),
|
VeryLow(value = -1f, nameRes = R.string.contrast_very_low),
|
||||||
Low(value = -0.5f, nameRes = R.string.contrast_low),
|
Low(value = -0.5f, nameRes = R.string.contrast_low),
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import cn.super12138.todo.R
|
||||||
|
|
||||||
enum class DarkMode(
|
enum class DarkMode(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@DrawableRes val iconRes: Int,
|
@param:DrawableRes val iconRes: Int,
|
||||||
@StringRes val nameRes: Int
|
@param:StringRes val nameRes: Int
|
||||||
) {
|
) {
|
||||||
FollowSystem(id = -1, iconRes = R.drawable.ic_lightbulb_2, nameRes = R.string.dark_mode_system),
|
FollowSystem(id = -1, iconRes = R.drawable.ic_lightbulb_2, nameRes = R.string.dark_mode_system),
|
||||||
Light(id = 1, iconRes = R.drawable.ic_light_mode, nameRes = R.string.dark_mode_light),
|
Light(id = 1, iconRes = R.drawable.ic_light_mode, nameRes = R.string.dark_mode_light),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import cn.super12138.todo.R
|
||||||
|
|
||||||
enum class PaletteStyle(
|
enum class PaletteStyle(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@StringRes val nameRes: Int
|
@param:StringRes val nameRes: Int
|
||||||
) {
|
) {
|
||||||
TonalSpot(id = 1, nameRes = R.string.palette_tonal_spot),
|
TonalSpot(id = 1, nameRes = R.string.palette_tonal_spot),
|
||||||
Neutral(id = 2, nameRes = R.string.palette_neutral),
|
Neutral(id = 2, nameRes = R.string.palette_neutral),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import cn.super12138.todo.R
|
||||||
|
|
||||||
enum class Priority(
|
enum class Priority(
|
||||||
val value: Float,
|
val value: Float,
|
||||||
@StringRes val nameRes: Int
|
@param:StringRes val nameRes: Int
|
||||||
) {
|
) {
|
||||||
Urgent(value = 2f, nameRes = R.string.priority_urgent),
|
Urgent(value = 2f, nameRes = R.string.priority_urgent),
|
||||||
Important(value = 1f, nameRes = R.string.priority_important),
|
Important(value = 1f, nameRes = R.string.priority_important),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import cn.super12138.todo.R
|
||||||
|
|
||||||
enum class SortingMethod(
|
enum class SortingMethod(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
@StringRes val nameRes: Int
|
@param:StringRes val nameRes: Int
|
||||||
) {
|
) {
|
||||||
// 按添加先后顺序
|
// 按添加先后顺序
|
||||||
Sequential(id = 1, nameRes = R.string.sorting_sequential),
|
Sequential(id = 1, nameRes = R.string.sorting_sequential),
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ object TodoDefaults {
|
||||||
/**
|
/**
|
||||||
* 屏幕上下预留边距(防止内容全部贴边显示过丑)
|
* 屏幕上下预留边距(防止内容全部贴边显示过丑)
|
||||||
*/
|
*/
|
||||||
val screenVerticalPadding = 16.dp
|
val screenVerticalPadding = 8.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 待办卡片默认高度
|
* 待办卡片默认高度
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.os.Bundle
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
|
@ -26,7 +27,7 @@ import cn.super12138.todo.logic.model.PaletteStyle
|
||||||
import cn.super12138.todo.ui.TodoDefaults
|
import cn.super12138.todo.ui.TodoDefaults
|
||||||
import cn.super12138.todo.ui.components.Konfetti
|
import cn.super12138.todo.ui.components.Konfetti
|
||||||
import cn.super12138.todo.ui.navigation.TodoDestinations
|
import cn.super12138.todo.ui.navigation.TodoDestinations
|
||||||
import cn.super12138.todo.ui.navigation.TodoNavigation
|
import cn.super12138.todo.ui.navigation.TopNavigation
|
||||||
import cn.super12138.todo.ui.theme.ToDoTheme
|
import cn.super12138.todo.ui.theme.ToDoTheme
|
||||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||||
import cn.super12138.todo.utils.VibrationUtils
|
import cn.super12138.todo.utils.VibrationUtils
|
||||||
|
|
@ -39,7 +40,7 @@ class MainActivity : ComponentActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContent {
|
setContent {
|
||||||
val mainViewModel: MainViewModel = viewModel()
|
val mainViewModel: MainViewModel = viewModel()
|
||||||
val navigationBackStack = mainViewModel.topLevelBackStack
|
val mainBackStack = mainViewModel.mainBackStack
|
||||||
val showConfetti = mainViewModel.showConfetti
|
val showConfetti = mainViewModel.showConfetti
|
||||||
// 主题
|
// 主题
|
||||||
val dynamicColor by DataStoreManager.dynamicColorFlow.collectAsState(initial = Constants.PREF_DYNAMIC_COLOR_DEFAULT)
|
val dynamicColor by DataStoreManager.dynamicColorFlow.collectAsState(initial = Constants.PREF_DYNAMIC_COLOR_DEFAULT)
|
||||||
|
|
@ -89,27 +90,34 @@ class MainActivity : ComponentActivity() {
|
||||||
Surface(color = TodoDefaults.BackgroundColor) {
|
Surface(color = TodoDefaults.BackgroundColor) {
|
||||||
NavigationSuiteScaffold(
|
NavigationSuiteScaffold(
|
||||||
navigationSuiteItems = {
|
navigationSuiteItems = {
|
||||||
TodoDestinations.entries.forEach {
|
TodoDestinations.entries.forEach { destination ->
|
||||||
val selected = it.route == navigationBackStack.topLevelKey
|
val selected = destination.route == mainBackStack.topLevelKey
|
||||||
item(
|
item(
|
||||||
icon = {
|
icon = {
|
||||||
Icon(
|
Crossfade(selected) {
|
||||||
painter = if (selected) painterResource(it.selectedIcon) else painterResource(
|
if (it) {
|
||||||
it.icon
|
Icon(
|
||||||
),
|
painter = painterResource(destination.selectedIcon),
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(destination.icon),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
label = { Text(stringResource(it.label)) },
|
label = { Text(stringResource(destination.label)) },
|
||||||
selected = selected,
|
selected = selected,
|
||||||
onClick = { navigationBackStack.addTopLevel(it.route) }
|
onClick = { mainBackStack.addTopLevel(destination.route) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
containerColor = TodoDefaults.BackgroundColor
|
containerColor = TodoDefaults.BackgroundColor
|
||||||
) {
|
) {
|
||||||
TodoNavigation(
|
TopNavigation(
|
||||||
backStack = navigationBackStack,
|
backStack = mainBackStack,
|
||||||
viewModel = mainViewModel,
|
viewModel = mainViewModel,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,105 @@
|
||||||
package cn.super12138.todo.ui.navigation
|
package cn.super12138.todo.ui.navigation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.navigation3.runtime.NavBackStack
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
|
import androidx.navigation3.runtime.entryProvider
|
||||||
|
import androidx.navigation3.ui.NavDisplay
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsAbout
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsAboutLicence
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsAppearance
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsData
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsDataCategory
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsDeveloperOptions
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsDeveloperOptionsPadding
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsInterface
|
||||||
|
import cn.super12138.todo.ui.pages.settings.SettingsMain
|
||||||
|
import cn.super12138.todo.ui.theme.materialSharedAxisX
|
||||||
|
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||||
|
|
||||||
|
private const val INITIAL_OFFSET_FACTOR = 0.10f
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingsNavigation(
|
||||||
|
backStack: NavBackStack<NavKey>,
|
||||||
|
viewModel: MainViewModel,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
fun onBack() {
|
||||||
|
backStack.removeLastOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
NavDisplay(
|
||||||
|
backStack = backStack,
|
||||||
|
onBack = ::onBack,
|
||||||
|
transitionSpec = {
|
||||||
|
materialSharedAxisX(
|
||||||
|
initialOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() },
|
||||||
|
targetOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() })
|
||||||
|
},
|
||||||
|
popTransitionSpec = {
|
||||||
|
materialSharedAxisX(
|
||||||
|
initialOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() },
|
||||||
|
targetOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() })
|
||||||
|
},
|
||||||
|
entryProvider = entryProvider {
|
||||||
|
entry<TodoScreen.Settings.Main> {
|
||||||
|
SettingsMain(
|
||||||
|
toAppearancePage = { backStack.add(TodoScreen.Settings.Appearance) },
|
||||||
|
toAboutPage = { backStack.add(TodoScreen.Settings.About) },
|
||||||
|
toInterfacePage = { backStack.add(TodoScreen.Settings.Interface) },
|
||||||
|
toDataPage = { backStack.add(TodoScreen.Settings.Data) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Settings.Appearance> {
|
||||||
|
SettingsAppearance(onNavigateUp = ::onBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Settings.Interface> {
|
||||||
|
SettingsInterface(onNavigateUp = ::onBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Settings.Data> {
|
||||||
|
SettingsData(
|
||||||
|
viewModel = viewModel,
|
||||||
|
toCategoryManager = { backStack.add(TodoScreen.Settings.DataCategory) },
|
||||||
|
onNavigateUp = ::onBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Settings.DataCategory> {
|
||||||
|
SettingsDataCategory(onNavigateUp = ::onBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Settings.About> {
|
||||||
|
SettingsAbout(
|
||||||
|
//toSpecialPage = { backStack.add(TodoScreen.Settings.AboutSpecial) },
|
||||||
|
toLicencePage = { backStack.add(TodoScreen.Settings.AboutLicence) },
|
||||||
|
toDevPage = { backStack.add(TodoScreen.Settings.DeveloperOptions) },
|
||||||
|
onNavigateUp = ::onBack,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*entry<TodoScreen.Settings.AboutSpecial> {
|
||||||
|
SettingsAboutSpecial(viewModel = viewModel)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
entry<TodoScreen.Settings.AboutLicence> {
|
||||||
|
SettingsAboutLicence(onNavigateUp = ::onBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Settings.DeveloperOptions> {
|
||||||
|
SettingsDeveloperOptions(
|
||||||
|
toPaddingPage = { backStack.add(TodoScreen.Settings.DeveloperOptionsPadding) },
|
||||||
|
onNavigateUp = ::onBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
entry<TodoScreen.Settings.DeveloperOptionsPadding> {
|
||||||
|
SettingsDeveloperOptionsPadding(onNavigateUp = ::onBack)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -6,9 +6,9 @@ import androidx.navigation3.runtime.NavKey
|
||||||
|
|
||||||
enum class TodoDestinations(
|
enum class TodoDestinations(
|
||||||
val route: NavKey,
|
val route: NavKey,
|
||||||
@StringRes val label: Int,
|
@param:StringRes val label: Int,
|
||||||
@DrawableRes val icon: Int,
|
@param:DrawableRes val icon: Int,
|
||||||
@DrawableRes val selectedIcon: Int
|
@param:DrawableRes val selectedIcon: Int
|
||||||
) {
|
) {
|
||||||
Overview(
|
Overview(
|
||||||
route = TodoScreen.Overview,
|
route = TodoScreen.Overview,
|
||||||
|
|
@ -23,7 +23,7 @@ enum class TodoDestinations(
|
||||||
selectedIcon = cn.super12138.todo.R.drawable.ic_ballot_filled
|
selectedIcon = cn.super12138.todo.R.drawable.ic_ballot_filled
|
||||||
),
|
),
|
||||||
Settings(
|
Settings(
|
||||||
route = TodoScreen.Settings,
|
route = TodoScreen.Settings.Main,
|
||||||
label = cn.super12138.todo.R.string.page_settings,
|
label = cn.super12138.todo.R.string.page_settings,
|
||||||
icon = cn.super12138.todo.R.drawable.ic_settings,
|
icon = cn.super12138.todo.R.drawable.ic_settings,
|
||||||
selectedIcon = cn.super12138.todo.R.drawable.ic_settings_filled
|
selectedIcon = cn.super12138.todo.R.drawable.ic_settings_filled
|
||||||
|
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
package cn.super12138.todo.ui.navigation
|
|
||||||
|
|
||||||
import androidx.compose.animation.EnterTransition
|
|
||||||
import androidx.compose.animation.ExitTransition
|
|
||||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
|
||||||
import androidx.compose.animation.SharedTransitionLayout
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.slideInVertically
|
|
||||||
import androidx.compose.animation.slideOutVertically
|
|
||||||
import androidx.compose.animation.togetherWith
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.navigation3.runtime.NavKey
|
|
||||||
import androidx.navigation3.runtime.entryProvider
|
|
||||||
import androidx.navigation3.ui.NavDisplay
|
|
||||||
import cn.super12138.todo.ui.pages.editor.TodoEditorPage
|
|
||||||
import cn.super12138.todo.ui.pages.main.MainPage
|
|
||||||
import cn.super12138.todo.ui.pages.overview.OverviewPage
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsAbout
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsAboutLicence
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsAppearance
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsData
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsDataCategory
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsDeveloperOptions
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsDeveloperOptionsPadding
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsInterface
|
|
||||||
import cn.super12138.todo.ui.pages.settings.SettingsMain
|
|
||||||
import cn.super12138.todo.ui.theme.materialSharedAxisX
|
|
||||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
|
||||||
|
|
||||||
private const val INITIAL_OFFSET_FACTOR = 0.10f
|
|
||||||
|
|
||||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
|
||||||
@Composable
|
|
||||||
fun TodoNavigation(
|
|
||||||
backStack: TopLevelBackStack<NavKey>,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
viewModel: MainViewModel
|
|
||||||
) {
|
|
||||||
fun onBack() {
|
|
||||||
backStack.removeLast()
|
|
||||||
}
|
|
||||||
|
|
||||||
val anim = NavDisplay.transitionSpec {
|
|
||||||
// Slide new content up, keeping the old content in place underneath
|
|
||||||
slideInVertically(
|
|
||||||
initialOffsetY = { it / 5 },
|
|
||||||
animationSpec = tween(200)
|
|
||||||
) + fadeIn(tween(200)) togetherWith ExitTransition.KeepUntilTransitionsFinished
|
|
||||||
} + NavDisplay.popTransitionSpec {
|
|
||||||
// Slide old content down, revealing the new content in place underneath
|
|
||||||
EnterTransition.None togetherWith
|
|
||||||
slideOutVertically(
|
|
||||||
targetOffsetY = { it / 5 },
|
|
||||||
animationSpec = tween(200)
|
|
||||||
) + fadeOut(tween(200))
|
|
||||||
} + NavDisplay.predictivePopTransitionSpec {
|
|
||||||
// Slide old content down, revealing the new content in place underneath
|
|
||||||
EnterTransition.None togetherWith
|
|
||||||
slideOutVertically(
|
|
||||||
targetOffsetY = { it / 5 },
|
|
||||||
animationSpec = tween(200)
|
|
||||||
) + fadeOut(tween(200))
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedTransitionLayout {
|
|
||||||
NavDisplay(
|
|
||||||
backStack = backStack.backStack,
|
|
||||||
onBack = ::onBack,
|
|
||||||
transitionSpec = {
|
|
||||||
materialSharedAxisX(
|
|
||||||
initialOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() },
|
|
||||||
targetOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() })
|
|
||||||
},
|
|
||||||
popTransitionSpec = {
|
|
||||||
materialSharedAxisX(
|
|
||||||
initialOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() },
|
|
||||||
targetOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() })
|
|
||||||
},
|
|
||||||
entryProvider = entryProvider {
|
|
||||||
entry<TodoScreen.Overview>(metadata = anim) {
|
|
||||||
OverviewPage()
|
|
||||||
}
|
|
||||||
entry<TodoScreen.Tasks>(metadata = anim) {
|
|
||||||
MainPage(
|
|
||||||
viewModel = viewModel,
|
|
||||||
toTodoEditPage = { backStack.add(TodoScreen.Editor(it)) },
|
|
||||||
sharedTransitionScope = this@SharedTransitionLayout
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<TodoScreen.Settings>(metadata = anim) {
|
|
||||||
SettingsMain(
|
|
||||||
toAppearancePage = { backStack.add(TodoScreen.SettingsAppearance) },
|
|
||||||
toAboutPage = { backStack.add(TodoScreen.SettingsAbout) },
|
|
||||||
toInterfacePage = { backStack.add(TodoScreen.SettingsInterface) },
|
|
||||||
toDataPage = { backStack.add(TodoScreen.SettingsData) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<TodoScreen.Editor> { editorArgs ->
|
|
||||||
TodoEditorPage(
|
|
||||||
toDo = editorArgs.toDo,
|
|
||||||
onSave = {
|
|
||||||
viewModel.addTodo(it)
|
|
||||||
// 如果原来的待办状态为未完成并且修改后状态为完成
|
|
||||||
if (editorArgs.toDo?.isCompleted != true && it.isCompleted) {
|
|
||||||
viewModel.playConfetti()
|
|
||||||
}
|
|
||||||
onBack()
|
|
||||||
},
|
|
||||||
onDelete = {
|
|
||||||
if (editorArgs.toDo !== null) {
|
|
||||||
viewModel.deleteTodo(editorArgs.toDo)
|
|
||||||
}
|
|
||||||
onBack()
|
|
||||||
},
|
|
||||||
onNavigateUp = ::onBack,
|
|
||||||
sharedTransitionScope = this@SharedTransitionLayout
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<TodoScreen.SettingsAppearance> {
|
|
||||||
SettingsAppearance(onNavigateUp = ::onBack)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<TodoScreen.SettingsInterface> {
|
|
||||||
SettingsInterface(onNavigateUp = ::onBack)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<TodoScreen.SettingsData> {
|
|
||||||
SettingsData(
|
|
||||||
viewModel = viewModel,
|
|
||||||
toCategoryManager = { backStack.add(TodoScreen.SettingsDataCategory) },
|
|
||||||
onNavigateUp = ::onBack
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<TodoScreen.SettingsDataCategory> {
|
|
||||||
SettingsDataCategory(onNavigateUp = ::onBack)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<TodoScreen.SettingsAbout> {
|
|
||||||
SettingsAbout(
|
|
||||||
//toSpecialPage = { backStack.add(TodoScreen.SettingsAboutSpecial) },
|
|
||||||
toLicencePage = { backStack.add(TodoScreen.SettingsAboutLicence) },
|
|
||||||
toDevPage = { backStack.add(TodoScreen.SettingsDev) },
|
|
||||||
onNavigateUp = ::onBack,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*entry<TodoScreen.SettingsAboutSpecial> {
|
|
||||||
SettingsAboutSpecial(viewModel = viewModel)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
entry<TodoScreen.SettingsAboutLicence> {
|
|
||||||
SettingsAboutLicence(onNavigateUp = ::onBack)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry<TodoScreen.SettingsDev> {
|
|
||||||
SettingsDeveloperOptions(
|
|
||||||
toPaddingPage = { backStack.add(TodoScreen.SettingsDevPadding) },
|
|
||||||
onNavigateUp = ::onBack
|
|
||||||
)
|
|
||||||
}
|
|
||||||
entry<TodoScreen.SettingsDevPadding> {
|
|
||||||
SettingsDeveloperOptionsPadding(onNavigateUp = ::onBack)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = modifier,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,35 +13,44 @@ sealed class TodoScreen : NavKey {
|
||||||
data object Tasks : TodoScreen()
|
data object Tasks : TodoScreen()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Editor(val toDo: TodoEntity?) : TodoScreen()
|
sealed class Settings : TodoScreen() {
|
||||||
|
@Serializable
|
||||||
|
data object Main : Settings()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object Appearance : Settings()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object Interface : Settings()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object Data : Settings()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object DataCategory : Settings()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object About : Settings()
|
||||||
|
|
||||||
|
// @Serializable
|
||||||
|
// data object AboutEasterEgg : Settings()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object AboutLicence : Settings()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object DeveloperOptions : Settings()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object DeveloperOptionsPadding : Settings()
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Settings : TodoScreen()
|
sealed class Editor : TodoScreen() {
|
||||||
|
@Serializable
|
||||||
|
data object Add : Editor()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object SettingsAppearance : TodoScreen()
|
data class Edit(val toDo: TodoEntity) : Editor()
|
||||||
|
}
|
||||||
@Serializable
|
|
||||||
data object SettingsInterface : TodoScreen()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data object SettingsData : TodoScreen()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data object SettingsDataCategory : TodoScreen()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data object SettingsAbout : TodoScreen()
|
|
||||||
|
|
||||||
// @Serializable
|
|
||||||
// data object SettingsAboutSpecial : TodoScreen()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data object SettingsAboutLicence : TodoScreen()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data object SettingsDev : TodoScreen()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data object SettingsDevPadding : TodoScreen()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package cn.super12138.todo.ui.navigation
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.animation.SharedTransitionLayout
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.togetherWith
|
||||||
|
import androidx.compose.animation.unveilIn
|
||||||
|
import androidx.compose.animation.veilOut
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
|
import androidx.navigation3.runtime.entryProvider
|
||||||
|
import androidx.navigation3.ui.NavDisplay
|
||||||
|
import cn.super12138.todo.ui.pages.editor.TodoAddPage
|
||||||
|
import cn.super12138.todo.ui.pages.editor.TodoEditPage
|
||||||
|
import cn.super12138.todo.ui.pages.overview.OverviewPage
|
||||||
|
import cn.super12138.todo.ui.pages.tasks.TasksPage
|
||||||
|
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun TopNavigation(
|
||||||
|
backStack: TopLevelBackStack<NavKey>,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: MainViewModel
|
||||||
|
) {
|
||||||
|
fun onBack() {
|
||||||
|
backStack.removeLast()
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* NavDisplay.transitionSpec {
|
||||||
|
* // Slide new content up, keeping the old content in place underneath
|
||||||
|
* slideInVertically(
|
||||||
|
* initialOffsetY = { it / 5 },
|
||||||
|
* animationSpec = tween(200)
|
||||||
|
* ) + fadeIn(tween(200)) togetherWith ExitTransition.KeepUntilTransitionsFinished
|
||||||
|
* } + NavDisplay.popTransitionSpec {
|
||||||
|
* // Slide old content down, revealing the new content in place underneath
|
||||||
|
* EnterTransition.None togetherWith
|
||||||
|
* slideOutVertically(
|
||||||
|
* targetOffsetY = { it / 5 },
|
||||||
|
* animationSpec = tween(200)
|
||||||
|
* ) + fadeOut(tween(200))
|
||||||
|
* } + NavDisplay.predictivePopTransitionSpec {
|
||||||
|
* // Slide old content down, revealing the new content in place underneath
|
||||||
|
* EnterTransition.None togetherWith
|
||||||
|
* slideOutVertically(
|
||||||
|
* targetOffsetY = { it / 5 },
|
||||||
|
* animationSpec = tween(200)
|
||||||
|
* ) + fadeOut(tween(200))
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
val veilColor = MaterialTheme.colorScheme.surfaceDim
|
||||||
|
SharedTransitionLayout {
|
||||||
|
NavDisplay(
|
||||||
|
backStack = backStack.backStack,
|
||||||
|
onBack = ::onBack,
|
||||||
|
transitionSpec = {
|
||||||
|
fadeIn() togetherWith veilOut(targetColor = veilColor)
|
||||||
|
},
|
||||||
|
popTransitionSpec = {
|
||||||
|
unveilIn(initialColor = veilColor) togetherWith fadeOut()
|
||||||
|
},
|
||||||
|
predictivePopTransitionSpec = {
|
||||||
|
unveilIn(initialColor = veilColor) togetherWith fadeOut()
|
||||||
|
},
|
||||||
|
entryProvider = entryProvider {
|
||||||
|
entry<TodoScreen.Overview> {
|
||||||
|
OverviewPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Tasks> {
|
||||||
|
TasksPage(
|
||||||
|
viewModel = viewModel,
|
||||||
|
toTodoAddPage = { backStack.add(TodoScreen.Editor.Add) },
|
||||||
|
toTodoEditPage = { backStack.add(TodoScreen.Editor.Edit(it)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Editor.Add> {
|
||||||
|
TodoAddPage(
|
||||||
|
onSave = {
|
||||||
|
viewModel.addTodo(it)
|
||||||
|
onBack()
|
||||||
|
},
|
||||||
|
onNavigateUp = ::onBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Editor.Edit> { editorArgs ->
|
||||||
|
TodoEditPage(
|
||||||
|
toDo = editorArgs.toDo,
|
||||||
|
onSave = {
|
||||||
|
viewModel.addTodo(it)
|
||||||
|
// 如果原来的待办状态为未完成并且修改后状态为完成
|
||||||
|
if (!editorArgs.toDo.isCompleted && it.isCompleted) {
|
||||||
|
viewModel.playConfetti()
|
||||||
|
}
|
||||||
|
onBack()
|
||||||
|
},
|
||||||
|
onDelete = {
|
||||||
|
viewModel.deleteTodo(editorArgs.toDo)
|
||||||
|
onBack()
|
||||||
|
},
|
||||||
|
onNavigateUp = ::onBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry<TodoScreen.Settings.Main> {
|
||||||
|
SettingsNavigation(
|
||||||
|
backStack = viewModel.settingsBackStack,
|
||||||
|
viewModel = viewModel
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,6 @@ package cn.super12138.todo.ui.pages.editor
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
|
||||||
import androidx.compose.animation.SharedTransitionScope
|
import androidx.compose.animation.SharedTransitionScope
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
|
|
@ -17,12 +16,9 @@ import androidx.compose.foundation.layout.imePadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
|
@ -41,6 +37,7 @@ import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.constants.Constants
|
import cn.super12138.todo.constants.Constants
|
||||||
import cn.super12138.todo.logic.database.TodoEntity
|
import cn.super12138.todo.logic.database.TodoEntity
|
||||||
import cn.super12138.todo.logic.datastore.DataStoreManager
|
import cn.super12138.todo.logic.datastore.DataStoreManager
|
||||||
|
import cn.super12138.todo.ui.TodoDefaults
|
||||||
import cn.super12138.todo.ui.components.ChipItem
|
import cn.super12138.todo.ui.components.ChipItem
|
||||||
import cn.super12138.todo.ui.components.ConfirmDialog
|
import cn.super12138.todo.ui.components.ConfirmDialog
|
||||||
import cn.super12138.todo.ui.components.TodoFloatingActionButton
|
import cn.super12138.todo.ui.components.TodoFloatingActionButton
|
||||||
|
|
@ -52,23 +49,50 @@ import cn.super12138.todo.ui.pages.editor.components.TodoPrioritySlider
|
||||||
import cn.super12138.todo.ui.pages.editor.state.rememberEditorState
|
import cn.super12138.todo.ui.pages.editor.state.rememberEditorState
|
||||||
import cn.super12138.todo.utils.VibrationUtils
|
import cn.super12138.todo.utils.VibrationUtils
|
||||||
|
|
||||||
@OptIn(
|
|
||||||
ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class,
|
|
||||||
ExperimentalMaterial3ExpressiveApi::class
|
|
||||||
)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TodoEditorPage(
|
fun SharedTransitionScope.TodoAddPage(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onSave: (TodoEntity) -> Unit,
|
||||||
|
onNavigateUp: () -> Unit
|
||||||
|
) = TodoEditorPage(
|
||||||
|
toDo = null,
|
||||||
|
modifier = modifier.sharedBounds(
|
||||||
|
sharedContentState = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
||||||
|
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||||
|
),
|
||||||
|
onSave = onSave,
|
||||||
|
onDelete = {},
|
||||||
|
onNavigateUp = onNavigateUp
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SharedTransitionScope.TodoEditPage(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
toDo: TodoEntity,
|
||||||
|
onSave: (TodoEntity) -> Unit,
|
||||||
|
onDelete: () -> Unit,
|
||||||
|
onNavigateUp: () -> Unit
|
||||||
|
) = TodoEditorPage(
|
||||||
|
toDo = toDo,
|
||||||
|
modifier = modifier.sharedBounds(
|
||||||
|
sharedContentState = rememberSharedContentState(key = "${Constants.KEY_TODO_ITEM_TRANSITION}_${toDo.id}"),
|
||||||
|
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||||
|
),
|
||||||
|
onSave = onSave,
|
||||||
|
onDelete = onDelete,
|
||||||
|
onNavigateUp = onNavigateUp
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TodoEditorPage(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
toDo: TodoEntity? = null,
|
toDo: TodoEntity? = null,
|
||||||
onSave: (TodoEntity) -> Unit,
|
onSave: (TodoEntity) -> Unit,
|
||||||
onDelete: () -> Unit,
|
onDelete: () -> Unit,
|
||||||
onNavigateUp: () -> Unit,
|
onNavigateUp: () -> Unit
|
||||||
sharedTransitionScope: SharedTransitionScope
|
|
||||||
) {
|
) {
|
||||||
// TODO: 本页及其相关组件重组性能检查优化
|
// TODO: 本页及其相关组件重组性能检查优化
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
val animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
|
||||||
TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
|
||||||
|
|
||||||
val uiState = rememberEditorState(initialTodo = toDo)
|
val uiState = rememberEditorState(initialTodo = toDo)
|
||||||
|
|
||||||
|
|
@ -114,7 +138,6 @@ fun TodoEditorPage(
|
||||||
TopAppBarScaffold(
|
TopAppBarScaffold(
|
||||||
title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task),
|
title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task),
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
with(sharedTransitionScope) {
|
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
modifier = Modifier.imePadding()
|
modifier = Modifier.imePadding()
|
||||||
|
|
@ -146,15 +169,9 @@ fun TodoEditorPage(
|
||||||
)
|
)
|
||||||
onSave(newTodo)
|
onSave(newTodo)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
modifier = Modifier
|
|
||||||
.sharedElement(
|
|
||||||
sharedContentState = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
|
||||||
animatedVisibilityScope = animatedVisibilityScope
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onBack = ::checkModifiedBeforeBack,
|
onBack = ::checkModifiedBeforeBack,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
@ -163,6 +180,10 @@ fun TodoEditorPage(
|
||||||
verticalArrangement = Arrangement.spacedBy(5.dp),
|
verticalArrangement = Arrangement.spacedBy(5.dp),
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
|
item {
|
||||||
|
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
||||||
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
TodoContentTextField(
|
TodoContentTextField(
|
||||||
value = uiState.toDoContent,
|
value = uiState.toDoContent,
|
||||||
|
|
@ -241,7 +262,7 @@ fun TodoEditorPage(
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.size(80.dp))
|
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.super12138.todo.ui.pages.overview
|
package cn.super12138.todo.ui.pages.overview
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -11,13 +12,14 @@ import androidx.compose.ui.res.stringResource
|
||||||
import cn.super12138.todo.R
|
import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.ui.components.TopAppBarScaffold
|
import cn.super12138.todo.ui.components.TopAppBarScaffold
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun OverviewPage(modifier: Modifier = Modifier) {
|
fun OverviewPage(modifier: Modifier = Modifier) {
|
||||||
TopAppBarScaffold(
|
TopAppBarScaffold(
|
||||||
title = stringResource(R.string.page_overview),
|
title = stringResource(R.string.page_overview),
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
Row {
|
Column {
|
||||||
Card {
|
Card {
|
||||||
Icon(painter = painterResource(R.drawable.ic_ballot), contentDescription = null)
|
Icon(painter = painterResource(R.drawable.ic_ballot), contentDescription = null)
|
||||||
Text("总任务")
|
Text("总任务")
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ fun SettingsCategory(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(
|
.padding(
|
||||||
top = if (first) 0.dp else TodoDefaults.settingsItemVerticalPadding,
|
top = if (first) 0.dp else TodoDefaults.screenVerticalPadding,
|
||||||
start = TodoDefaults.settingsItemHorizontalPadding / 2,
|
start = TodoDefaults.screenHorizontalPadding / 2,
|
||||||
end = TodoDefaults.settingsItemHorizontalPadding / 2,
|
end = TodoDefaults.screenHorizontalPadding / 2,
|
||||||
bottom = TodoDefaults.settingsItemVerticalPadding / 2
|
bottom = TodoDefaults.screenVerticalPadding
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,13 @@ fun SettingsContainer(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding))
|
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
||||||
}
|
}
|
||||||
|
|
||||||
content()
|
content()
|
||||||
|
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding))
|
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.super12138.todo.ui.pages.main
|
package cn.super12138.todo.ui.pages.tasks
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.super12138.todo.ui.pages.main
|
package cn.super12138.todo.ui.pages.tasks
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.SharedTransitionScope
|
import androidx.compose.animation.SharedTransitionScope
|
||||||
|
|
@ -38,17 +38,17 @@ import cn.super12138.todo.ui.TodoDefaults
|
||||||
import cn.super12138.todo.ui.components.ConfirmDialog
|
import cn.super12138.todo.ui.components.ConfirmDialog
|
||||||
import cn.super12138.todo.ui.components.TodoFloatingActionButton
|
import cn.super12138.todo.ui.components.TodoFloatingActionButton
|
||||||
import cn.super12138.todo.ui.components.TopAppBarScaffold
|
import cn.super12138.todo.ui.components.TopAppBarScaffold
|
||||||
import cn.super12138.todo.ui.pages.main.components.TodoCard
|
import cn.super12138.todo.ui.pages.tasks.components.TodoCard
|
||||||
import cn.super12138.todo.ui.pages.main.components.TodoTopAppBar
|
import cn.super12138.todo.ui.pages.tasks.components.TodoTopAppBar
|
||||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainPage(
|
fun SharedTransitionScope.TasksPage(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: MainViewModel,
|
viewModel: MainViewModel,
|
||||||
toTodoEditPage: (TodoEntity?) -> Unit,
|
toTodoAddPage: () -> Unit,
|
||||||
sharedTransitionScope: SharedTransitionScope,
|
toTodoEditPage: (TodoEntity) -> Unit,
|
||||||
) {
|
) {
|
||||||
val animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
val animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||||
|
|
||||||
|
|
@ -82,23 +82,21 @@ fun MainPage(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
with(sharedTransitionScope) {
|
TodoFloatingActionButton(
|
||||||
TodoFloatingActionButton(
|
text = stringResource(R.string.action_add_task),
|
||||||
text = stringResource(R.string.action_add_task),
|
iconRes = R.drawable.ic_add,
|
||||||
iconRes = R.drawable.ic_add,
|
expanded = expandedFab,
|
||||||
expanded = expandedFab,
|
onClick = { toTodoAddPage() },
|
||||||
onClick = { toTodoEditPage(null) },
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.sharedBounds(
|
||||||
.sharedElement(
|
sharedContentState = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
||||||
sharedContentState = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
animatedVisibilityScope = animatedVisibilityScope
|
||||||
animatedVisibilityScope = animatedVisibilityScope
|
)
|
||||||
)
|
.animateFloatingActionButton(
|
||||||
.animateFloatingActionButton(
|
visible = !inSelectedMode,
|
||||||
visible = !inSelectedMode,
|
alignment = Alignment.BottomEnd,
|
||||||
alignment = Alignment.BottomEnd,
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets(0, 0, 0, 0),
|
contentWindowInsets = WindowInsets(0, 0, 0, 0),
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
@ -109,7 +107,7 @@ fun MainPage(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding))
|
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filteredTodoList.isEmpty()) {
|
if (filteredTodoList.isEmpty()) {
|
||||||
|
|
@ -146,6 +144,10 @@ fun MainPage(
|
||||||
viewModel.playConfetti()
|
viewModel.playConfetti()
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.sharedBounds(
|
||||||
|
sharedContentState = rememberSharedContentState(key = "${Constants.KEY_TODO_ITEM_TRANSITION}_${it.id}"),
|
||||||
|
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||||
|
)
|
||||||
.animateItem(
|
.animateItem(
|
||||||
fadeInSpec = MaterialTheme.motionScheme.defaultEffectsSpec(),
|
fadeInSpec = MaterialTheme.motionScheme.defaultEffectsSpec(),
|
||||||
placementSpec = MaterialTheme.motionScheme.defaultSpatialSpec(),
|
placementSpec = MaterialTheme.motionScheme.defaultSpatialSpec(),
|
||||||
|
|
@ -154,7 +156,7 @@ fun MainPage(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.size(TodoDefaults.settingsItemVerticalPadding))
|
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
package cn.super12138.todo.ui.pages.main.components
|
package cn.super12138.todo.ui.pages.tasks.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
|
||||||
import androidx.compose.animation.expandHorizontally
|
import androidx.compose.animation.expandHorizontally
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkHorizontally
|
import androidx.compose.animation.shrinkHorizontally
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.basicMarquee
|
import androidx.compose.foundation.basicMarquee
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
|
@ -21,7 +19,8 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Badge
|
import androidx.compose.material3.Badge
|
||||||
import androidx.compose.material3.BadgedBox
|
import androidx.compose.material3.BadgedBox
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -32,7 +31,6 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
|
@ -45,10 +43,7 @@ import cn.super12138.todo.ui.TodoDefaults
|
||||||
import cn.super12138.todo.utils.VibrationUtils
|
import cn.super12138.todo.utils.VibrationUtils
|
||||||
import cn.super12138.todo.utils.containerColor
|
import cn.super12138.todo.utils.containerColor
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class,
|
|
||||||
ExperimentalMaterial3ExpressiveApi::class
|
|
||||||
)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TodoCard(
|
fun TodoCard(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -63,8 +58,8 @@ fun TodoCard(
|
||||||
onChecked: () -> Unit = {}
|
onChecked: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
val context = LocalContext.current
|
Card(
|
||||||
ElevatedCard(
|
colors = CardDefaults.cardColors(containerColor = TodoDefaults.ContainerColor),
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(TodoDefaults.toDoCardHeight)
|
.height(TodoDefaults.toDoCardHeight)
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.super12138.todo.ui.pages.main.components
|
package cn.super12138.todo.ui.pages.tasks.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
|
@ -5,6 +5,7 @@ import android.net.Uri
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.navigation3.runtime.NavBackStack
|
||||||
import androidx.navigation3.runtime.NavKey
|
import androidx.navigation3.runtime.NavKey
|
||||||
import cn.super12138.todo.TodoApp
|
import cn.super12138.todo.TodoApp
|
||||||
import cn.super12138.todo.constants.Constants
|
import cn.super12138.todo.constants.Constants
|
||||||
|
|
@ -35,7 +36,8 @@ import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class MainViewModel : ViewModel() {
|
class MainViewModel : ViewModel() {
|
||||||
val topLevelBackStack = TopLevelBackStack<NavKey>(startKey = TodoScreen.Overview)
|
val mainBackStack = TopLevelBackStack<NavKey>(TodoScreen.Overview)
|
||||||
|
val settingsBackStack = NavBackStack<NavKey>(TodoScreen.Settings.Main)
|
||||||
|
|
||||||
// 待办
|
// 待办
|
||||||
private val toDos: Flow<List<TodoEntity>> = Repository.getAllTodos()
|
private val toDos: Flow<List<TodoEntity>> = Repository.getAllTodos()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue