-
Notifications
You must be signed in to change notification settings - Fork 8
Open
Description
Hello, I have a use case where I have an app tour/overview animation showing to the user.
This is a 1 big animation and it is divided by multiple segments (pages of each feature). During composition (screen open) I want to set the 1st page segment on the animation and play only that segment. Then wait until user clicks on page 2.
However I discovered an issue that DotLottie plays the full animation during composition if you call controller.play() in a LaunchedEffect() . I use it to set the segment and start it, but it just plays the whole thing.
Weirdly enough, the issue is solved if I add delay(100) to my LaunchedEffect.
Can this be cause by DotLottieController.shouldPlayOnInit?
Compose code:
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("LongParameterList", "LongMethod")
@Composable
fun TourScreen(
state: TourViewModelState,
onBack: () -> Unit,
onPrevious: () -> Unit,
onNext: () -> Unit,
onSkipTour: () -> Unit,
modifier: Modifier = Modifier,
) {
var currentPage by remember { mutableIntStateOf(state.currentPage) }
val pagerState = rememberPagerState { state.pageCount }
val controller = remember { DotLottieController() }
LaunchedEffect(Unit) {
delay(100)
val segment = getSegment(state.currentPage)
controller.setSegment(
firstFrame = segment.first,
lastFrame = segment.second,
)
controller.setPlayMode(Mode.FORWARD)
controller.play()
}
LaunchedEffect(state.currentPage) {
if (currentPage < state.currentPage) {
controller.stop()
val segment = getSegment(state.currentPage)
controller.setSegment(segment.first, segment.second)
controller.setPlayMode(Mode.FORWARD)
controller.play()
}
if (currentPage > state.currentPage) {
controller.stop()
val segment = getSegment(currentPage)
controller.setSegment(segment.first, segment.second)
controller.setPlayMode(Mode.REVERSE)
controller.play()
}
currentPage = state.currentPage
pagerState.animateScrollToPage(currentPage)
}
LaunchedEffect(pagerState) {
snapshotFlow { pagerState.targetPage }
.distinctUntilChanged()
.collect { newPage ->
when {
newPage > currentPage -> {
onNext()
}
newPage < currentPage -> {
onPrevious()
}
}
}
}
Scaffold(
modifier = modifier
.testTag("tour_layout")
.systemBarsPadding()
.semantics {
testTagsAsResourceId = true
},
topBar = {
TopAppBar(
navigationIcon = {
if (state.isBackActionVisible) {
IconButton(
onClick = onBack,
modifier = Modifier.testTag("tour_back"),
analyticsTag = "Back",
) {
Icon(
painter = painterResource(UICoreR.drawable.ic_arrow_back),
tint = MaterialTheme.colorScheme.onSurface,
contentDescription = stringResource(id = UICoreR.string.back_button_description),
)
}
}
},
)
},
content = { contentPadding ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.imePadding()
.padding(contentPadding),
) {
DotLottieAnimation(
controller = controller,
source = DotLottieSource.Asset("anim/app_tour/app_tour.lottie"),
modifier = Modifier
.fillMaxWidth()
.weight(1f),
)
HorizontalPager(
state = pagerState,
modifier = Modifier
.height(186.dp),
pageContent = { index ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize(),
) {
Text(
text = stringResource(getTitleResId(index)),
style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center,
modifier = Modifier.padding(
all = MaterialTheme.spaceDimensions.medium,
),
)
Text(
text = stringResource(getDescriptionResId(index)),
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center,
modifier = Modifier
.padding(
horizontal = MaterialTheme.spaceDimensions.medium,
),
)
}
},
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(horizontal = MaterialTheme.spaceDimensions.medium),
) {
OutlinedIconButton(
shape = Shapes.extraLarge,
enabled = state.isPreviousEnabled,
onClick = onPrevious,
content = {
Icon(
imageVector = Icons.Default.ChevronLeft,
contentDescription = null,
)
},
)
Spacer(Modifier.weight(1f))
PageIndicator(
pagesCount = state.pageCount,
currentPageIndex = state.currentPage,
modifier = Modifier
.semantics { currentPageIndex = currentPage },
)
Spacer(Modifier.weight(1f))
OutlinedIconButton(
enabled = state.isNextEnabled,
shape = Shapes.extraLarge,
onClick = onNext,
content = {
Icon(
imageVector = Icons.Default.ChevronRight,
contentDescription = null,
)
},
)
}
OutlinedButton(
modifier = Modifier
.fillMaxWidth()
.padding(MaterialTheme.spaceDimensions.medium)
.testTag("tour_skip_tour"),
label = stringResource(R.string.tour_skip_tour),
onClick = onSkipTour,
isSmall = true,
)
}
},
)
}
This is a fixed version for illustration purposes:
trimmed.mp4
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels