Skip to content

Commit 8407d95

Browse files
authored
Merge pull request #15 from android/adaptive-navigation
Adaptive navigation
2 parents 38e6c56 + d171489 commit 8407d95

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1111
-22
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea/caches
5+
/.idea/libraries
6+
/.idea/modules.xml
7+
/.idea/workspace.xml
8+
/.idea/navEditor.xml
9+
/.idea/assetWizardSettings.xml
10+
.DS_Store
11+
/build
12+
/captures
13+
.externalNativeBuild
14+
.cxx
15+
local.properties
16+
/.idea/

AdaptiveNavigationSample/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Navigation Suite Scaffold with icons vertical alignment
2+
3+
This sample showcases how to leverage the new version of the `NavigationSuiteScaffold` to align the
4+
icons vertically to the top, center or bottom of the `NavRail`.
5+
6+
The core changes to an existing codebase are:
7+
- Use the `navigationItems` parameter to provide the entries, together with the constructor `NavigationSuiteItem` rather than the `item()` helper function
8+
- Pass the `Arrangement.Vertical` value to the `navigationItemVerticalArrangement` parameter in the `NavigationSuiteScaffold` declaration.
9+
10+
## Screenshot
11+
![Animation of the Navigation Suite Scaffold changing arrangement based on user's choice](/resources/scaffold_animated.gif)
12+
13+
### License
14+
15+
```
16+
Copyright 2025 The Android Open Source Project
17+
18+
Licensed under the Apache License, Version 2.0 (the "License");
19+
you may not use this file except in compliance with the License.
20+
You may obtain a copy of the License at
21+
22+
https://www.apache.org/licenses/LICENSE-2.0
23+
24+
Unless required by applicable law or agreed to in writing, software
25+
distributed under the License is distributed on an "AS IS" BASIS,
26+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27+
See the License for the specific language governing permissions and
28+
limitations under the License.
29+
30+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
plugins {
2+
alias(libs.plugins.android.application)
3+
alias(libs.plugins.kotlin.android)
4+
alias(libs.plugins.kotlin.compose)
5+
}
6+
7+
android {
8+
namespace = "com.google.sample.adaptivenavigationsample"
9+
compileSdk = 36
10+
11+
defaultConfig {
12+
applicationId = "com.google.sample.adaptivenavigationsample"
13+
minSdk = 24
14+
targetSdk = 36
15+
versionCode = 1
16+
versionName = "1.0"
17+
18+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19+
}
20+
21+
buildTypes {
22+
release {
23+
isMinifyEnabled = false
24+
proguardFiles(
25+
getDefaultProguardFile("proguard-android-optimize.txt"),
26+
"proguard-rules.pro"
27+
)
28+
}
29+
}
30+
compileOptions {
31+
sourceCompatibility = JavaVersion.VERSION_11
32+
targetCompatibility = JavaVersion.VERSION_11
33+
}
34+
kotlinOptions {
35+
jvmTarget = "11"
36+
}
37+
buildFeatures {
38+
compose = true
39+
}
40+
}
41+
42+
dependencies {
43+
44+
implementation(libs.androidx.core.ktx)
45+
implementation(libs.androidx.lifecycle.runtime.ktx)
46+
implementation(libs.androidx.activity.compose)
47+
implementation(platform(libs.androidx.compose.bom))
48+
implementation(libs.androidx.ui)
49+
implementation(libs.androidx.ui.graphics)
50+
implementation(libs.androidx.ui.tooling.preview)
51+
implementation(libs.androidx.material3)
52+
implementation(libs.androidx.material3.adaptive)
53+
implementation(libs.androidx.material3.adaptive.navigation.suite)
54+
implementation(libs.androidx.material.icons)
55+
implementation(libs.androidx.material.icons.extended)
56+
testImplementation(libs.junit)
57+
androidTestImplementation(libs.androidx.junit)
58+
androidTestImplementation(libs.androidx.espresso.core)
59+
androidTestImplementation(platform(libs.androidx.compose.bom))
60+
androidTestImplementation(libs.androidx.ui.test.junit4)
61+
debugImplementation(libs.androidx.ui.tooling)
62+
debugImplementation(libs.androidx.ui.test.manifest)
63+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
<application
5+
android:allowBackup="true"
6+
android:dataExtractionRules="@xml/data_extraction_rules"
7+
android:fullBackupContent="@xml/backup_rules"
8+
android:icon="@mipmap/ic_launcher"
9+
android:label="@string/app_name"
10+
android:roundIcon="@mipmap/ic_launcher_round"
11+
android:supportsRtl="true"
12+
android:theme="@style/Theme.AdaptiveNavigationSample">
13+
<activity
14+
android:name=".MainActivity"
15+
android:exported="true"
16+
android:label="@string/app_name"
17+
android:theme="@style/Theme.AdaptiveNavigationSample">
18+
<intent-filter>
19+
<action android:name="android.intent.action.MAIN" />
20+
21+
<category android:name="android.intent.category.LAUNCHER" />
22+
</intent-filter>
23+
</activity>
24+
</application>
25+
26+
</manifest>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package com.google.sample.adaptivenavigationsample
2+
3+
/*
4+
* Copyright 2025 The Android Open Source Project
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import android.os.Bundle
20+
import androidx.activity.ComponentActivity
21+
import androidx.activity.compose.setContent
22+
import androidx.activity.enableEdgeToEdge
23+
import androidx.annotation.StringRes
24+
import androidx.compose.foundation.layout.Arrangement
25+
import androidx.compose.foundation.layout.Column
26+
import androidx.compose.foundation.layout.Row
27+
import androidx.compose.foundation.layout.fillMaxSize
28+
import androidx.compose.foundation.layout.fillMaxWidth
29+
import androidx.compose.foundation.layout.height
30+
import androidx.compose.foundation.layout.padding
31+
import androidx.compose.foundation.selection.selectable
32+
import androidx.compose.material.icons.Icons
33+
import androidx.compose.material.icons.filled.AccountBox
34+
import androidx.compose.material.icons.filled.Favorite
35+
import androidx.compose.material.icons.filled.Home
36+
import androidx.compose.material.icons.filled.ShoppingCart
37+
import androidx.compose.material.icons.outlined.AccountBox
38+
import androidx.compose.material.icons.outlined.Favorite
39+
import androidx.compose.material.icons.outlined.Home
40+
import androidx.compose.material.icons.outlined.ShoppingCart
41+
import androidx.compose.material3.Icon
42+
import androidx.compose.material3.MaterialTheme
43+
import androidx.compose.material3.RadioButton
44+
import androidx.compose.material3.Text
45+
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItem
46+
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
47+
import androidx.compose.runtime.Composable
48+
import androidx.compose.runtime.getValue
49+
import androidx.compose.runtime.mutableIntStateOf
50+
import androidx.compose.runtime.mutableStateOf
51+
import androidx.compose.runtime.remember
52+
import androidx.compose.runtime.setValue
53+
import androidx.compose.ui.Alignment
54+
import androidx.compose.ui.Modifier
55+
import androidx.compose.ui.graphics.vector.ImageVector
56+
import androidx.compose.ui.res.stringResource
57+
import androidx.compose.ui.semantics.Role
58+
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
59+
import androidx.compose.ui.unit.dp
60+
import com.google.sample.adaptivenavigationsample.ui.theme.AdaptiveNavigationSampleTheme
61+
62+
enum class AppDestinations(
63+
@StringRes val label: Int,
64+
val icon: ImageVector,
65+
val iconSelected: ImageVector,
66+
@StringRes val contentDescription: Int
67+
) {
68+
HOME(R.string.home, Icons.Outlined.Home, Icons.Filled.Home, R.string.home), FAVORITES(
69+
R.string.favorites, Icons.Outlined.Favorite, Icons.Filled.Favorite, R.string.favorites
70+
),
71+
SHOPPING(
72+
R.string.shopping, Icons.Outlined.ShoppingCart, Icons.Filled.ShoppingCart, R.string.shopping
73+
),
74+
PROFILE(
75+
R.string.profile, Icons.Outlined.AccountBox, Icons.Filled.AccountBox, R.string.profile
76+
),
77+
}
78+
79+
class MainActivity : ComponentActivity() {
80+
override fun onCreate(savedInstanceState: Bundle?) {
81+
super.onCreate(savedInstanceState)
82+
enableEdgeToEdge()
83+
setContent {
84+
AdaptiveNavigationSampleTheme {
85+
NavigationSample()
86+
}
87+
}
88+
}
89+
}
90+
91+
@Composable
92+
fun NavigationSample() {
93+
var selectedItem by remember { mutableIntStateOf(0) }
94+
val options = listOf(
95+
"Top" to Arrangement.Top, "Center" to Arrangement.Center, "Bottom" to Arrangement.Bottom
96+
)
97+
98+
val (verticalArrangement, onOptionSelected) = remember { mutableStateOf(options[1]) }
99+
100+
NavigationSuiteScaffold(
101+
modifier = Modifier.fillMaxSize(),
102+
navigationItems = {
103+
AppDestinations.entries.forEachIndexed { index, item ->
104+
NavigationSuiteItem(
105+
icon = {
106+
val vector = if (index == selectedItem) {
107+
item.iconSelected
108+
} else {
109+
item.icon
110+
}
111+
Icon(
112+
vector, contentDescription = stringResource(item.contentDescription)
113+
)
114+
},
115+
label = { Text(stringResource(item.label)) },
116+
selected = index == selectedItem,
117+
onClick = { selectedItem = index })
118+
}
119+
},
120+
navigationItemVerticalArrangement = verticalArrangement.second
121+
) {
122+
Column(
123+
modifier = Modifier
124+
.fillMaxSize()
125+
.padding(horizontal = 16.dp),
126+
verticalArrangement = Arrangement.Center
127+
) {
128+
Text("NavRail icons arrangement", style = MaterialTheme.typography.headlineSmall)
129+
options.forEach { item ->
130+
Row(
131+
Modifier
132+
.fillMaxWidth()
133+
.height(56.dp)
134+
.selectable(
135+
selected = (item == verticalArrangement), onClick = {
136+
onOptionSelected(item)
137+
}, role = Role.RadioButton
138+
)
139+
.padding(horizontal = 16.dp),
140+
verticalAlignment = Alignment.CenterVertically
141+
) {
142+
RadioButton(
143+
selected = (item == verticalArrangement), onClick = null
144+
)
145+
Text(
146+
text = item.first,
147+
style = MaterialTheme.typography.bodyLarge,
148+
modifier = Modifier.padding(start = 16.dp)
149+
)
150+
}
151+
}
152+
}
153+
}
154+
}
155+
156+
@PreviewScreenSizes
157+
@Composable
158+
fun NavigationSamplePreview() {
159+
AdaptiveNavigationSampleTheme {
160+
NavigationSample()
161+
}
162+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.google.sample.adaptivenavigationsample.ui.theme
2+
3+
/*
4+
* Copyright 2025 The Android Open Source Project
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import androidx.compose.ui.graphics.Color
20+
21+
val Purple80 = Color(0xFFD0BCFF)
22+
val PurpleGrey80 = Color(0xFFCCC2DC)
23+
val Pink80 = Color(0xFFEFB8C8)
24+
25+
val Purple40 = Color(0xFF6650a4)
26+
val PurpleGrey40 = Color(0xFF625b71)
27+
val Pink40 = Color(0xFF7D5260)

0 commit comments

Comments
 (0)