diff --git a/app/src/main/java/com/joker/kit/core/navigation/AppNavHost.kt b/app/src/main/java/com/joker/kit/core/navigation/AppNavHost.kt index bf726ee..dff1636 100644 --- a/app/src/main/java/com/joker/kit/core/navigation/AppNavHost.kt +++ b/app/src/main/java/com/joker/kit/core/navigation/AppNavHost.kt @@ -2,6 +2,7 @@ package com.joker.kit.core.navigation import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionLayout +import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween import androidx.compose.animation.slideInHorizontally @@ -9,6 +10,7 @@ import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.togetherWith import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.IntOffset import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator @@ -49,7 +51,9 @@ fun AppNavHost( // 创建应用级回退栈,首个页面固定为主页面。 val backStack = rememberNavBackStack(MainRoutes.Main) // 基于当前回退栈构建导航控制器,供 AppNavigator 分发命令时使用。 - val navigationController = rememberBackStackNavigationController(backStack, navigator) + val navigationController = remember(backStack, navigator) { + createBackStackNavigationController(backStack, navigator) + } // 在组合生命周期内绑定/解绑导航控制器,确保导航命令总是指向当前有效宿主。 DisposableEffect(navigationController) { @@ -77,7 +81,7 @@ fun AppNavHost( transitionSpec = { createForwardTransition() }, popTransitionSpec = { createBackwardTransition() }, predictivePopTransitionSpec = { createBackwardTransition() }, - entryProvider = appEntryProvider(), + entryProvider = appEntryProvider(this@SharedTransitionLayout), ) } } @@ -118,7 +122,7 @@ private fun createBackwardTransition() = slideInHorizontally( * @return 应用级 EntryProvider * @author Joker.X */ -private fun appEntryProvider() = entryProvider { +private fun appEntryProvider(scope: SharedTransitionScope) = entryProvider { mainGraph() demoGraph() authGraph() diff --git a/app/src/main/java/com/joker/kit/core/navigation/BackStackNavigationController.kt b/app/src/main/java/com/joker/kit/core/navigation/BackStackNavigationController.kt index 1e8f17c..7fb32c6 100644 --- a/app/src/main/java/com/joker/kit/core/navigation/BackStackNavigationController.kt +++ b/app/src/main/java/com/joker/kit/core/navigation/BackStackNavigationController.kt @@ -1,7 +1,5 @@ package com.joker.kit.core.navigation -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.navigation3.runtime.NavBackStack import androidx.navigation3.runtime.NavKey @@ -13,14 +11,11 @@ import androidx.navigation3.runtime.NavKey * @return BackStack 导航控制器 * @author Joker.X */ -@Composable -internal fun rememberBackStackNavigationController( +fun createBackStackNavigationController( backStack: NavBackStack, navigator: AppNavigator, ): NavigationController { - return remember(backStack, navigator) { - BackStackNavigationController(backStack = backStack, navigator = navigator) - } + return BackStackNavigationController(backStack = backStack, navigator = navigator) } /** @@ -49,7 +44,11 @@ private class BackStackNavigationController( override fun navigateTo(route: NavKey, navOptions: NavigationOptions?) { val popUpToRoute = navOptions?.popUpToRoute if (popUpToRoute != null) { - backStack.popUpTo(route = popUpToRoute, inclusive = navOptions.inclusive) + backStack.popUpTo( + route = popUpToRoute, + inclusive = navOptions.inclusive, + allowPopToEmpty = navOptions.allowPopToEmpty, + ) } backStack.add(route) } @@ -94,9 +93,14 @@ private class BackStackNavigationController( * * @param route 目标路由 * @param inclusive 是否包含目标路由 + * @param allowPopToEmpty 当目标路由是栈底时,是否允许清空整个返回栈 * @author Joker.X */ -private fun NavBackStack.popUpTo(route: NavKey, inclusive: Boolean) { +private fun NavBackStack.popUpTo( + route: NavKey, + inclusive: Boolean, + allowPopToEmpty: Boolean = false, +) { val targetIndex = indexOfLast { it == route } if (targetIndex == -1) return @@ -104,7 +108,9 @@ private fun NavBackStack.popUpTo(route: NavKey, inclusive: Boolean) { if (removeFromIndex >= size) return if (removeFromIndex == 0) { - if (size > 1) { + if (allowPopToEmpty) { + clear() + } else if (size > 1) { subList(1, size).clear() } return diff --git a/app/src/main/java/com/joker/kit/core/navigation/NavigationController.kt b/app/src/main/java/com/joker/kit/core/navigation/NavigationController.kt index 3cbd70c..3bb3805 100644 --- a/app/src/main/java/com/joker/kit/core/navigation/NavigationController.kt +++ b/app/src/main/java/com/joker/kit/core/navigation/NavigationController.kt @@ -9,7 +9,7 @@ import androidx.navigation3.runtime.NavKey * * @author Joker.X */ -internal interface NavigationController { +interface NavigationController { /** * 导航到目标路由 * diff --git a/app/src/main/java/com/joker/kit/core/navigation/NavigationOptions.kt b/app/src/main/java/com/joker/kit/core/navigation/NavigationOptions.kt index bada1fd..37e06f5 100644 --- a/app/src/main/java/com/joker/kit/core/navigation/NavigationOptions.kt +++ b/app/src/main/java/com/joker/kit/core/navigation/NavigationOptions.kt @@ -7,9 +7,12 @@ import androidx.navigation3.runtime.NavKey * * @param popUpToRoute 回退栈弹出到的目标路由 * @param inclusive 是否包含目标路由本身 + * @param allowPopToEmpty 是否允许清空整个返回栈(用于替换栈底页面,如 Splash -> Main) * @author Joker.X */ data class NavigationOptions( val popUpToRoute: NavKey? = null, val inclusive: Boolean = false, + val allowPopToEmpty: Boolean = false, ) + diff --git a/app/src/main/java/com/joker/kit/core/navigation/NavigationParam.kt b/app/src/main/java/com/joker/kit/core/navigation/NavigationParam.kt deleted file mode 100644 index 27653dd..0000000 --- a/app/src/main/java/com/joker/kit/core/navigation/NavigationParam.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.joker.kit.core.navigation - -/** - * 公共导航参数定义 - * - * 用于沉淀跨模块可复用的导航参数模型,避免在各业务模块重复声明。 - * - * @author Joker.X - */ -/** - * 通用 ID 参数 - * - * 适用于仅需要传递单个 ID 的页面跳转场景。 - * - * @property id 通用业务 ID - * @author Joker.X - */ -data class IdParam( - /** - * 通用业务 ID - */ - val id: Long, -) diff --git a/app/src/main/java/com/joker/kit/feature/auth/view/LoginScreen.kt b/app/src/main/java/com/joker/kit/feature/auth/view/LoginScreen.kt index 97d99a6..14f8987 100644 --- a/app/src/main/java/com/joker/kit/feature/auth/view/LoginScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/auth/view/LoginScreen.kt @@ -27,27 +27,24 @@ internal fun LoginRoute( viewModel: LoginViewModel = hiltViewModel() ) { LoginScreen( - onLoginClick = viewModel::login, - onBackClick = ::navigateBack + onLoginClick = viewModel::login ) } /** * 登录页面 * - * @param onBackClick 返回按钮回调 * @param onLoginClick 登录按钮回调 * @author Joker.X */ @OptIn(ExperimentalMaterial3Api::class) @Composable internal fun LoginScreen( - onBackClick: () -> Unit = {}, onLoginClick: () -> Unit = {}, ) { AppScaffold( titleText = "登录", - onBackClick = onBackClick, + onBackClick = { navigateBack() }, ) { LoginContentView( onLoginClick = onLoginClick diff --git a/app/src/main/java/com/joker/kit/feature/auth/viewmodel/LoginViewModel.kt b/app/src/main/java/com/joker/kit/feature/auth/viewmodel/LoginViewModel.kt index 3156089..914cfef 100644 --- a/app/src/main/java/com/joker/kit/feature/auth/viewmodel/LoginViewModel.kt +++ b/app/src/main/java/com/joker/kit/feature/auth/viewmodel/LoginViewModel.kt @@ -14,7 +14,6 @@ import javax.inject.Inject /** * 登录页 ViewModel * - * @param navigator 导航管理器 * @param userState 全局用户状态 * @author Joker.X */ diff --git a/app/src/main/java/com/joker/kit/feature/demo/view/DatabaseScreen.kt b/app/src/main/java/com/joker/kit/feature/demo/view/DatabaseScreen.kt index 6d0ef0d..187cbb4 100644 --- a/app/src/main/java/com/joker/kit/feature/demo/view/DatabaseScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/demo/view/DatabaseScreen.kt @@ -75,8 +75,7 @@ internal fun DatabaseRoute( onDescriptionChange = viewModel::onDescriptionChange, onAddClick = viewModel::addItem, onDeleteItem = viewModel::deleteItem, - onClearAll = viewModel::clearAll, - onBackClick = ::navigateBack + onClearAll = viewModel::clearAll ) } @@ -91,7 +90,6 @@ internal fun DatabaseRoute( * @param onAddClick 点击新增记录 * @param onDeleteItem 删除指定记录 * @param onClearAll 清空所有记录 - * @param onBackClick 返回按钮回调 * @author Joker.X */ @OptIn(ExperimentalMaterial3Api::class) @@ -105,11 +103,10 @@ internal fun DatabaseScreen( onAddClick: () -> Unit = {}, onDeleteItem: (Long) -> Unit = {}, onClearAll: () -> Unit = {}, - onBackClick: () -> Unit = {}, ) { AppScaffold( titleText = "数据库", - onBackClick = onBackClick + onBackClick = { navigateBack() } ) { DatabaseContent( title = title, diff --git a/app/src/main/java/com/joker/kit/feature/demo/view/LocalStorageScreen.kt b/app/src/main/java/com/joker/kit/feature/demo/view/LocalStorageScreen.kt index 96757e7..e254bb7 100644 --- a/app/src/main/java/com/joker/kit/feature/demo/view/LocalStorageScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/demo/view/LocalStorageScreen.kt @@ -68,8 +68,7 @@ internal fun LocalStorageRoute( onAvatarChange = viewModel::onAvatarChange, onSaveUser = viewModel::saveUser, onClearUser = viewModel::clearUser, - onReloadUser = viewModel::loadUser, - onBackClick = ::navigateBack + onReloadUser = viewModel::loadUser ) } @@ -86,7 +85,6 @@ internal fun LocalStorageRoute( * @param onSaveUser 保存用户信息 * @param onClearUser 清除用户信息 * @param onReloadUser 重新读取用户信息 - * @param onBackClick 返回按钮回调 * @author Joker.X */ @OptIn(ExperimentalMaterial3Api::class) @@ -102,11 +100,10 @@ internal fun LocalStorageScreen( onSaveUser: () -> Unit = {}, onClearUser: () -> Unit = {}, onReloadUser: () -> Unit = {}, - onBackClick: () -> Unit = {}, ) { AppScaffold( titleText = "本地存储", - onBackClick = onBackClick, + onBackClick = { navigateBack() }, ) { LocalStorageContent( userId = userId, diff --git a/app/src/main/java/com/joker/kit/feature/demo/view/NavigationResultScreen.kt b/app/src/main/java/com/joker/kit/feature/demo/view/NavigationResultScreen.kt index 8fc0feb..90c8474 100644 --- a/app/src/main/java/com/joker/kit/feature/demo/view/NavigationResultScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/demo/view/NavigationResultScreen.kt @@ -27,7 +27,6 @@ internal fun NavigationResultRoute( viewModel: NavigationResultViewModel = hiltViewModel() ) { NavigationResultScreen( - onBackClick = ::navigateBack, onSendResult = viewModel::sendResultAndBack ) } @@ -36,18 +35,16 @@ internal fun NavigationResultRoute( * 结果回传示例界面 * * @param onSendResult 发送结果并返回回调 - * @param onBackClick 返回按钮回调 * @author Joker.X */ @OptIn(ExperimentalMaterial3Api::class) @Composable internal fun NavigationResultScreen( - onBackClick: () -> Unit = {}, onSendResult: () -> Unit = {}, ) { AppScaffold( titleText = "结果回传", - onBackClick = onBackClick + onBackClick = { navigateBack() } ) { NavigationResultContent(onSendResult = onSendResult) } diff --git a/app/src/main/java/com/joker/kit/feature/demo/view/NavigationWithArgsScreen.kt b/app/src/main/java/com/joker/kit/feature/demo/view/NavigationWithArgsScreen.kt index a5ca28e..ba8312d 100644 --- a/app/src/main/java/com/joker/kit/feature/demo/view/NavigationWithArgsScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/demo/view/NavigationWithArgsScreen.kt @@ -28,8 +28,7 @@ internal fun NavigationWithArgsRoute( ) ) { NavigationWithArgsScreen( - goodsId = viewModel.goodsId, - onBackClick = ::navigateBack + goodsId = viewModel.goodsId ) } @@ -37,18 +36,16 @@ internal fun NavigationWithArgsRoute( * 带参跳转示例界面 * * @param goodsId 传入的商品 ID - * @param onBackClick 返回按钮回调 * @author Joker.X */ @OptIn(ExperimentalMaterial3Api::class) @Composable internal fun NavigationWithArgsScreen( goodsId: Long = 0, - onBackClick: () -> Unit = {}, ) { AppScaffold( titleText = "带参跳转", - onBackClick = onBackClick + onBackClick = { navigateBack() } ) { NavigationWithArgsContent(goodsId = goodsId) } diff --git a/app/src/main/java/com/joker/kit/feature/demo/view/NetworkDemoScreen.kt b/app/src/main/java/com/joker/kit/feature/demo/view/NetworkDemoScreen.kt index 4acc428..c551ef2 100644 --- a/app/src/main/java/com/joker/kit/feature/demo/view/NetworkDemoScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/demo/view/NetworkDemoScreen.kt @@ -34,7 +34,6 @@ internal fun NetworkDemoRoute( NetworkDemoScreen( uiState = uiState, - onBackClick = ::navigateBack, onRetry = viewModel::retryRequest ) } @@ -43,7 +42,6 @@ internal fun NetworkDemoRoute( * Network Demo 界面 * * @param uiState UI 状态 - * @param onBackClick 返回按钮回调 * @param onRetry 重试回调 * @author Joker.X */ @@ -51,12 +49,11 @@ internal fun NetworkDemoRoute( @Composable internal fun NetworkDemoScreen( uiState: BaseNetWorkUiState = BaseNetWorkUiState.Loading, - onBackClick: () -> Unit = {}, onRetry: () -> Unit = {}, ) { AppScaffold( titleText = "Network Demo", - onBackClick = onBackClick, + onBackClick = { navigateBack() }, ) { BaseNetWorkView( uiState = uiState, diff --git a/app/src/main/java/com/joker/kit/feature/demo/view/NetworkListDemoScreen.kt b/app/src/main/java/com/joker/kit/feature/demo/view/NetworkListDemoScreen.kt index 3ca302f..50c5883 100644 --- a/app/src/main/java/com/joker/kit/feature/demo/view/NetworkListDemoScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/demo/view/NetworkListDemoScreen.kt @@ -49,7 +49,6 @@ internal fun NetworkListDemoRoute( onRefresh = viewModel::onRefresh, onLoadMore = viewModel::onLoadMore, shouldTriggerLoadMore = viewModel::shouldTriggerLoadMore, - onBackClick = ::navigateBack, onRetry = viewModel::retryRequest, ) } @@ -64,7 +63,6 @@ internal fun NetworkListDemoRoute( * @param onRefresh 刷新回调 * @param onLoadMore 加载更多回调 * @param shouldTriggerLoadMore 是否触发加载更多 - * @param onBackClick 返回回调 * @param onRetry 重试回调 * @author Joker.X */ @@ -78,12 +76,11 @@ internal fun NetworkListDemoScreen( onRefresh: () -> Unit = {}, onLoadMore: () -> Unit = {}, shouldTriggerLoadMore: (lastIndex: Int, totalCount: Int) -> Boolean = { _, _ -> false }, - onBackClick: () -> Unit = {}, onRetry: () -> Unit = {}, ) { AppScaffold( titleText = "Network List Demo", - onBackClick = onBackClick + onBackClick = { navigateBack() } ) { BaseNetWorkListView( uiState = uiState, diff --git a/app/src/main/java/com/joker/kit/feature/demo/view/NetworkRequestScreen.kt b/app/src/main/java/com/joker/kit/feature/demo/view/NetworkRequestScreen.kt index c0341e6..067f125 100644 --- a/app/src/main/java/com/joker/kit/feature/demo/view/NetworkRequestScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/demo/view/NetworkRequestScreen.kt @@ -38,7 +38,6 @@ internal fun NetworkRequestRoute( NetworkRequestScreen( goods = goods, - onBackClick = ::navigateBack, onRequestClick = viewModel::onRequestClick ) } @@ -47,7 +46,6 @@ internal fun NetworkRequestRoute( * 网络请求示例界面 * * @param goods 商品信息 - * @param onBackClick 返回按钮回调 * @param onRequestClick 请求按钮回调 * @author Joker.X */ @@ -55,12 +53,11 @@ internal fun NetworkRequestRoute( @Composable internal fun NetworkRequestScreen( goods: Goods? = null, - onBackClick: () -> Unit = {}, onRequestClick: () -> Unit = {}, ) { AppScaffold( titleText = "网络请求", - onBackClick = onBackClick + onBackClick = { navigateBack() } ) { NetworkRequestContent( goods = goods, diff --git a/app/src/main/java/com/joker/kit/feature/demo/view/StateManagementScreen.kt b/app/src/main/java/com/joker/kit/feature/demo/view/StateManagementScreen.kt index 947a1ae..cee635c 100644 --- a/app/src/main/java/com/joker/kit/feature/demo/view/StateManagementScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/demo/view/StateManagementScreen.kt @@ -51,8 +51,7 @@ internal fun StateManagementRoute( count = count, onIncrease = viewModel::increase, onDecrease = viewModel::decrease, - onReset = viewModel::reset, - onBackClick = ::navigateBack + onReset = viewModel::reset ) } @@ -63,7 +62,6 @@ internal fun StateManagementRoute( * @param onIncrease +1 回调 * @param onDecrease -1 回调 * @param onReset 重置回调 - * @param onBackClick 返回按钮回调 * @author Joker.X */ @OptIn(ExperimentalMaterial3Api::class) @@ -73,11 +71,10 @@ internal fun StateManagementScreen( onIncrease: () -> Unit = {}, onDecrease: () -> Unit = {}, onReset: () -> Unit = {}, - onBackClick: () -> Unit = {}, ) { AppScaffold( titleText = "状态管理", - onBackClick = onBackClick, + onBackClick = { navigateBack() }, ) { StateManagementContent( count = count, diff --git a/app/src/main/java/com/joker/kit/feature/user/view/UserInfoScreen.kt b/app/src/main/java/com/joker/kit/feature/user/view/UserInfoScreen.kt index eee69e8..cdd438a 100644 --- a/app/src/main/java/com/joker/kit/feature/user/view/UserInfoScreen.kt +++ b/app/src/main/java/com/joker/kit/feature/user/view/UserInfoScreen.kt @@ -27,27 +27,24 @@ internal fun UserInfoRoute( viewModel: UserInfoViewModel = hiltViewModel() ) { UserInfoScreen( - onLogoutClick = viewModel::logout, - onBackClick = ::navigateBack + onLogoutClick = viewModel::logout ) } /** * 用户信息页面 * - * @param onBackClick 返回按钮回调 * @param onLogoutClick 退出登录回调 * @author Joker.X */ @OptIn(ExperimentalMaterial3Api::class) @Composable internal fun UserInfoScreen( - onBackClick: () -> Unit = {}, onLogoutClick: () -> Unit = {}, ) { AppScaffold( titleText = "用户信息", - onBackClick = onBackClick, + onBackClick = { navigateBack() }, ) { UserInfoContentView( modifier = Modifier.padding(SpacePaddingLarge), diff --git a/app/src/main/java/com/joker/kit/feature/user/viewmodel/UserInfoViewModel.kt b/app/src/main/java/com/joker/kit/feature/user/viewmodel/UserInfoViewModel.kt index 407b653..c6a06b0 100644 --- a/app/src/main/java/com/joker/kit/feature/user/viewmodel/UserInfoViewModel.kt +++ b/app/src/main/java/com/joker/kit/feature/user/viewmodel/UserInfoViewModel.kt @@ -12,7 +12,6 @@ import javax.inject.Inject /** * 用户信息页 ViewModel * - * @param navigator 导航管理器 * @param userState 全局用户状态 * @author Joker.X */