All Decisions

ADR-0034: Onboarding Flow Structure and Skippability

DateMarch 1, 2026
CategoryOnboarding & Features
Tags
onboardingnavigation

Context

  • Phase 7 introduces a first-launch onboarding experience to guide new users through app setup and the core GTD + reflection value proposition.
  • LocusFlow uses a single-Activity architecture with Jetpack Navigation Compose (ADR-0025) and MVVM + Repository layering (ADR-0003).
  • Settings persistence uses Jetpack DataStore (ADR-0027), which already defines an onboarding_completed boolean key.
  • Feature flags (ADR-0028) gate optional capabilities like LLM-powered features.
  • The onboarding content must serve double duty: first-run flow and a replayable tutorial accessible from Settings.
  • Users cannot be assumed to know GTD methodology; the onboarding is likely their first exposure.

Constraints:

  • Single-Activity, Compose-only — no separate OnboardingActivity.
  • Onboarding screens must be reusable composables that work both in the first-run flow and in a dedicated Tutorials section accessible from Settings.
  • Skip is not available on every screen — core setup screens (name, timezone, AI opt-in) must be completed before proceeding or explicitly deferred as described below.
  • The flow must collect essential settings: display name, timezone, and AI opt-in (ADR-0035).

Decision

1. Flow Structure: ~6 Compose Screens in a Dedicated NavGraph

We will implement the onboarding as approximately six full-screen Compose pages within a dedicated onboarding nested navigation graph inside the existing NavHost.

Planned screen sequence:

#ScreenPurpose
1WelcomeApp introduction, set display name
2Core Concept — GTDExplain GTD methodology with simple visual diagrams
3Core Concept — ReflectExplain daily reflection loop and its value
4AI Opt-InExplain on-device AI, ask user to enable/disable (ADR-0035)
5Model DownloadIf AI opted in: background download with progress (ADR-0035)
6ReadyTimezone confirmation, summary, "Get Started"

Screen 5 is conditionally shown only if the user opts in to AI features on screen 4. If the user declines AI, the flow advances directly from screen 4 to screen 6.

2. Navigation Pattern

  • First run: MainActivity observes SettingsRepository.onboardingCompleted. If false, the start destination is onboarding/welcome. On completion or skip, the app navigates to the main graph and sets onboarding_completed = true.
  • Tutorial replay: Settings exposes a "Replay Tutorial" action that navigates to the same onboarding nested graph but with a tutorialMode = true argument. In tutorial mode the skip button is replaced by a "Close" button, and settings writes are suppressed (display name, timezone, AI opt-in are view-only or hidden).
ui/ ├── features/ │ └── onboarding/ │ ├── OnboardingNavigation.kt # NavGraphBuilder.onboardingGraph() extension │ ├── OnboardingViewModel.kt # Shared ViewModel scoped to onboarding nav graph │ ├── WelcomeScreen.kt # Screen 1 — name input │ ├── TimezoneScreen.kt # Screen 2 — timezone picker │ ├── PathSelectionScreen.kt # Screen 3 — GTD vs Vision choice │ ├── gtd/ │ │ ├── GtdCaptureScreen.kt # GTD concept A: capture & inbox │ │ ├── GtdProcessingScreen.kt # GTD concept B: processing decisions │ │ ├── GtdActionsScreen.kt # GTD concept C: next actions & contexts │ │ └── GtdReflectionScreen.kt # GTD concept D: weekly review & reflection │ ├── vision/ │ │ ├── VisionValuesScreen.kt # Vision concept A: personal vision & values │ │ ├── VisionReflectionScreen.kt # Vision concept B: daily reflection loop │ │ ├── VisionSynthesisScreen.kt # Vision concept C: weekly synthesis │ │ └── VisionExecutionScreen.kt # Vision concept D: how GTD supports vision │ ├── AiOptInScreen.kt # Screen 8 — AI opt-in gate │ ├── ModelDownloadScreen.kt # Screen 9 (conditional) — reuses ModelDownloadViewModel │ └── ReadyScreen.kt # Screen 10 — completion

Tutorial reuse: The four GTD concept screens (gtd/) and four Vision concept screens (vision/) are standalone stateless composables with no dependency on onboarding-specific state. They are directly reused in the Tutorials section (accessible from Settings), where they render identically but with no settings writes or completion tracking. The tutorial entry point navigates to a tutorials/ nested graph that orchestrates the same composables.

3. Screen Transitions and Progress Indication

  • Screens use horizontal slide transitions (left-to-right forward, right-to-left back).
  • A linear progress indicator (segmented or continuous) is displayed at the bottom of every screen showing currentStep / totalSteps.
  • Back and forward navigation arrows are shown at the bottom. The forward arrow is gated by any required input on that screen (e.g., display name must be non-empty on screen 1).

4. Skip Behavior

Skip availability is split into two tiers:

Non-skippable screens (1, 2, 8): No Skip button is rendered. These screens require user action before the forward arrow is enabled:

  • Screen 1 (Welcome): display name must be non-empty to advance.
  • Screen 2 (Timezone): user must confirm (or accept the pre-filled device timezone) to advance.
  • Screen 8 (AI Opt-In): user must explicitly choose "Enable AI" or "No thanks" to advance. There is no Skip — a deliberate opt-in decision is required because it affects feature availability and a potential 700 MB download.

