diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index d9b3c2616..32d12673f 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -23,8 +23,6 @@ updates:
- "org.jetbrains.kotlin:kotlin-stdlib"
- "org.jetbrains.kotlin:kotlin-gradle-plugin"
- "org.jetbrains.compose.compiler"
- - "com.google.dagger.hilt.android"
- - "com.google.devtools.ksp"
# Request that all AGP dependent upgrades are grouped together
agp:
patterns:
diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml
index fab2e6241..88244527e 100644
--- a/android/gradle/libs.versions.toml
+++ b/android/gradle/libs.versions.toml
@@ -15,12 +15,13 @@ kotlin = "2.3.10"
# https://github.com/jeremymailen/kotlinter-gradle/blob/master/README.md#compatibility
kotlinter = "5.0.1"
-activity = "1.12.2"
+activity = "1.13.0"
+adaptive-navigation3 = "1.3.0-alpha09"
appcompat = "1.7.1"
coil = "2.7.0"
-composeBom = "2025.12.01"
+composeBom = "2026.03.00"
ktlint-compose = "0.5.6"
-coreKtx = "1.17.0"
+coreKtx = "1.18.0"
detekt = "1.23.8"
dokka = "2.1.0"
inject = "1"
@@ -55,6 +56,7 @@ androidx-compose-material3-windowSize = { group = "androidx.compose.material3",
androidx-compose-uiTest = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-lint-gradle = { module = "androidx.lint:lint-gradle", version.ref = "lint-gradle" }
+androidx-nav-adaptive = { module = "androidx.compose.material3.adaptive:adaptive-navigation3", version.ref = "adaptive-navigation3" }
androidx-nav-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav" }
androidx-nav-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
diff --git a/android/sample/build.gradle.kts b/android/sample/build.gradle.kts
index 2292436e8..f51cfe3e8 100644
--- a/android/sample/build.gradle.kts
+++ b/android/sample/build.gradle.kts
@@ -53,4 +53,5 @@ dependencies {
implementation(libs.androidx.nav.runtime)
implementation(libs.androidx.nav.ui)
+ implementation(libs.androidx.nav.adaptive)
}
\ No newline at end of file
diff --git a/android/sample/detekt-baseline.xml b/android/sample/detekt-baseline.xml
index 7246ba85f..7264c96c6 100644
--- a/android/sample/detekt-baseline.xml
+++ b/android/sample/detekt-baseline.xml
@@ -3,7 +3,7 @@
CognitiveComplexMethod:ChipsPreview.kt$@OptIn(ExperimentalLayoutApi::class) @Composable internal fun ChipsPreview(onBackPress: () -> Unit, modifier: Modifier = Modifier)
- LongMethod:MainActivity.kt$MainActivity$override fun onCreate(savedInstanceState: Bundle?)
+ LongMethod:MainActivity.kt$MainActivity$@OptIn(ExperimentalMaterial3AdaptiveApi::class) override fun onCreate(savedInstanceState: Bundle?)
UnusedPrivateMember:ButtonPreview.kt$@PreviewLightDark @Composable private fun Preview()
UnusedPrivateMember:ChipsPreview.kt$@PreviewLightDark @Composable private fun Preview()
UnusedPrivateMember:PromoStickerPreview.kt$@PreviewLightDark @Composable private fun Preview()
diff --git a/android/sample/src/main/kotlin/com/gu/source/Home.kt b/android/sample/src/main/kotlin/com/gu/source/Home.kt
index dba8919ae..08cdc096e 100644
--- a/android/sample/src/main/kotlin/com/gu/source/Home.kt
+++ b/android/sample/src/main/kotlin/com/gu/source/Home.kt
@@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@@ -32,7 +34,7 @@ internal fun Home(modifier: Modifier = Modifier, navigate: (Destination) -> Unit
topBar = {
SampleTopAppBar {
Text(
- text = "Welcome to Source.",
+ text = "Source",
style = Source.Typography.HeadlineMedium20,
color = AppColour(
light = Source.Palette.Brand400,
@@ -49,7 +51,8 @@ internal fun Home(modifier: Modifier = Modifier, navigate: (Destination) -> Unit
Column(
modifier = Modifier
.fillMaxSize()
- .padding(paddingValues + PaddingValues(16.dp)),
+ .padding(paddingValues + PaddingValues(16.dp))
+ .verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
diff --git a/android/sample/src/main/kotlin/com/gu/source/MainActivity.kt b/android/sample/src/main/kotlin/com/gu/source/MainActivity.kt
index b30196aac..14b8a32e4 100644
--- a/android/sample/src/main/kotlin/com/gu/source/MainActivity.kt
+++ b/android/sample/src/main/kotlin/com/gu/source/MainActivity.kt
@@ -2,13 +2,16 @@ package com.gu.source
import android.os.Bundle
import androidx.activity.ComponentActivity
+import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.slideInHorizontally
-import androidx.compose.animation.slideOutHorizontally
-import androidx.compose.animation.togetherWith
+import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
+import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
+import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective
+import androidx.compose.material3.adaptive.navigation3.ListDetailSceneStrategy
+import androidx.compose.material3.adaptive.navigation3.rememberListDetailSceneStrategy
+import androidx.compose.runtime.remember
+import androidx.compose.ui.unit.dp
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.ui.NavDisplay
import com.gu.source.daynight.AppColourMode
@@ -25,6 +28,7 @@ import com.gu.source.previews.RatingPreview
import com.gu.source.previews.TextButtonPreview
internal class MainActivity : ComponentActivity() {
+ @OptIn(ExperimentalMaterial3AdaptiveApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
@@ -32,62 +36,94 @@ internal class MainActivity : ComponentActivity() {
setContent {
val navigator = rememberNavigator(Destination.Home)
+ val windowInfo = currentWindowAdaptiveInfo()
+ val isHomeOnTop = navigator.backstack.lastOrNull() is Destination.Home
+
+ val directive = remember(windowInfo, isHomeOnTop) {
+ val isCompact = !windowInfo.windowSizeClass
+ .isWidthAtLeastBreakpoint(widthDpBreakpoint = 600)
+ calculatePaneScaffoldDirective(windowInfo)
+ .copy(
+ horizontalPartitionSpacerSize = 0.dp,
+ maxHorizontalPartitions = if (isCompact || isHomeOnTop) 1 else 2,
+ )
+ }
+
AppColourMode { modifier ->
NavDisplay(
backStack = navigator.backstack,
onBack = { navigator.popBackStack() },
modifier = modifier,
+ sceneStrategy = rememberListDetailSceneStrategy(
+ shouldHandleSinglePaneLayout = true,
+ directive = directive,
+ ),
entryProvider = entryProvider {
- entry(Destination.Home) {
- Home { navigator.navigate(it) }
+ entry(
+ metadata = ListDetailSceneStrategy.listPane(),
+ ) {
+ Home {
+ if (!isHomeOnTop) {
+ navigator.popBackStack()
+ }
+ navigator.navigate(it)
+ }
}
- entry(Destination.PalettePreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
PalettePreview(navigator::popBackStack)
}
- entry(Destination.PagerProgressBarPreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
ImagePagerWithProgressIndicator(navigator::popBackStack)
}
- entry(Destination.ButtonsPreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
ButtonPreview(navigator::popBackStack)
}
- entry(Destination.TextButtonPreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
TextButtonPreview(navigator::popBackStack)
}
- entry(Destination.IconsPreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
IconsPreview(navigator::popBackStack)
}
- entry(Destination.AlertBannerPreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
AlertBannerPreview(navigator::popBackStack)
}
- entry(Destination.ChipsPreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
ChipsPreview(navigator::popBackStack)
}
- entry(Destination.BadgesPreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
PromoStickerPreview(navigator::popBackStack)
}
- entry(Destination.StarRatingsPreview) {
+ entry(
+ metadata = ListDetailSceneStrategy.detailPane(),
+ ) {
+ BackHandler { navigator.popBackStack() }
RatingPreview(navigator::popBackStack)
}
},
- transitionSpec = {
- // Slide in from right when navigating forward
- slideInHorizontally(initialOffsetX = { it }) togetherWith
- slideOutHorizontally(targetOffsetX = { -it })
- },
- popTransitionSpec = {
- // Slide in from left when navigating back
- fadeIn(
- spring(dampingRatio = 1.0f, stiffness = 1600.0f),
- ) togetherWith
- slideOutHorizontally(targetOffsetX = { it })
- },
- predictivePopTransitionSpec = {
- // Slide in from left when navigating back
- fadeIn(
- spring(dampingRatio = 1.0f, stiffness = 1600.0f),
- ) togetherWith
- slideOutHorizontally(targetOffsetX = { it })
- },
)
}
}
diff --git a/android/sample/src/main/kotlin/com/gu/source/previews/PromoStickerPreview.kt b/android/sample/src/main/kotlin/com/gu/source/previews/PromoStickerPreview.kt
index 23411ffa0..c78111d98 100644
--- a/android/sample/src/main/kotlin/com/gu/source/previews/PromoStickerPreview.kt
+++ b/android/sample/src/main/kotlin/com/gu/source/previews/PromoStickerPreview.kt
@@ -21,7 +21,7 @@ import com.gu.source.foundation.typography.TextSansBold17
internal fun PromoStickerPreview(onBackPress: () -> Unit, modifier: Modifier = Modifier) {
PreviewScaffold("Promo sticker", onBackPress, modifier) {
LazyVerticalGrid(
- columns = GridCells.Fixed(getGridCount()),
+ columns = GridCells.Fixed(2),
modifier = it,
verticalArrangement = Arrangement.Center,
) {