From e811ef875bdf700760c1b4903d3897faffbc1b05 Mon Sep 17 00:00:00 2001 From: Ash Nohe Date: Tue, 19 Mar 2024 15:30:18 -0700 Subject: [PATCH] add nav rail for large screen devices --- app/build.gradle.kts | 1 + .../android/samples/socialite/ui/home/Home.kt | 164 +++++++++++++----- gradle/libs.versions.toml | 2 + 3 files changed, 127 insertions(+), 40 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 26dadbf5..2ba138c7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -89,6 +89,7 @@ dependencies { implementation(libs.compose.ui.tooling.preview) implementation(libs.compose.material3) implementation(libs.compose.material.icons) + implementation(libs.compose.material3.windowsizeclass) androidTestImplementation(libs.compose.ui.test) debugImplementation(libs.compose.ui.tooling) debugImplementation(libs.compose.ui.test.manisfest) diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/home/Home.kt b/app/src/main/java/com/google/android/samples/socialite/ui/home/Home.kt index 491f7ad8..00170d1d 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/home/Home.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/home/Home.kt @@ -17,6 +17,8 @@ package com.google.android.samples.socialite.ui.home import androidx.annotation.StringRes +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ChatBubbleOutline @@ -26,10 +28,15 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.NavigationRail +import androidx.compose.material3.NavigationRailItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass +import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -38,6 +45,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -48,11 +56,27 @@ import androidx.navigation.compose.rememberNavController import com.google.android.samples.socialite.R import com.google.android.samples.socialite.ui.AnimationConstants import com.google.android.samples.socialite.ui.home.timeline.Timeline +import com.google.android.samples.socialite.ui.player.findActivity +@OptIn(ExperimentalMaterial3WindowSizeClassApi::class) @Composable fun Home( onChatClicked: (chatId: Long) -> Unit, modifier: Modifier = Modifier, +) { + val windowSizeClass = calculateWindowSizeClass( + activity = LocalContext.current.findActivity() + ).widthSizeClass + when (windowSizeClass) { + WindowWidthSizeClass.Compact -> CompactScreen(modifier, onChatClicked) + else -> LargeScreen(onChatClicked = onChatClicked) + } +} + +@Composable +private fun CompactScreen( + modifier: Modifier, + onChatClicked: (chatId: Long) -> Unit ) { var destination by rememberSaveable { mutableStateOf(Destination.Chats) } Scaffold( @@ -65,47 +89,26 @@ fun Home( ) }, ) { innerPadding -> - val navController = rememberNavController() - HomeBackground(modifier = Modifier.fillMaxSize()) - NavHost( - navController = navController, - startDestination = destination.route, + HomeContent(innerPadding, modifier, destination, onChatClicked) + } +} + +@Composable +private fun LargeScreen( + modifier: Modifier = Modifier, + onChatClicked: (chatId: Long) -> Unit +) { + var destination by rememberSaveable { mutableStateOf(Destination.Chats) } + Row(modifier = Modifier.fillMaxSize()) { + HomeNavigationRail( + currentDestination = destination.route, + onDestinationChanged = { destination = it } + ) + Scaffold( modifier = modifier, - ) { - composable( - route = Destination.Timeline.route, - enterTransition = { AnimationConstants.enterTransition }, - exitTransition = { AnimationConstants.exitTransition }, - ) { - Timeline( - contentPadding = innerPadding, - modifier = modifier, - ) - } - composable( - route = Destination.Chats.route, - enterTransition = { AnimationConstants.enterTransition }, - exitTransition = { AnimationConstants.exitTransition }, - ) { - val viewModel: HomeViewModel = hiltViewModel() - val chats by viewModel.chats.collectAsStateWithLifecycle() - ChatList( - chats = chats, - contentPadding = innerPadding, - onChatClicked = onChatClicked, - modifier = modifier, - ) - } - composable( - route = Destination.Settings.route, - enterTransition = { AnimationConstants.enterTransition }, - exitTransition = { AnimationConstants.exitTransition }, - ) { - Settings( - contentPadding = innerPadding, - modifier = Modifier.fillMaxSize(), - ) - } + topBar = { HomeAppBar(title = stringResource(destination.label)) } + ) {innerPadding -> + HomeContent(innerPadding, modifier, destination, onChatClicked) } } } @@ -125,6 +128,57 @@ private fun HomeAppBar( ) } +@Composable +private fun HomeContent( + innerPadding: PaddingValues, + modifier: Modifier, + destination: Destination, + onChatClicked: (chatId: Long) -> Unit +){ + val navController = rememberNavController() + HomeBackground(modifier = Modifier.fillMaxSize()) + NavHost( + navController = navController, + startDestination = destination.route, + modifier = modifier, + ) { + composable( + route = Destination.Timeline.route, + enterTransition = { AnimationConstants.enterTransition }, + exitTransition = { AnimationConstants.exitTransition }, + ) { + Timeline( + contentPadding = innerPadding, + modifier = modifier, + ) + } + composable( + route = Destination.Chats.route, + enterTransition = { AnimationConstants.enterTransition }, + exitTransition = { AnimationConstants.exitTransition }, + ) { + val viewModel: HomeViewModel = hiltViewModel() + val chats by viewModel.chats.collectAsStateWithLifecycle() + ChatList( + chats = chats, + contentPadding = innerPadding, + onChatClicked = onChatClicked, + modifier = modifier, + ) + } + composable( + route = Destination.Settings.route, + enterTransition = { AnimationConstants.enterTransition }, + exitTransition = { AnimationConstants.exitTransition }, + ) { + Settings( + contentPadding = innerPadding, + modifier = Modifier.fillMaxSize(), + ) + } + } +} + private enum class Destination( val route: String, @StringRes val label: Int, @@ -176,3 +230,33 @@ private fun HomeNavigationBar( } } } + +@Composable +private fun HomeNavigationRail( + currentDestination: String, + onDestinationChanged: (Destination) -> Unit, + modifier: Modifier = Modifier, +) { + NavigationRail( + modifier = modifier + ) { + for (destination in Destination.values()) { + val selected = currentDestination == destination.route + val label = stringResource(destination.label) + NavigationRailItem( + selected = selected, + onClick = { onDestinationChanged(destination) }, + icon = { + Icon( + imageVector = destination.imageVector, + contentDescription = label, + ) + }, + label = { + Text(text = label) + }, + alwaysShowLabel = false, + ) + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ed50c214..bd0e6e3e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,6 +45,7 @@ truth = "1.1.3" turbine = "1.0.0" uiautomator = "2.2.0" window = "1.2.0" +windowSizeClass = "1.2.1" [libraries] accompanist-painter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" } @@ -62,6 +63,7 @@ coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coi compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose_bom" } compose-material-icons = { group = "androidx.compose.material", name = "material-icons-extended" } compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } +compose-material3-windowsizeclass = {group = "androidx.compose.material3", name = "material3-window-size-class", version.ref = "windowSizeClass"} compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } compose-ui = { group = "androidx.compose.ui", name = "ui" } compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }