I am trying to display splash screen where elements would have enter animation and when data would be loaded for the first time then minimize it to top app bar and display data.
Edit:
I managed to put both animation to one screen, but I am not sure if this is okay to use AnimatedVisibility
inside MotionLayout
.
My SplashAppBar
looks this way:
fun SplashAppBar(
collapsed: Boolean,
hideEnterAnimation: Boolean = true,
) {
val context = LocalContext.current
val motionScene = remember {
context.resources.openRawResource(R.raw.splash_app_bar_motion_scene).readBytes()
.decodeToString()
}
val progress by animateFloatAsState(
targetValue = if (collapsed) 1f else 0f,
tween(2000)
)
val motionHeight by animateDpAsState(
targetValue = if (collapsed) 56.dp else LocalConfiguration.current.screenHeightDp.dp,
tween(2000)
)
val fontSize by animateIntAsState(
targetValue = if (collapsed) 20 else 96,
tween(2000)
)
var visible by remember { mutableStateOf(hideEnterAnimation) }
val density = LocalDensity.current
LaunchedEffect(key1 = true) {
visible = true
}
MotionLayout(
motionScene = MotionScene(content = motionScene),
progress = progress,
modifier = Modifier
.fillMaxWidth()
.height(motionHeight)
.background(
Brush.verticalGradient(
listOf(
Color.Black,
BlueGray500
)
),
)
) {
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
animationSpec = tween(
durationMillis = 1000,
easing = EaseOutCirc
)
) {
with(density) { 200.dp.roundToPx() }
} + fadeIn(initialAlpha = 0.3f),
modifier = Modifier.layoutId("title"),
) {
Text(
text = "Foodie",
style = if (progress > 0.5f) MaterialTheme.typography.h6 else MaterialTheme.typography.h1,
fontSize = fontSize.sp,
color = Color.White,
)
}
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
animationSpec = tween(
durationMillis = 2000,
easing = EaseOutBounce
)
) {
with(density) { -500.dp.roundToPx() }
} + fadeIn(initialAlpha = 0.3f),
modifier = Modifier.layoutId("logo"),
) {
Image(
painter = painterResource(R.drawable.foodie_logo),
contentDescription = "Foodie logo",
)
}
}
}
With splash_app_bar_motion_scene
:
{
ConstraintSets: {
start: {
logo:{
width: 200,
height: 200,
start: ['parent', 'start'],
end: ['parent', 'end'],
top: ['parent', 'top'],
bottom: ['parent', 'bottom']
},
title: {
start: ['logo', 'start'],
end: ['logo', 'end'],
top: ['logo', 'bottom']
}
},
end: {
logo:{
width: 24,
height: 24,
start: ['parent', 'start', 16],
top: ['parent', 'top', 12],
bottom: ['parent', 'bottom', 12],
},
title: {
start: ['logo', 'end', 16],
top: ['logo', 'top'],
bottom: ['logo', 'bottom']
}
}
},
Transitions: {
default: {
from: 'start',
to: 'end',
pathMotionArc: 'startVertical',
KeyFrames: {
KeyAttributes: [
{
target: ['logo'],
frames: [0, 100],
rotationZ: [0, 360]
},
{
target: ['title'],
frames: [0, 20, 50, 80, 100],
translationY: [0,-100, -45, -10, 0],
translationX: [0, 0, -80, -85, 0],
}
]
}
}
}
}
The way I present it is:
@Composable
fun MainView(
uiState: UiState<Data>,
) {
Column {
SplashAppBar(
collapsed = uiState.initialized,
hideEnterAnimation = false,
)
when {
!uiState.initialized -> Unit
uiState.errorMessage.isNotEmpty() -> ErrorRetryView()
uiState.successData != null -> SuccessView(uiState.successData)
uiState.loading -> LoadingView()
else -> RetryView()
}
}
}