Skippable screens (3–7): A Skip button is visible from the Path Selection screen onward through all four concept screens.

  • Tapping Skip shows a brief confirmation: "Skip the tour? You can replay it anytime from Settings → Tutorials."
  • On confirmation, the flow jumps directly to screen 8 (AI Opt-In).
  • Skip does not bypass screen 8; AI opt-in is always required.
  • After skipping the tour, onboarding_completed is set to true only when the user reaches and completes screen 10 (Ready).

Permanent dismissal: Once onboarding_completed = true is written, the first-run gate never triggers again. The user's chosen concept path (or skip) is not stored — the Tutorials section always shows both paths.

5. Screen Reusability

Each screen composable is a stateless function that receives display data and callbacks:

@Composable
fun GtdCaptureScreen(
    onNext: () -> Unit,
    onBack: () -> Unit,
    onSkip: (() -> Unit)?,  // null → hides Skip button (non-skippable screens & tutorial mode)
)

The OnboardingViewModel (scoped to the onboarding nav graph) supplies the callbacks, manages page index, tracks the selected path (GTD vs Vision), and coordinates settings writes.

For the Tutorials section, a separate TutorialsViewModel (or a thin wrapper) supplies the same callback signatures with onSkip = null and no settings writes, allowing identical composables to serve both contexts without modification.

Progress indicator: The bottom progress bar shows currentStep / totalSteps. When the user picks GTD (4 concept screens), total is 10; if one path is unavailable (Vision deferred), total is still calculated correctly. The conditional model download screen (screen 9) is counted only after the user opts in, adjusting totalSteps dynamically.

6. ViewModel Scoping

OnboardingViewModel is scoped to the onboarding nested nav graph via hiltViewModel(navBackStackEntry), ensuring state is shared across all onboarding screens but cleaned up when the user exits the flow.

Rationale

  • ~10 screens balances thoroughness with brevity given the scope of content (name, timezone, path selection, 4 concept screens, AI opt-in, optional download, ready). Each screen carries a single focused purpose.
  • Nested NavGraph keeps onboarding navigation self-contained and allows graph-scoped ViewModel sharing without polluting the main nav graph.
  • Tiered skippability (non-skippable setup + skippable tour + non-skippable AI gate) ensures essential data is always collected while respecting users who don't want a full tour. Users who skip the tour can replay it from Settings → Tutorials.
  • Non-skippable AI opt-in prevents accidental feature disablement. Requiring an explicit choice (not a skip-to-dismiss) ensures the user understands the AI capability and makes an informed decision about the potential model download.
  • Compose-only, no separate Activity is consistent with the single-Activity architecture (ADR-0025) and avoids duplicating theme/DI setup.
  • Stateless screen composables enable straightforward Compose UI testing (ADR-0026 Tier 2) and direct reuse in the Tutorials section without code duplication.
  • DataStore for onboarding state aligns with ADR-0027, which already defines the onboarding_completed key. SharedPreferences was considered but ADR-0027 explicitly deprecated it in favor of DataStore for type safety and Flow observability.

Consequences

  • Positive:
    • Users get a structured, path-appropriate introduction to the app's value proposition.
    • Concept screens are reused verbatim in the Tutorials section — zero duplication.
    • Tiered skippability respects both impatient users (skip tour, not setup) and thorough ones.
    • Non-skippable AI opt-in guarantees a deliberate, informed choice about feature availability.
    • Stateless screens are easy to test, preview, and iterate on.
  • Negative:
    • ~10 screens is a significant implementation effort.
    • Dynamic totalSteps (adjusting when model download screen appears) adds minor progress indicator complexity.
    • Tutorial mode requires onSkip = null and suppressed writes — minor ViewModel complexity.
  • Follow-up:
    • ADR-0035 defines the model download screen content (benchmarks, website links).
    • ADR-0038 defines the content for all four GTD and Vision concept screens.
    • A future ADR or UI/UX spec will define the Tutorials section navigation structure in Settings.
    • The exact screen content, copy, and visual design are out of scope for this ADR.

Alternatives Considered

  • Separate OnboardingActivity — rejected because it conflicts with the single-Activity architecture, duplicates Hilt/theme setup, and complicates navigation back to the main graph.
  • ViewPager / HorizontalPager — considered for swipe-based paging. Rejected because Navigation Compose with slide transitions provides the same UX with better integration into the existing nav framework, deep-link support, and per-screen ViewModel access. HorizontalPager also makes it harder to gate the forward swipe on required inputs.
  • Shared skip-all approach (skip available from screen 1) — rejected. Allowing skip before name and timezone are set leaves the app in an unconfigured state. Name and timezone are essential enough to always collect.
  • Skippable AI opt-in — rejected. The AI decision has material consequences (feature availability, potential 700 MB download). A skip that implicitly means "no" is worse UX than an explicit two-option choice.

Notes

  • The screen count (~10) is approximate. Content screens may merge or split during implementation; the architecture accommodates any reasonable count.
  • The Vision-Reflection path (screens 4–7 variant) is deferred until Phase 12 (Personal Vision) is implemented. Until then, path selection is skipped and the GTD path runs by default.
  • The Tutorials section in Settings is defined here architecturally but its exact navigation structure, entry point, and ViewModel design will be decided when Settings is extended in a future phase.
  • Related ADRs: ADR-0025 (UI packaging), ADR-0027 (settings persistence), ADR-0028 (feature flags), ADR-0035 (model download and content), ADR-0037 (completion tracking), ADR-0038 (concept screen content).