mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: implement MeshtasticNavDisplay and centralize Navigation 3 configuration
- Introduce `MeshtasticNavDisplay` in `core:ui` to encapsulate shared Navigation 3 configuration, including entry decorators, scene strategies, and transitions. - Integrate `rememberViewModelStoreNavEntryDecorator` to provide entry-scoped `ViewModelStoreOwner` support, ensuring ViewModels are automatically cleared when their backstack entry is popped. - Configure `DialogSceneStrategy` to enable navigation-driven dialogs that respect backstack lifecycle and predictive back gestures. - Implement a standardized 350 ms crossfade transition for both forward and pop navigation across all platforms. - Refactor `app` and `desktop` host modules to utilize `MeshtasticNavDisplay`, removing redundant manual configuration of `NavDisplay` and decorators. - Update `core:ui` dependencies to include `lifecycle-viewmodel-navigation3` and move it away from platform-specific host modules. - Update architectural documentation (`AGENTS.md`, `GEMINI.md`, and decision logs) to reflect the adoption of centralized navigation and ViewModel scoping patterns.
This commit is contained in:
parent
37729c13d8
commit
829aecd888
10 changed files with 122 additions and 57 deletions
|
|
@ -57,6 +57,8 @@ kotlin {
|
|||
implementation(libs.jetbrains.compose.material3.adaptive.navigation.suite)
|
||||
implementation(libs.jetbrains.navigationevent.compose)
|
||||
implementation(libs.jetbrains.navigation3.ui)
|
||||
implementation(libs.jetbrains.lifecycle.viewmodel.navigation3)
|
||||
implementation(libs.jetbrains.lifecycle.viewmodel.compose)
|
||||
}
|
||||
|
||||
val jvmAndroidMain by getting { dependencies { implementation(libs.compose.multiplatform.ui.tooling) } }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.ui.component
|
||||
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.ContentTransform
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||
import androidx.navigation3.runtime.NavEntry
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
||||
import androidx.navigation3.scene.DialogSceneStrategy
|
||||
import androidx.navigation3.scene.Scene
|
||||
import androidx.navigation3.scene.SinglePaneSceneStrategy
|
||||
import androidx.navigation3.ui.NavDisplay
|
||||
|
||||
/**
|
||||
* Duration in milliseconds for the shared crossfade transition between navigation scenes.
|
||||
*
|
||||
* This is faster than the library's Android default (700 ms) and matches Material 3 motion guidance for medium-emphasis
|
||||
* container transforms (~300-400 ms).
|
||||
*/
|
||||
private const val TRANSITION_DURATION_MS = 350
|
||||
|
||||
/**
|
||||
* Shared [NavDisplay] wrapper that configures the standard Meshtastic entry decorators, scene strategies, and
|
||||
* transition animations for all platform hosts.
|
||||
*
|
||||
* **Entry decorators** (applied to every backstack entry):
|
||||
* - [rememberSaveableStateHolderNavEntryDecorator] — saveable state per entry.
|
||||
* - [rememberViewModelStoreNavEntryDecorator] — entry-scoped `ViewModelStoreOwner` so that ViewModels obtained via
|
||||
* `koinViewModel()` are automatically cleared when the entry is popped.
|
||||
*
|
||||
* **Scene strategies** (evaluated in order):
|
||||
* - [DialogSceneStrategy] — entries annotated with `metadata = DialogSceneStrategy.dialog()` render as overlay
|
||||
* [Dialog][androidx.compose.ui.window.Dialog] windows with proper backstack lifecycle.
|
||||
* - [SinglePaneSceneStrategy] — default single-pane fallback.
|
||||
*
|
||||
* **Transitions**: A uniform 350 ms crossfade for both forward and pop navigation.
|
||||
*
|
||||
* @param backStack the navigation backstack, typically from [rememberNavBackStack].
|
||||
* @param entryProvider the entry provider built from feature navigation graphs.
|
||||
* @param modifier modifier applied to the underlying [NavDisplay].
|
||||
*/
|
||||
@Composable
|
||||
fun MeshtasticNavDisplay(
|
||||
backStack: List<NavKey>,
|
||||
entryProvider: (key: NavKey) -> NavEntry<NavKey>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
entryProvider = entryProvider,
|
||||
entryDecorators =
|
||||
listOf(rememberSaveableStateHolderNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator()),
|
||||
sceneStrategies = listOf(DialogSceneStrategy(), SinglePaneSceneStrategy()),
|
||||
transitionSpec = meshtasticTransitionSpec(),
|
||||
popTransitionSpec = meshtasticTransitionSpec(),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared crossfade [ContentTransform] used for both forward and pop navigation. Returns a lambda compatible with
|
||||
* [NavDisplay]'s `transitionSpec` / `popTransitionSpec` parameters.
|
||||
*/
|
||||
private fun meshtasticTransitionSpec(): AnimatedContentTransitionScope<Scene<NavKey>>.() -> ContentTransform = {
|
||||
ContentTransform(
|
||||
fadeIn(animationSpec = tween(TRANSITION_DURATION_MS)),
|
||||
fadeOut(animationSpec = tween(TRANSITION_DURATION_MS)),
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue