Skip to content

Commit

Permalink
[JetSurvey] Use AnimatedContent to slide in and out changes between q…
Browse files Browse the repository at this point in the history
…uestions (#842)

* Animate the changes between different question screens, as well as the end result screen.

* spotless

* Change duration

* Switch to slideIntoContainer

* PR Feedback
  • Loading branch information
riggaroo committed Jun 20, 2022
1 parent 203a582 commit 3d0b54f
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts.TakePicture
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.with
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
Expand All @@ -41,6 +48,7 @@ class SurveyFragment : Fragment() {
}
}

@OptIn(ExperimentalAnimationApi::class)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand All @@ -56,20 +64,34 @@ class SurveyFragment : Fragment() {
)
setContent {
JetsurveyTheme {
viewModel.uiState.observeAsState().value?.let { surveyState ->
when (surveyState) {
val state = viewModel.uiState.observeAsState().value ?: return@JetsurveyTheme
AnimatedContent(
targetState = state,
transitionSpec = {
fadeIn() + slideIntoContainer(
towards = AnimatedContentScope
.SlideDirection.Up,
animationSpec = tween(ANIMATION_SLIDE_IN_DURATION)
) with
fadeOut(animationSpec = tween(ANIMATION_FADE_OUT_DURATION))
}
) { targetState ->
// It's important to use targetState and not state, as its critical to ensure
// a successful lookup of all the incoming and outgoing content during
// content transform.
when (targetState) {
is SurveyState.Questions -> SurveyQuestionsScreen(
questions = surveyState,
questions = targetState,
shouldAskPermissions = viewModel.askForPermissions,
onAction = { id, action -> handleSurveyAction(id, action) },
onDoNotAskForPermissions = { viewModel.doNotAskForPermissions() },
onDonePressed = { viewModel.computeResult(surveyState) },
onDonePressed = { viewModel.computeResult(targetState) },
onBackPressed = {
activity?.onBackPressedDispatcher?.onBackPressed()
}
)
is SurveyState.Result -> SurveyResultScreen(
result = surveyState,
result = targetState,
onDonePressed = {
activity?.onBackPressedDispatcher?.onBackPressed()
}
Expand Down Expand Up @@ -110,4 +132,9 @@ class SurveyFragment : Fragment() {
private fun selectContact(questionId: Int) {
// TODO: unsupported for now
}

companion object {
private const val ANIMATION_SLIDE_IN_DURATION = 600
private const val ANIMATION_FADE_OUT_DURATION = 200
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@

package com.example.compose.jetsurvey.survey

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.TweenSpec
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.with
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand Down Expand Up @@ -51,11 +57,15 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.example.compose.jetsurvey.R
import com.example.compose.jetsurvey.theme.progressIndicatorBackground
import com.example.compose.jetsurvey.util.supportWideScreen

private const val CONTENT_ANIMATION_DURATION = 500

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun SurveyQuestionsScreen(
questions: SurveyState.Questions,
Expand All @@ -79,22 +89,49 @@ fun SurveyQuestionsScreen(
)
},
content = { innerPadding ->
Question(
question = questionState.question,
answer = questionState.answer,
shouldAskPermissions = shouldAskPermissions,
onAnswer = {
if (it !is Answer.PermissionsDenied) {
questionState.answer = it
}
questionState.enableNext = true
},
onAction = onAction,
onDoNotAskForPermissions = onDoNotAskForPermissions,
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
)
AnimatedContent(
targetState = questionState,
transitionSpec = {
val animationSpec: TweenSpec<IntOffset> = tween(CONTENT_ANIMATION_DURATION)
val direction =
if (targetState.questionIndex > initialState.questionIndex) {
// Going forwards in the survey: Set the initial offset to start
// at the size of the content so it slides in from right to left, and
// slides out from the left of the screen to -fullWidth
AnimatedContentScope.SlideDirection.Left
} else {
// Going back to the previous question in the set, we do the same
// transition as above, but with different offsets - the inverse of
// above, negative fullWidth to enter, and fullWidth to exit.
AnimatedContentScope.SlideDirection.Right
}
slideIntoContainer(
towards = direction,
animationSpec = animationSpec
) with
slideOutOfContainer(
towards = direction,
animationSpec = animationSpec
)
}
) { targetState ->
Question(
question = targetState.question,
answer = targetState.answer,
shouldAskPermissions = shouldAskPermissions,
onAnswer = {
if (it !is Answer.PermissionsDenied) {
targetState.answer = it
}
targetState.enableNext = true
},
onAction = onAction,
onDoNotAskForPermissions = onDoNotAskForPermissions,
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
)
}
},
bottomBar = {
SurveyBottomBar(
Expand Down

0 comments on commit 3d0b54f

Please sign in to comment.