ADR-0034: Onboarding Flow Structure and Skippability
DateMarch 1, 2026
CategoryOnboarding & Features
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:
| # | Screen | Purpose |
|---|
| 1 | Welcome | App introduction, set display name |
| 2 | Core Concept — GTD | Explain GTD methodology with simple visual diagrams |
| 3 | Core Concept — Reflect | Explain daily reflection loop and its value |
| 4 | AI Opt-In | Explain on-device AI, ask user to enable/disable (ADR-0035) |
| 5 | Model Download | If AI opted in: background download with progress (ADR-0035) |
| 6 | Ready | Timezone 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).