Learn Jetpack Compose by Example
Learn Jetpack Compose by Example
Jetpack Compose?
Why do I need to
learn new things?
}
}
}
fun ComponentActivity.setContent(
recomposer: Recomposer = Recomposer.current(),
content: @Composable () -> Unit
): Composition {
// Some magic ✨🦄
}
class HelloWorldActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
}
}
}
class HelloWorldActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text(text = "Hello World")
}
}
}
class HelloWorldActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text(text = "Hello World")
}
}
}
class HelloWorldActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text(text = "Hello World")
}
}
}
@Composable
fun CustomTextComponent() {
Text(text = "Hello World")
}
@Composable
fun CustomTextComponent(displayText: String) {
Text(
text = displayText,
style = TextStyle(
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
)
)
}
@Composable
fun CustomTextComponent(displayText: String) {
Text(
text = displayText,
style = TextStyle(
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
)
)
}
@Composable
fun CustomTextComponent(displayText: String) {
Text(
text = displayText,
style = TextStyle(
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
)
)
}
@Preview
@Composable
fun CustomTextComponentPreview() {
CustomTextComponent("Hello World")
}
Example: Show an Image
@Composable
fun DrawableImage() {
}
@Composable
fun DrawableImage() {
val image = loadImageResource(R.drawable.lena)
}
@Composable
fun DrawableImage() {
val image = loadImageResource(R.drawable.lena)
image.resource.resource?.let {
Image(asset = it, modifier = Modifier.preferredSize(200.dp))
}
}
@Composable
fun DrawableImage(@DrawableRes resId: Int) {
val image = loadImageResource(resId)
image.resource.resource?.let {
Image(asset = it, modifier = Modifier.preferredSize(200.dp))
}
}
@Composable
fun DrawableImage(@DrawableRes resId: Int) {
val image = loadImageResource(resId)
image.resource.resource?.let {
Image(asset = it, modifier = Modifier.preferredSize(200.dp))
}
}
Example: Alert Dialog
// Classic Android
AlertDialog.Builder(context)
.setTitle("360|AnDev")
.setMessage("Isn't this conference amazing?")
.setPositiveButton(android.R.string.yes, null)
.setNegativeButton(android.R.string.no, null)
.setIcon(android.R.drawable.ic_dialog_alert)
.show()
@Composable
fun AlertDialogComponent() {
}
@Composable
fun AlertDialogComponent() {
var showPopup by state { false }
}
@Composable
fun AlertDialogComponent() {
var showPopup by state { false }
if (showPopup) {
AlertDialog(
onCloseRequest = { showPopup = false },
text = {
Text("Congratulations! You just clicked the text successfully")
},
confirmButton = {
Button(
onClick = onPopupDismissed
){
Text(text = "Ok")
}
}
)
}
}
@Composable
fun AlertDialogComponent() {
var showPopup by state { false }
if (showPopup) {
AlertDialog(
onCloseRequest = { showPopup = false },
text = {
Text("Congratulations! You just clicked the text successfully")
},
confirmButton = {
Button(
onClick = onPopupDismissed
){
Text(text = "Ok")
}
}
)
}
}
@Composable
fun AlertDialogComponent() {
var showPopup by remember { mutableStateOf(false) }
val onButtonClicked = { showPopup = true }
val onPopupDismissed = { showPopup = false }
if (!showPopup) {
Button(onClick = onButtonClicked, backgroundColor = Color.DarkGray) {
Text(text = "Click Me")
}
} else {
AlertDialog(
onCloseRequest = onPopupDismissed,
text = {
Text("Congratulations! You just clicked the text successfully")
},
confirmButton = {
Button(
onClick = onPopupDismissed
){
Text(text = "Ok")
}
}
)
}
}
Example: Simple Layouts
Row
Row
1 2
Row
1
2
Column
@Composable
fun ImageWithTitleSubtitleComponent() {
}
@Composable
fun ImageWithTitleSubtitleComponent() {
Row() {
Column() {
}
}
}
@Composable
fun ImageWithTitleSubtitleComponent() {
Row(modifier = Modifier.fillMaxWidth() + Modifier.padding(16.dp)) {
Column(modifier = Modifier.padding(start = 16.dp)) {
}
}
}
@Composable
fun ImageWithTitleSubtitleComponent() {
Row(modifier = Modifier.fillMaxWidth() + Modifier.padding(16.dp)) {
DrawableImage(R.drawable.lenna)
Column(modifier = Modifier.padding(start = 16.dp)) {
CustomTextComponent(displayText = "Title")
CustomTextComponent(displayText = "Subtitle")
}
}
}
@Composable
fun ImageWithTitleSubtitleComponent(title: String, subtitle: String, imageUrl: String) {
Row(modifier = Modifier.fillMaxWidth() + Modifier.padding(16.dp)) {
NetworkImage(imageUrl)
Column(modifier = Modifier.padding(start = 16.dp)) {
CustomTextComponent(displayText = title)
CustomTextComponent(displayText = subtitle)
}
}
}
@Composable
fun ImageWithTitleSubtitleComponent(title: String, subtitle: String, imageUrl: String) {
Row(modifier = Modifier.fillMaxWidth() + Modifier.padding(16.dp)) {
NetworkImage(imageUrl)
Column(modifier = Modifier.padding(start = 16.dp)) {
CustomTextComponent(displayText = title)
CustomTextComponent(displayText = subtitle)
}
}
}
Example: ConstraintLayout
@Composable
fun SimpleRowComponent(titleText: String, subtitleText: String, imageUrl: String) {
}
@Composable
fun SimpleRowComponent(titleText: String, subtitleText: String, imageUrl: String) {
ConstraintLayout {
val (title, subtitle, image) = createRefs()
}
}
@Composable
fun SimpleRowComponent(titleText: String, subtitleText: String, imageUrl: String) {
ConstraintLayout {
val (title, subtitle, image) = createRefs()
CustomTextComponent(
displayText = titleText,
)
CustomTextComponent(
displayText = subtitleText,
)
NetworkImage(
url = imageUrl,
)
}
}
@Composable
fun SimpleRowComponent(titleText: String, subtitleText: String, imageUrl: String) {
ConstraintLayout {
val (title, subtitle, image) = createRefs()
CustomTextComponent(
displayText = titleText,
modifier = Modifier.constrainAs(title) {
start.linkTo(image.end, margin = 8.dp)
top.linkTo(image.top)
}
)
CustomTextComponent(
displayText = subtitleText,
)
NetworkImage(
url = imageUrl,
)
}
}
@Composable
fun SimpleRowComponent(titleText: String, subtitleText: String, imageUrl: String) {
ConstraintLayout {
val (title, subtitle, image) = createRefs()
CustomTextComponent(
displayText = titleText,
modifier = Modifier.constrainAs(title) {
start.linkTo(image.end, margin = 8.dp)
top.linkTo(image.top)
}
)
CustomTextComponent(
displayText = subtitleText,
modifier = Modifier.constrainAs(subtitle) {
bottom.linkTo(image.bottom)
start.linkTo(image.end, margin = 8.dp)
}
)
NetworkImage(
url = imageUrl,
modifier = Modifier.constrainAs(image) {
centerVerticallyTo(parent)
start.linkTo(parent.start, margin = 16.dp)
top.linkTo(parent.top, margin = 16.dp)
bottom.linkTo(parent.bottom, margin = 16.dp)
}
)
}
}
@Composable
fun SimpleRowComponent(titleText: String, subtitleText: String, imageUrl: String) {
Card(
modifier = Modifier.fillMaxWidth() + Modifier.padding(8.dp),
shape = RoundedCornerShape(4.dp)
){
ConstraintLayout {
val (title, subtitle, image) = createRefs()
CustomTextComponent(
displayText = titleText,
modifier = Modifier.constrainAs(title) {
start.linkTo(image.end, margin = 8.dp)
top.linkTo(image.top)
}
)
CustomTextComponent(
displayText = subtitleText,
modifier = Modifier.constrainAs(subtitle) {
bottom.linkTo(image.bottom)
start.linkTo(image.end, margin = 8.dp)
}
)
NetworkImage(
url = imageUrl,
modifier = Modifier.constrainAs(image) {
centerVerticallyTo(parent)
start.linkTo(parent.start, margin = 16.dp)
top.linkTo(parent.top, margin = 16.dp)
bottom.linkTo(parent.bottom, margin = 16.dp)
})
}
}
}
@Composable
fun SimpleRowComponent(titleText: String, subtitleText: String, imageUrl: String) {
Card(
modifier = Modifier.fillMaxWidth() + Modifier.padding(8.dp),
shape = RoundedCornerShape(4.dp)
){
ConstraintLayout {
val (title, subtitle, image) = createRefs()
CustomTextComponent(
displayText = titleText,
modifier = Modifier.constrainAs(title) {
start.linkTo(image.end, margin = 8.dp)
top.linkTo(image.top)
}
)
CustomTextComponent(
displayText = subtitleText,
modifier = Modifier.constrainAs(subtitle) {
bottom.linkTo(image.bottom)
start.linkTo(image.end, margin = 8.dp)
}
)
NetworkImage(
url = imageUrl,
modifier = Modifier.constrainAs(image) {
centerVerticallyTo(parent)
start.linkTo(parent.start, margin = 16.dp)
top.linkTo(parent.top, margin = 16.dp)
bottom.linkTo(parent.bottom, margin = 16.dp)
})
}
}
}
Example: List
www.JetpackCompose.app
@Composable
fun ListComponent(superheroList: List<Person>) {
}
@Composable
fun ListComponent(superheroList: List<Person>) {
LazyColumnItems(items = superheroList) { person ->
}
}
@Composable
fun ListComponent(superheroList: List<Person>) {
LazyColumnItems(items = superheroList) { person ->
SimpleRowComponent(
person.name,
person.age,
person.profilePictureUrl
)
}
}
@Composable
fun ListComponent(superheroList: List<Person>) {
LazyColumnItems(items = superheroList) { person ->
SimpleRowComponent(
person.name,
person.age,
person.profilePictureUrl
)
}
}
Example: Click
@Composable
fun SimpleRowComponent(
titleText: String,
subtitleText: String,
imageUrl: String
){
Card(
modifier = Modifier.fillMaxWidth() +
Modifier.padding(8.dp),
shape = RoundedCornerShape(4.dp)
){
....
....
}
}
@Composable
fun SimpleRowComponent(
titleText: String,
subtitleText: String,
imageUrl: String,
viewModel: SuperheroViewModel
){
Card(
modifier = Modifier.fillMaxWidth() +
Modifier.padding(8.dp) +
Modifier.cl
shape = RoundedCornerShape(4.dp)
){
....
....
}
}
@Composable
fun SimpleRowComponent(
titleText: String,
subtitleText: String,
imageUrl: String,
viewModel: SuperheroViewModel
){
Card(
modifier = Modifier.fillMaxWidth() +
Modifier.padding(8.dp) +
Modifier.cl
clip
shape = RoundedCornerShape(4.dp)
){
....
clickable
.... clipToBounds
}
}
@Composable
fun SimpleRowComponent(
titleText: String,
subtitleText: String,
imageUrl: String,
viewModel: SuperheroViewModel
){
Card(
modifier = Modifier.fillMaxWidth() +
Modifier.padding(8.dp) +
Modifier.clickable {
viewModel.updateSelectedSuperhero()
},
shape = RoundedCornerShape(4.dp)
){
....
....
}
}
@Composable
fun SimpleRowComponent(
titleText: String,
subtitleText: String,
imageUrl: String,
viewModel: SuperheroViewModel
){
👎
Card(
modifier = Modifier.fillMaxWidth() +
Modifier.padding(8.dp) +
Modifier.clickable {
viewModel.updateSelectedSuperhero()
},
shape = RoundedCornerShape(4.dp)
){
....
....
}
}
@Composable
fun SimpleRowComponent(
titleText: String,
subtitleText: String,
imageUrl: String,
onClick: () -> Unit
){
Card(
modifier = Modifier.fillMaxWidth() +
Modifier.padding(8.dp) +
Modifier.clickable {
onClick()
},
shape = RoundedCornerShape(4.dp)
){
....
....
}
}
@Composable
fun SimpleRowComponent(
titleText: String,
subtitleText: String,
imageUrl: String,
onClick: () -> Unit
){
Card(
modifier = Modifier.fillMaxWidth() +
Modifier.padding(8.dp) +
Modifier.clickable {
onClick()
},
shape = RoundedCornerShape(4.dp)
){
....
....
}
}
Example: Pinch-to-Zoom &
Drag
@Composable
fun ZoomableImageComponent(imageUrl: String) {
}
@Composable
fun ZoomableImageComponent(imageUrl: String) {
var scale by state { 1f }
var panOffset by state { Offset(0f, 0f) }
}
@Composable
fun ZoomableImageComponent(imageUrl: String) {
var scale by state { 1f }
var panOffset by state { Offset(0f, 0f) }
Box(gravity = Alignment.Center) {
NetworkImage(
imageUrl = imageUrl,
modifier = Modifier.fillMaxSize()
)
}
}
@Composable
fun ZoomableImageComponent(imageUrl: String) {
var scale by state { 1f }
var panOffset by state { Offset(0f, 0f) }
Box(
gravity = Alignment.Center,
modifier = Modifier.zoomable(onZoomDelta = { scale *= it })
){
NetworkImage(
imageUrl = imageUrl,
modifier = Modifier.fillMaxSize() + Modifier.drawLayer(
scaleX = scale,
scaleY = scale
)
)
}
}
@Composable
fun ZoomableImageComponent(imageUrl: String) {
var scale by state { 1f }
var panOffset by state { Offset(0f, 0f) }
Box(
gravity = Alignment.Center,
modifier = Modifier.zoomable(onZoomDelta = { scale *= it }) + Modifier.rawDragGestureFilter(
object : DragObserver {
override fun onDrag(dragDistance: Offset): Offset {
panOffset = panOffset.plus(dragDistance)
return super.onDrag(dragDistance)
}
})
){
NetworkImage(
imageUrl = imageUrl,
modifier = Modifier.fillMaxSize() + Modifier.drawLayer(
scaleX = scale,
scaleY = scale,
translationX = panOffset.x,
translationY = panOffset.y
)
)
}
}
@Composable
fun ZoomableImageComponent(imageUrl: String) {
var scale by state { 1f }
var panOffset by state { Offset(0f, 0f) }
Box(
gravity = Alignment.Center,
modifier = Modifier.zoomable(onZoomDelta = { scale *= it }) + Modifier.rawDragGestureFilter(
object : DragObserver {
override fun onDrag(dragDistance: Offset): Offset {
panOffset = panOffset.plus(dragDistance)
return super.onDrag(dragDistance)
}
})
){
NetworkImage(
imageUrl = imageUrl,
modifier = Modifier.fillMaxSize() + Modifier.drawLayer(
scaleX = scale,
scaleY = scale,
translationX = panOffset.x,
translationY = panOffset.y
)
)
}
}
Interoperability
Example: Compose in
Classic Android
class ComposeInClassicAndroidActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_compose_in_classic_android)
}
}
class ComposeInClassicAndroidActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_compose_in_classic_android)
val containerLayout = findViewById(R.id.frame_container)
containerLayout.setContent(Recomposer.current()) {
SimpleRowComponent()
}
}
}
class ComposeInClassicAndroidActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_compose_in_classic_android)
val containerLayout = findViewById(R.id.frame_container)
containerLayout.setContent(Recomposer.current()) {
SimpleRowComponent()
}
}
}
Example: Classic Android in
Compose
credit_card.xml