Cpapter 5: Activities, Navigation, and App Structure

Activities, Navigation, and App Structure! This is a big one — we’re moving from single-screen “hello world” stuff to real multi-screen apps, like how Instagram switches from home feed to profile to settings.

In 2026, modern Android apps use single-activity architecture + Jetpack Navigation Compose (not the old fragment-based one). One main Activity hosts a NavHost that swaps composable screens. No more multiple Activities for every screen — it’s cleaner, faster, and easier to manage state.

We’ll cover:

  • Activity lifecycle (still super important, even in single-activity apps)
  • Setting up Navigation Component with Compose
  • Passing data safely between screens
  • Hands-on: A multi-screen Tip Calculator app (input bill → calculate tip → show result on second screen)

Let’s go step-by-step like we’re coding together on your machine in Airoli.

1. Activity Lifecycle (The Life Story of Your App’s Main Screen)

Even with Compose and single-activity, your MainActivity goes through a full lifecycle. Understanding this prevents bugs like losing data on rotation or when user switches apps.

Key states & callbacks (same as always — no big changes in Android 16):

  • Created → onCreate(savedInstanceState: Bundle?)
    • First birth (or rebirth after rotation/process death).
    • Do one-time setup: setContent { … }, initialize ViewModels.
    • Restore state if savedInstanceState != null.
    • Call super.onCreate() first!
  • Started → onStart()
    • Becoming visible (but not interactive yet).
  • Resumed → onResume()
    • Foreground & interactive! Start animations, location, etc.
    • User can tap now.
  • Paused → onPause()
    • Partially obscured (dialog, incoming call, multi-window).
    • Pause heavy stuff (camera, sensors). Quick — no heavy work!
  • Stopped → onStop()
    • Not visible at all (another app full-screen).
    • Release resources, save data if needed.
  • Destroyed → onDestroy()
    • Killed (back pressed, system kill, rotation).
    • Clean up final resources.
  • Restart → onRestart() (rarely used — between Stopped → Started)

Common scenarios:

  • Screen rotation → Destroy old → Create new (use ViewModel to survive).
  • Process death (low memory) → Recreate from saved state.
  • Back press → popBackStack() in NavController (handled by Navigation lib usually).

Best practice in Compose:

  • Don’t put logic in Activity callbacks — use ViewModel + Lifecycle-aware stuff.
  • For UI state: Use rememberSaveable or ViewModel.
  • Save persistent data in onStop() or repository.

Analogy: Think of Activity as a person:

  • Born (onCreate) → Grow up (onStart) → Active life (onResume) → Take a nap (onPause) → Sleep (onStop) → Wake up (onRestart) → Die (onDestroy).

2. Navigation Component with Multiple Screens (Jetpack Navigation Compose)

We use androidx.navigation:navigation-compose (latest stable ~2.9.x in 2026).

Why single-activity?

  • Faster (no Activity creation overhead)
  • Easier state sharing (one ViewModel scope)
  • Better for adaptive UIs (foldables, tablets)

Setup steps:

  1. Add dependency (in app/build.gradle.kts):

    Kotlin
  2. Create a sealed class or @Serializable for routes (type-safe!): Use Kotlinx Serialization (add implementation(“org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1”) and plugin).

    In a new file Navigation.kt:

    Kotlin
  3. Remember NavController in your root composable.

  4. Set up NavHost in MainActivity.kt → setContent:

    Kotlin
    • rememberNavController() → Survives recomposition.
    • composable<Destination> → Type-safe route.
    • toRoute<>() → Gets arguments safely.

3. Passing Data Between Screens

Best way in 2026: Pass minimal data (IDs or primitives) via route args. Fetch heavy data from ViewModel/Repository.

  • Simple types: String, Int, Double — fine in route.
  • Complex: Use @Serializable data class (like TipResult above).
  • Avoid passing large objects or Parcelable directly — use IDs + shared ViewModel or repository.

In code:

  • Navigate: navController.navigate(TipResult(100.0, 15, 15.0, 115.0))
  • Receive: backStackEntry.toRoute<TipResult>()

Pro tip: For shared state across screens, use a shared ViewModel scoped to the NavGraph.

4. Hands-on: Multi-Screen Tip Calculator App

Let’s build it! (Extend your existing project)

Screen 1: Home/Input

  • Bill amount TextField
  • Tip % Slider (0-30%)
  • Calculate button → Navigate to result

Screen 2: Result

  • Shows bill, tip %, tip amount, total
  • Back button

Step-by-step code:

  1. HomeScreen.kt (new file or in MainActivity):
    Kotlin
  2. ResultScreen.kt:
    Kotlin
  3. Run it!
    • Enter bill (e.g., 500), slide tip to 20%, calculate → See result screen.
    • Rotate screen → State survives thanks to remember + Navigation.
    • Press back → Returns to input (NavController handles it).

Enhancements to try:

  • Add validation (if bill empty → show error).
  • Use shared ViewModel for bill data.
  • Add icons (use Icon composable).

You’ve built a real multi-screen app! No XML fragments, pure Compose + type-safe navigation.

Questions? Error on toRoute? Want to add a third screen (history)? Or explain ViewModel integration deeper? Tell me — next chapter: State management & interactivity. You’re rocking this! 🚀💪

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *