From Beginner to Hero
Wajahat Karim
wajahatkarim.com
Howdy! This is Wajahat Karim
a modern UI toolkit which simplifies and accelerates UI development on Android with less code, powerful tools, and intuitive Kotlin APIs.
@Composable
fun ExpandableText() {
val shortText = "Click me"
val longText = "Very long text passage that spans
\nacross multiple lines, paragraphs
\nand pages"
var short by remember { mutableStateOf(true) }
Box(
modifier = Modifier
.background(
Color.Blue,
RoundedCornerShape(15.dp)
)
.clickable { short = !short }
.padding(20.dp)
.wrapContentSize()
.animateContentSize()
) {
Text(
if (short) {
shortText
} else {
longText
},
style = TextStyle(color = Color.White)
)
}
}
@Composable
fun PortraitModeImage() {
var portraitMode by remember { mutableStateOf(true) }
Box(
Modifier.clickable { portraitMode = !portraitMode }
.sizeIn(maxWidth = 300.dp, maxHeight = 300.dp)
.background(
if (portraitMode) Color.Yellow else Color.Green)
.animateContentSize(
animSpec = tween(500, easing = LinearEasing),
endListener = { startSize, endSize ->
Log.d("Compose", "$startSize -> $endSize")
}
)
.aspectRatio(if (portraitMode) 3 / 4f else 16 / 9f)
) {
Text(
if (portraitMode) {
"3 : 4"
} else {
"16 : 9"
},
style = TextStyle(color = Color.Black)
)
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun VisibilityAnimationFAB() {
var expanded by remember { mutableStateOf(true) }
FloatingActionButton(
onClick = { expanded = !expanded },
) {
Row(Modifier.padding(start = 16.dp, end = 16.dp)) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = "Favorite Icon",
Modifier.align(Alignment.CenterVertically)
)
AnimatedVisibility(
expanded,
modifier = Modifier.align(Alignment.CenterVertically)
) {
Text(modifier = Modifier.padding(start = 8.dp), text = "Like")
}
}
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun VisibilityAnimationFAB() {
var expanded by remember { mutableStateOf(true) }
FloatingActionButton(
onClick = { expanded = !expanded },
) {
Row(Modifier.padding(start = 16.dp, end = 16.dp)) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = "Favorite Icon",
Modifier.align(Alignment.CenterVertically)
)
AnimatedVisibility(
expanded,
modifier = Modifier.align(Alignment.CenterVertically),
enter = slideInHorizontally(
initialOffsetX = { 300 },
animSpec = tween(durationMillis = 2000)
),
exit = slideOutVertically(
targetOffsetY = { 100 },
animSpec = tween(durationMillis = 2000)
)
) {
Text(text = "Like")
}
}
}
}
Default
Enter
Enter + Exit
cs.android.com
AnimatedVisibility in LazyColumn
AnimatedVisibility in multiple Composables
The fire-and-forget functions to create single value based animations.
@Composable
fun ScaleAndColorAnimation() {
val enabled = remember { mutableStateOf(true) }
val color: Color by animateColorAsState(
if (enabled.value) Color.Blue else Colors.green)
val height: Dp by animateDpAsState(if (enabled.value) 40.dp else 60.dp)
val width: Dp by animateDpAsState(if (enabled.value) 150.dp else 300.dp)
Button(
onClick = { enabled.value = !enabled.value },
colors = ButtonDefaults.buttonColors(backgroundColor = color),
modifier = Modifier
.padding(16.dp)
.preferredHeight(height)
.preferredWidth(width),
) {
Text("Scale & Color")
}
}
@Composable
fun GenderSelectAnimation() {
val female = remember { mutableStateOf(true) }
Row(horizontalArrangement = Arrangement.Center,
modifier = Modifier.padding(8.dp).fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(R.drawable.male),
contentDescription = "Male Image",
contentScale = ContentScale.Crop,
modifier = Modifier
.preferredSize(animateDpAsState(if (female.value) 100.dp else 250.dp).value)
.border(width = animateDpAsState(if (female.value) 0.dp else 4.dp).value,
color = animateColorAsState(if (female.value) Color.Transparent else Color.Red).value)
.padding(8.dp)
.clickable { female.value = !female.value }
)
Image(
painter = painterResource(R.drawable.female),
contentDescription = "Female Image",
contentScale = ContentScale.Crop,
modifier = Modifier
// ...
// Like previous image
)
}
}
github.com/Gurupreet/ComposeCookBook
@Composable
fun HeartBeatDemo() {
val animScale = remember { Animatable(initialValue = 1f) }
val animColor = remember { Animatable(initialValue = Color.Red) }
LaunchedEffect(animScale) {
animScale.animateTo(
targetValue = 2f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 300, delayMillis = 1000),
repeatMode = RepeatMode.Reverse
)
)
}
LaunchedEffect(animColor) {
animColor.animateTo(
targetValue = Color.Blue,
animationSpec = repeatable(iterations = 2,
animation = tween(durationMillis = 300, delayMillis = 1000),
repeatMode = RepeatMode.Reverse
)
)
}
Image(imageVector = Icons.Default.Favorite,
contentDescription = "Favourite Icon",
modifier = Modifier.padding(10.dp).size((40*animScale.value).dp),
colorFilter = ColorFilter.tint(animColor.value)
)
}
A simple Spacer() to allow you to draw anything on it
@Composable
fun Canvas
(
modifier: Modifier,
onDraw: DrawScope.() -> Unit
) = Spacer(modifier.drawBehind(onDraw))
DrawScope - Handles the drawing API
drawRect()
drawOval()
drawLine()
drawImage()
drawRoundRect()
drawCircle()
drawArc()
drawPath()
fun DrawScope.drawMyShape() { }
Custom View Example in Canvas
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(
color = Color.Red,
radius = 300f
)
drawCircle(
color = Color.Green,
radius = 200f
)
drawCircle(
color = Color.Blue,
radius = 100f
)
}
@Composable
fun MovingSquare() {
val animPosX = remember { Animatable(initialValue = 0f) }
LaunchedEffect(animPosX) {
animPosX.animateTo(
targetValue = 500f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 1000)
)
)
}
Canvas(modifier = Modifier.preferredSize(100.dp), onDraw = {
withTransform({
translate(left = animPosX.value)
}) {
drawRect(color = Color.Red)
}
})
}
A simple moving square example
Its all about mathematics & drawing
github.com/wajahatkarim3/DinoCompose
github.com/alexjlockwood/bees-and-bombs-compose/
class DemoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
DemoTheme {
Scaffold(
topBar = {
TopAppBar( /* App Bar */ )
},
floatingActionButton = {
ExplodingFabButton()
},
bodyContent = {
// Content Composables
}
}
}
}
}
enum class FabSizeState {
NORMAL, EXPLODED
}
@Composable
fun ExplodingFabButton() {
var fabSizeState by remember { mutableStateOf(FabSizeState.NORMAL) }
val fabTransition: Transition<FabSizeState> = updateTransition(fabSizeState)
val fabSize: Float by fabTransition.animateFloat() { state ->
when (state) {
FabSizeState.NORMAL -> 80f
FabSizeState.EXPLODED -> 5000f
}
}
val fabColor: Color by fabTransition.animateColor() { state ->
when (state) {
FabSizeState.NORMAL -> secondaryColor
FabSizeState.EXPLODED -> primaryColor
}
}
}
@Composable
fun ExplodingFabButton() {
var fabSizeState by remember { mutableStateOf(FabSizeState.NORMAL) }
val fabTransition: Transition<FabSizeState> = updateTransition(fabSizeState)
val fabSize: Float by fabTransition.animateFloat() { /* from previous slide */ }
val fabColor: Color by fabTransition.animateColor() { /* from previous slide */ }
FloatingActionButton(
onClick = {
fabSizeState = if (fabSizeState == FabSizeState.NORMAL)
FabSizeState.EXPLODED
else FabSizeState.NORMAL
},
modifier = Modifier.size(fabSize.dp),
backgroundColor = fabColor
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Add"
)
}
}
val fabSize: Float by fabTransition.animateFloat(
transitionSpec = {
when {
FabSizeState.NORMAL isTransitioningTo FabSizeState.EXPLODED -> {
keyframes {
durationMillis = 1000
80f at 0
35f at 200
5000f at 1000
}
}
FabSizeState.EXPLODED isTransitioningTo FabSizeState.NORMAL -> {
tween(durationMillis = 1000, easing = FastOutSlowInEasing)
}
else -> snap()
}
}
) { state ->
when (state) {
FabSizeState.NORMAL -> 80f
FabSizeState.EXPLODED -> 5000f
}
}
https://joebirch.co/
https://www.raywenderlich.com/13282144-jetpack-compose-animations-tutorial-getting-started
https://github.com/wajahatkarim3/Flippable
https://joebirch.co/
https://developer.android.com/jetpack/compose/animation
Alex Lockwood
https://github.com/alexjlockwood/bees-and-bombs-compose
https://github.com/alexjlockwood/android-2048-compose
Joe Birch
https://joebirch.co/exploring-jetpack-compose/
Gurupreet Singh
https://github.com/Gurupreet/ComposeCookBook
Leland Richardson
https://www.twitch.tv/intelligibabble
Official by Google
https://developer.android.com/courses/pathways/compose
Vinay Gaba
https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example
Code is available at
http://github.com/wajahatkarim3/ComposeAnimationsDemo
WajahatKarim
wajahatkarim.com/subscribe
wajahatkarim.com