NavHostEngine
(WireNavHostEngine) sits on top of Compose Destinations to implement tablet-specific
presentation policy without duplicating navigation call sites.
Navigation graph structure
All destinations are collected under a single root nav graph,WireRootGraph, which is
held by MainNavHost:
HomeDestination sealed class defines the top-level items in the home
side-navigation drawer:
| Destination | Description |
|---|---|
Conversations | All conversations list with search bar and FAB |
Archive | Archived conversations |
Settings | User settings |
Vault | Wire Vault (secure storage) |
Cells | Wire Cells (file storage) |
Meetings | Meetings / scheduling |
WhatsNew | What’s new / changelog |
TeamManagement | Team admin tools |
Destination annotation system
Screens register themselves by annotating their composable with one of the Wire-specific destination annotations defined innavigation/annotation/app/AppDestinations.kt. Each annotation is itself
annotated with @Destination<NavGraph> pointing to the correct nav graph and
applying the standard wrapper stack.
| Annotation | Target nav graph |
|---|---|
@WireRootDestination | WireRootNavGraph |
@WireHomeDestination | HomeNavGraph |
@WireLoginDestination | LoginNavGraph |
@WireNewLoginDestination | NewLoginNavGraph |
@WireCreateAccountDestination | CreateAccountNavGraph |
@WireCreatePersonalAccountDestination | CreatePersonalAccountNavGraph |
@WireCreateTeamAccountDestination | CreateTeamAccountNavGraph |
@WireNewConversationDestination | NewConversationNavGraph |
@WirePersonalToTeamMigrationDestination | PersonalToTeamMigrationNavGraph |
Compose Destinations 2.3.x generates destination styles as immutable
val
properties. Never try to mutate Destination.style at runtime — use
WireNavHostEngine and TabletDialogRoutePolicy instead (see below).MainNavHost and Navigator
MainNavHost is the single DestinationsNavHost composable for the whole app.
It is responsible for:
- Providing a
WireNavHostEngine(custom engine — see below) - Sharing a
SharedTransitionScopeviaCompositionLocalProviderfor animated shared-element transitions - Injecting cross-cutting dependencies (
Navigator,LoginTypeSelector) into all destinations viadependenciesContainerBuilder - Scoping shared ViewModels to nested graphs (e.g.
NewConversationViewModellives at theNewConversationNavGraphlevel so all screens in that flow share the same instance)
Navigator wraps NavHostController and exposes type-safe methods:
rememberNavigator creates a Navigator backed by a tracking nav controller
that records the current destination name for analytics.
Deep link handling (ADR-0004)
All deep link URI parsing is centralised inDeepLinkProcessor. Before this
refactor, each ViewModel duplicated type-checking, error-logging, and
authorisation-guard logic. DeepLinkProcessor now provides:
- Duplicate detection — a single place verifies the deeplink type and logs errors.
- Authorisation guards — shows a toast and aborts if the destination requires a signed-in session that is absent.
- Call-state protection — prevents account switching when a call is active, preserving session integrity.
- Improved testability — the class is independently testable without spinning up a ViewModel.
Deep links that require an authenticated session display a user-facing toast
via
DeepLinkProcessor rather than silently failing. This behaviour is now
tested in one place rather than scattered across multiple ViewModels.Tablet dialog parity (ADR-0010)
Before the Compose Destinations 2.3.x migration, 17 destinations usedDestinationStyle.Runtime so they could be switched to DialogNavigation on
tablets at runtime. After the migration destination styles became immutable,
breaking tablet dialog presentation.
The solution introduces two components:
WireNavHostEngine
A customNavHostEngine that overrides composable(...) registration to
resolve the effective destination style before registering each destination
with the NavGraph:
TabletDialogRoutePolicy
The single source of truth for which routes must be presented as dialogs on tablets. It holds aSet<String> of base routes:
resolveTabletDialogParityStyle function combines both:
TabletDialogWrapper applies rounded-corner clipping when the same route
policy identifies a destination as a tablet dialog, keeping visuals consistent.
Both MainNavHost and the nested nav host in HomeScreen use
rememberWireNavHostEngine().
Maintenance rules for tablet dialog routes
Update the route list
Add or remove the base route string in
TabletDialogRoutePolicy.destinationBaseRoutes.Navigation wrappers
All Wire destination annotations include two wrappers applied in order:| Wrapper | Purpose |
|---|---|
WaitUntilTransitionEndsWrapper | Delays the content until the entry/exit animation completes, preventing visual glitches during transitions. |
TabletDialogWrapper | Applies rounded-corner clipping to destinations that match the tablet dialog route policy. |