CS 3180 Mobile Application Development

Week 7 Monday: Grids and Navigation

CS 3180 Mobile Application Development

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Today’s Topics

  1. Grids with LazyVerticalGrid
  2. Navigation Compose essentials
  3. Reminder: Chapter 7 quiz on Canvas
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Part 1: Grids in Compose

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why a Grid?

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why a Grid? (continued)

  • Many equal choices on screen
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why a Grid? (continued)

  • Many equal choices on screen
  • Efficient use of horizontal space
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why a Grid? (continued)

  • Many equal choices on screen
  • Efficient use of horizontal space
  • Visual scanning is faster than long lists
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why a Grid? (continued)

  • Many equal choices on screen
  • Efficient use of horizontal space
  • Visual scanning is faster than long lists
  • Ideal for dashboards and launchers
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Grid Basics: Fixed Columns

LazyVerticalGrid(
    columns = GridCells.Fixed(3),
    contentPadding = PaddingValues(16.dp),
    verticalArrangement = Arrangement.spacedBy(12.dp),
    horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
    items(holes, key = { it.number }) { hole ->
        HoleTile(hole)
    }
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Grid Basics: Adaptive Columns

LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 96.dp)
) {
    items(actions, key = { it.id }) { action ->
        ActionTile(action)
    }
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Tile Composable Example

@Composable
fun HoleTile(hole: Hole) {
    Surface(
        tonalElevation = 2.dp,
        shape = RoundedCornerShape(12.dp)
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(12.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text("Hole ${hole.number}", style = MaterialTheme.typography.titleMedium)
            Text("Par ${hole.par}", style = MaterialTheme.typography.bodyMedium)
        }
    }
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Grid Spacing Rules

LazyVerticalGrid(
    columns = GridCells.Fixed(3),
    contentPadding = PaddingValues(16.dp),
    verticalArrangement = Arrangement.spacedBy(12.dp),
    horizontalArrangement = Arrangement.spacedBy(12.dp)
) { /* items */ }
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Grid Mistakes

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Grid Mistakes (continued)

  • No stable keys for items
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Grid Mistakes (continued)

  • No stable keys for items
  • Tiles too small for touch targets
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Grid Mistakes (continued)

  • No stable keys for items
  • Tiles too small for touch targets
  • Heavy work inside tile composables
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Grid Mistakes (continued)

  • No stable keys for items
  • Tiles too small for touch targets
  • Heavy work inside tile composables
  • Forgetting padding for system bars
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Part 2: Navigation Compose

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Navigation?

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Navigation? (continued)

  • Breaks complex flows into focused screens
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Navigation? (continued)

  • Breaks complex flows into focused screens
  • Creates consistent back behavior
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Navigation? (continued)

  • Breaks complex flows into focused screens
  • Creates consistent back behavior
  • Supports deep links and arguments
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Navigation? (continued)

  • Breaks complex flows into focused screens
  • Creates consistent back behavior
  • Supports deep links and arguments
  • Simplifies UI state scope
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development
  • NavController
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development
  • NavController
  • NavHost
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development
  • NavController
  • NavHost
  • NavGraph
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development
  • NavController
  • NavHost
  • NavGraph
  • Destination composable screens
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Basic Navigation Setup

@Composable
fun GolfApp() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = "home"
    ) {
        composable("home") { HomeScreen(navController) }
        composable("scores") { ScoresScreen(navController) }
    }
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development
composable(
    route = "hole/{holeNumber}",
    arguments = listOf(navArgument("holeNumber") { type = NavType.IntType })
) { backStackEntry ->
    val holeNumber = backStackEntry.arguments?.getInt("holeNumber") ?: 1
    HoleDetailScreen(holeNumber)
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development
val route = "hole/${hole.number}"
navController.navigate(route)
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Type-Safe Navigation (Modern Approach)

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Type-Safe Navigation?

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Type-Safe Navigation? (continued)

  • Compile-time route validation
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Type-Safe Navigation? (continued)

  • Compile-time route validation
  • Type-safe argument passing
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Type-Safe Navigation? (continued)

  • Compile-time route validation
  • Type-safe argument passing
  • Centralized route definitions
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Why Type-Safe Navigation? (continued)

  • Compile-time route validation
  • Type-safe argument passing
  • Centralized route definitions
  • Better IDE support and autocomplete
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Defining Type-Safe Routes

sealed class Routes {
    @Serializable
    data object Home

    @Serializable
    data object Scores

    @Serializable
    data class HoleDetail(
        val holeNumber: Int,
        val par: Int
    )
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Type-Safe NavHost Setup

@Composable
fun GolfApp() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = Routes.Home
    ) {
        composable<Routes.Home> {
            HomeScreen(
                onNavigateToScores = {
                    navController.navigate(Routes.Scores)
                }
            )
        }
    }
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Type-Safe Composable with Arguments

composable<Routes.HoleDetail> { backStackEntry ->
    val route: Routes.HoleDetail = backStackEntry.toRoute()

    HoleDetailScreen(
        holeNumber = route.holeNumber,
        par = route.par
    )
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Type-Safe Navigation Call

@Composable
fun HomeScreen(onNavigateToHole: (Int, Int) -> Unit) {
    Button(
        onClick = {
            navController.navigate(
                Routes.HoleDetail(
                    holeNumber = 7,
                    par = 4
                )
            )
        }
    ) {
        Text("View Hole 7")
    }
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Complete Type-Safe Example

sealed class Routes {
    @Serializable
    data object List

    @Serializable
    data class Detail(val petId: Int)

    @Serializable
    data class Adopt(val petId: Int)
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Complete Example: NavHost

@Composable
fun PetApp() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = Routes.List
    ) {
        composable<Routes.List> {
            ListScreen(
                onImageClick = { pet ->
                    navController.navigate(Routes.Detail(pet.id))
                }
            )
        }
        // continued...
    }
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Complete Example: Detail Screen

composable<Routes.Detail> { backStackEntry ->
    val details: Routes.Detail = backStackEntry.toRoute()

    DetailScreen(
        petId = details.petId,
        onAdoptClick = {
            navController.navigate(
                Routes.Adopt(details.petId)
            )
        },
        onUpClick = {
            navController.navigateUp()
        }
    )
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Complete Example: Adopt Screen

composable<Routes.Adopt> { backStackEntry ->
    val adopt: Routes.Adopt = backStackEntry.toRoute()

    AdoptScreen(
        petId = adopt.petId,
        onUpClick = {
            navController.navigateUp()
        }
    )
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Type-Safe vs String-Based Routes

Approach Pros Cons
String-based Simple for basic cases Error-prone, no compile-time checks
Type-safe Compile-time safety, refactor-friendly More setup code
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Required Dependencies

// In build.gradle.kts
dependencies {
    implementation("androidx.navigation:navigation-compose:2.7.+")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.+")
}

// Apply plugin
plugins {
    kotlin("plugin.serialization")
}
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Back Stack Essentials

navController.popBackStack()
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Top App Bar with Back

TopAppBar(
    title = { Text("Hole Details") },
    navigationIcon = {
        IconButton(onClick = { navController.popBackStack() }) {
            Icon(Icons.Default.ArrowBack, contentDescription = "Back")
        }
    }
)
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Navigation Pitfalls

Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Navigation Pitfalls (continued)

  • Routes that don’t match exactly
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Navigation Pitfalls (continued)

  • Routes that don’t match exactly
  • Forgetting to pass required arguments
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Navigation Pitfalls (continued)

  • Routes that don’t match exactly
  • Forgetting to pass required arguments
  • Creating multiple NavController instances
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Common Navigation Pitfalls (continued)

  • Routes that don’t match exactly
  • Forgetting to pass required arguments
  • Creating multiple NavController instances
  • Doing heavy work inside composables on each navigation
Week 7 Monday: Grids and Navigation
CS 3180 Mobile Application Development

Wrap‑Up

  • Grids provide quick, visual selection
  • Navigation connects screens cleanly
  • Next: hands‑on demo on Wednesday
Week 7 Monday: Grids and Navigation

SPEAKER NOTES: Welcome to Week 7! Today we connect two essentials for the Hope Foundation golf app: grid layouts for quick selection and navigation for multi‑screen flows.

SPEAKER NOTES: We’ll start with grids for layout and end with screen‑to‑screen navigation. Quiz reminder at the end.

SPEAKER NOTES: Grids are perfect for quick, visual selection in the golf app: hole numbers, teams, or score actions.

SPEAKER NOTES: Let’s reason about when grid layouts make sense.

SPEAKER NOTES: Example: selecting a hole number or quick actions.

SPEAKER NOTES: Phones are narrow, but grids help us use width for faster scanning.

SPEAKER NOTES: Users can scan a 3x6 grid of holes faster than a 18‑row list.

SPEAKER NOTES: Think of a “Tournament Hub” screen with quick actions.

SPEAKER NOTES: Fixed columns are predictable: 3 columns across on phones for 18 holes.

SPEAKER NOTES: Adaptive columns let the device size decide how many columns fit.

SPEAKER NOTES: Use a dedicated tile composable for reuse and clean grid code.

SPEAKER NOTES: Spacing creates visual rhythm. Without it, tiles look cramped and hard to tap.

SPEAKER NOTES: Let’s avoid the most common issues that make grids feel broken or slow.

SPEAKER NOTES: Keys prevent state leaks when the list changes.

SPEAKER NOTES: Aim for at least 48dp touch targets.

SPEAKER NOTES: Avoid decoding images or doing complex calculations inside each tile.

SPEAKER NOTES: Use `Scaffold` padding or `WindowInsets` so content isn’t hidden behind bars.

SPEAKER NOTES: Now we connect screens: dashboard → hole detail → team summary.

SPEAKER NOTES: A single screen can’t handle the entire tournament workflow.

SPEAKER NOTES: Each screen does one job well.

SPEAKER NOTES: Users expect Android back to work across the app.

SPEAKER NOTES: We can navigate straight to “Hole 7” from a notification.

SPEAKER NOTES: Each screen owns the state it needs.

SPEAKER NOTES: There are four core concepts you must know.

SPEAKER NOTES: The controller performs navigation actions.

SPEAKER NOTES: The host displays the current destination.

SPEAKER NOTES: The graph defines all destinations and routes.

SPEAKER NOTES: Each destination is a composable function.

SPEAKER NOTES: This is the minimal setup: controller + host + routes.

SPEAKER NOTES: Arguments let us navigate to a specific hole detail screen.

SPEAKER NOTES: Build the route string using the argument value.

SPEAKER NOTES: The string-based routes we just saw work, but they're error-prone. Type-safe navigation using serializable data classes gives us compile-time safety.

SPEAKER NOTES: Let's understand the benefits of the modern navigation approach.

SPEAKER NOTES: No more typos in route strings causing runtime crashes.

SPEAKER NOTES: Arguments are part of the data class, so you can't pass the wrong type.

SPEAKER NOTES: All routes live in one sealed class, making them easy to find and refactor.

SPEAKER NOTES: Your IDE can help you navigate correctly with autocomplete on route names.

SPEAKER NOTES: Each route is a serializable data class or object. Arguments become properties.

SPEAKER NOTES: Notice we use Routes.Home directly instead of a string. Type-safe at the start destination.

SPEAKER NOTES: The toRoute() extension extracts our typed route object. No manual parsing of arguments!

SPEAKER NOTES: Creating the route is just creating a data class instance. The compiler ensures you provide all required arguments.

SPEAKER NOTES: Here's a complete example for a pet adoption app. Three screens with type-safe routes.

SPEAKER NOTES: The list screen navigates to detail, passing the pet ID in a type-safe way.

SPEAKER NOTES: Extract the route, use its properties, and navigate forward to adoption or back to list.

SPEAKER NOTES: Same pattern: extract typed route, use the arguments. Clean and type-safe throughout.

SPEAKER NOTES: For small apps, strings work. For production apps with many screens, type-safe navigation prevents bugs.

SPEAKER NOTES: Don't forget to add the serialization plugin and dependency for type-safe navigation to work.

SPEAKER NOTES: Use this for back actions on app bars or buttons. Works the same for both navigation approaches.

SPEAKER NOTES: Compose doesn’t handle back automatically for custom app bars. We wire it.

SPEAKER NOTES: Navigation issues are often subtle. Watch for these.

SPEAKER NOTES: A mismatch between "hole/{id}" and "holes/{id}" will crash.

SPEAKER NOTES: If a route expects an argument, the navigation call must include it.

SPEAKER NOTES: Keep a single controller at the app root.

SPEAKER NOTES: Use ViewModels for heavy loading and preserve state.

SPEAKER NOTES: Close with the connection to the Hope Foundation app and preview the demo.