Add onboarding image carousel

Bundle the rotating onboarding illustrations (light/dark) and wire them
into the Android onboarding info component, matching the iOS carousel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 01:03:53 +02:00
parent 38118ebc36
commit 0aa3184406
17 changed files with 71 additions and 5 deletions

View File

@@ -0,0 +1,59 @@
package app.voltplan.cable.ui.components
import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import kotlinx.coroutines.delay
/**
* Auto-advancing onboarding illustration carousel. Mirrors iOS `OnboardingCarouselView`:
* cycles through the images every 8s with a horizontal slide. A single image stays static.
*/
@Composable
fun OnboardingCarousel(
@DrawableRes images: List<Int>,
modifier: Modifier = Modifier,
) {
if (images.isEmpty()) return
var index by remember(images) { mutableIntStateOf(0) }
if (images.size > 1) {
LaunchedEffect(images) {
while (true) {
delay(8_000)
index = (index + 1) % images.size
}
}
}
AnimatedContent(
targetState = index,
transitionSpec = {
slideInHorizontally(tween(800)) { it } togetherWith
slideOutHorizontally(tween(800)) { -it }
},
label = "onboarding-carousel",
modifier = modifier,
) { i ->
Image(
painter = painterResource(images[i]),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier.fillMaxSize(),
)
}
}

View File

@@ -1,10 +1,12 @@
package app.voltplan.cable.ui.components
import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
@@ -29,6 +31,7 @@ fun OnboardingInfo(
onPrimary: () -> Unit,
secondaryLabel: String? = null,
onSecondary: (() -> Unit)? = null,
@DrawableRes images: List<Int> = emptyList(),
modifier: Modifier = Modifier,
) {
Column(
@@ -36,7 +39,11 @@ fun OnboardingInfo(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Icon(icon, contentDescription = null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(56.dp))
if (images.isNotEmpty()) {
OnboardingCarousel(images = images, modifier = Modifier.fillMaxWidth().height(200.dp))
} else {
Icon(icon, contentDescription = null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(56.dp))
}
Spacer(Modifier.size(16.dp))
Text(title, style = MaterialTheme.typography.titleLarge, textAlign = TextAlign.Center)
Spacer(Modifier.size(8.dp))

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB