SlideShare a Scribd company logo
Jetpack
Compose
uma nova forma de implementar UI no Android
Nelson Glauber
@nglauber
Jetpack
Compose
uma nova forma de implementar UI no Android
Nelson Glauber
@nglauber
Jetpack Compose é um moderno conjunto
de ferramentas para construir interfaces
gráficas para aplicações Android de forma
declarativa utilizando Kotlin.
app.sli.do/event/djw2yt2e
app.sli.do/event/djw2yt2e
UI Imperativa x UI Declarativa
• UI Imperativa é onde uma entidade de UI (com uma classe View, por
exemplo) é totalmente construída e posteriormente é modificada
usando métodos e/ou propriedades públicas.
• Na UI Declarativa, o desenvolvedor descreve o estado da UI e deixa
o transicionamento desse estado por conta de um framework.
app.sli.do/event/djw2yt2e
Por que?
• Todas as APIs do Android evoluíram, menos o UI toolkit.

Room, View Model, Paging, Work Manager, Camera, Navigation, …
• Não é simples criar uma custom view… Componente.kt,
componente.xml, attrs.xml, styles.xml…
app.sli.do/event/djw2yt2e
Jetpack Compose
• Segue uma abordagem declarativa similar ao SwiftUI, ReactNative, e
Flutter.
• É uma biblioteca separada do S.O.
• É uma nova forma se se pensar na elaboração da UI: componentes ao
invés de telas; Composição no lugar de Herança.
• Compatível com aplicações Android existentes, podendo ser adotado
progressivamente.
• EXPERIMENTAL! Ainda está em versão alpha desde… ontem 😅
app.sli.do/event/djw2yt2e
Benefícios
• No Compose, a UI é totalmente escrita em Kotlin e nós sabemos
organizar e refatorar código melhor que arquivos XML :)
• Qual o maior trabalho no front-end? 

Deixar UI e dados sincronizados e consistentes (Data Binding, Live
Data, …).
• O UI toolkit atual não está nem aí pros teus problemas! 

Ele tem o próprio estado e só avisa que o seu estado interno mudou.
Android Studio 4.2
(Preview)
Compose Preview
Interactive Mode
Launch
Composable
Inline
Documentation
Embedded
Emulator
@Preview
app.sli.do/event/djw2yt2e
@Composable
• É maneira de criar um “custom component”. 

Simplesmente criando uma função!
• @Composable não usa processamento de anotação. É uma
instrução para o plugin do compilador do Kotlin.
• Só pode ser chamada dentro de um contexto de uma @Composable
function.
app.sli.do/event/djw2yt2e
setContent
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Greeting("Android")
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
app.sli.do/event/djw2yt2e
setContent
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Greeting("Android")
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
app.sli.do/event/djw2yt2e
setContent
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Greeting("Android")
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
app.sli.do/event/djw2yt2e
setContent
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Greeting("Android")
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
app.sli.do/event/djw2yt2e
setContent
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Greeting("Android")
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(stringResource(R.string.hello, name))
}
app.sli.do/event/djw2yt2e
Material Theme
• Define o tema da aplicação por meio de cores, estilos, fontes e
decoração de componentes.
• Normalmente é o elemento raiz da tela.
setContent {
AppTheme {
Greeting("Android")
}
}
app.sli.do/event/djw2yt2e
Material Theme
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = typography,
shapes = shapes,
content = content
)
}
private val DarkColorPalette =
darkColors(
primary = purple200,
primaryVariant = purple700,
secondary = teal200
)
private val LightColorPalette =
lightColors(
primary = purple500,
primaryVariant = purple700,
secondary = teal200
)
app.sli.do/event/djw2yt2e
TextStyle
Text(
text = "Hello!",
style = MaterialTheme.typography.h4
)
app.sli.do/event/djw2yt2e
Modifiers
• Decoram um elemento
• Provê parâmetros de layout
• Comportamentos adicionais
• São encadeados e a ordem importa!
val shape = CutCornerShape(topLeft = 16.dp, bottomRight = 16.dp)
Text(
text = "Text 1",
style = TextStyle(
color = Color.White,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center),
modifier = Modifier.fillMaxWidth()
.padding(16.dp)
.border(2.dp, MaterialTheme.colors.secondary, shape)
.padding(1.dp)
.background(MaterialTheme.colors.primary, shape)
.clickable(onClick = {
// Click event
})
.padding(16.dp)
)
val shape = CutCornerShape(topLeft = 16.dp, bottomRight = 16.dp)
Text(
text = "Text 1",
style = TextStyle(
color = Color.White,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center),
modifier = Modifier.fillMaxWidth()
.padding(16.dp)
.border(2.dp, MaterialTheme.colors.secondary, shape)
.padding(1.dp)
.background(MaterialTheme.colors.primary, shape)
.clickable(onClick = {
// Click event
})
.padding(16.dp)
)
val shape = CutCornerShape(topLeft = 16.dp, bottomRight = 16.dp)
Text(
text = "Text 1",
style = TextStyle(
color = Color.White,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center),
modifier = Modifier.fillMaxWidth()
.padding(16.dp)
.border(2.dp, MaterialTheme.colors.secondary, shape)
.padding(1.dp)
.background(MaterialTheme.colors.primary, shape)
.clickable(onClick = {
// Click event
})
.padding(16.dp)
)
val shape = CutCornerShape(topLeft = 16.dp, bottomRight = 16.dp)
Text(
text = "Text 1",
style = TextStyle(
color = Color.White,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center),
modifier = Modifier.fillMaxWidth()
.padding(16.dp)
.border(2.dp, MaterialTheme.colors.secondary, shape)
.padding(1.dp)
.background(MaterialTheme.colors.primary, shape)
.clickable(onClick = {
// Click event
})
.padding(16.dp)
)
val shape = RoundedCornerShape(8.dp)
Text(
text = "Text 1",
style = TextStyle(
color = Color.White,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center),
modifier = Modifier.fillMaxWidth()
.padding(16.dp)
.border(2.dp, MaterialTheme.colors.secondary, shape)
.padding(1.dp)
.background(MaterialTheme.colors.primary, shape)
.clickable(onClick = {
// Click event
})
.padding(16.dp)
)
val shape = CircleShape
Text(
text = "Text 1",
style = TextStyle(
color = Color.White,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center),
modifier = Modifier.fillMaxWidth()
.padding(16.dp)
.border(2.dp, MaterialTheme.colors.secondary, shape)
.padding(1.dp)
.background(MaterialTheme.colors.primary, shape)
.clickable(onClick = {
// Click event
})
.padding(16.dp)
)
app.sli.do/event/djw2yt2e
Layouts
• Column
• Row
• Stack
• ConstraintLayout
Stack(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
) {
Text("Column Text 1")
Text("Column Text 2")
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Text(text = "Row Text 1")
Text(text = "Row Text 2")
}
}
Text(
"Stack Text",
modifier = Modifier
.gravity(Alignment.TopEnd)
.padding(end = 16.dp, top = 16.dp)
)
}
Stack(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
) {
Text("Column Text 1")
Text("Column Text 2")
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Text(text = "Row Text 1")
Text(text = "Row Text 2")
}
}
Text(
"Stack Text",
modifier = Modifier
.gravity(Alignment.TopEnd)
.padding(end = 16.dp, top = 16.dp)
)
}
Stack(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
) {
Text("Column Text 1")
Text("Column Text 2")
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Text(text = "Row Text 1")
Text(text = "Row Text 2")
}
}
Text(
"Stack Text",
modifier = Modifier
.gravity(Alignment.TopEnd)
.padding(end = 16.dp, top = 16.dp)
)
}
Stack(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
) {
Text("Column Text 1")
Text("Column Text 2")
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Text(text = "Row Text 1")
Text(text = "Row Text 2")
}
}
Text(
"Stack Text",
modifier = Modifier
.gravity(Alignment.TopEnd)
.padding(end = 16.dp, top = 16.dp)
)
}
app.sli.do/event/djw2yt2e
ConstraintLayout
ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) {
val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs()
Text("Nome", modifier = Modifier.constrainAs(text1Ref) {
top.linkTo(parent.top)
centerHorizontallyTo(parent)
})
TextField(modifier = Modifier.padding(top = 8.dp)
.constrainAs(edit1Ref) {
start.linkTo(parent.start)
end.linkTo(parent.end)
top.linkTo(text1Ref.bottom)
})
Button(onClick = {}, modifier = Modifier.padding(top = 8.dp)
.constrainAs(btn1Ref) {
end.linkTo(edit1Ref.end)
top.linkTo(edit1Ref.bottom)
}
)
TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp)
.constrainAs(btn2Ref) {
end.linkTo(btn1Ref.start)
baseline.linkTo(btn1Ref.baseline)
}
)
}
ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) {
val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs()
Text("Nome", modifier = Modifier.constrainAs(text1Ref) {
top.linkTo(parent.top)
centerHorizontallyTo(parent)
})
TextField(modifier = Modifier.padding(top = 8.dp)
.constrainAs(edit1Ref) {
start.linkTo(parent.start)
end.linkTo(parent.end)
top.linkTo(text1Ref.bottom)
})
Button(onClick = {}, modifier = Modifier.padding(top = 8.dp)
.constrainAs(btn1Ref) {
end.linkTo(edit1Ref.end)
top.linkTo(edit1Ref.bottom)
}
)
TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp)
.constrainAs(btn2Ref) {
end.linkTo(btn1Ref.start)
baseline.linkTo(btn1Ref.baseline)
}
)
}
ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) {
val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs()
Text("Nome", modifier = Modifier.constrainAs(text1Ref) {
top.linkTo(parent.top)
centerHorizontallyTo(parent)
})
TextField(modifier = Modifier.padding(top = 8.dp)
.constrainAs(edit1Ref) {
start.linkTo(parent.start)
end.linkTo(parent.end)
top.linkTo(text1Ref.bottom)
})
Button(onClick = {}, modifier = Modifier.padding(top = 8.dp)
.constrainAs(btn1Ref) {
end.linkTo(edit1Ref.end)
top.linkTo(edit1Ref.bottom)
}
)
TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp)
.constrainAs(btn2Ref) {
end.linkTo(btn1Ref.start)
baseline.linkTo(btn1Ref.baseline)
}
)
}
ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) {
val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs()
Text("Nome", modifier = Modifier.constrainAs(text1Ref) {
top.linkTo(parent.top)
centerHorizontallyTo(parent)
})
TextField(modifier = Modifier.padding(top = 8.dp)
.constrainAs(edit1Ref) {
start.linkTo(parent.start)
end.linkTo(parent.end)
top.linkTo(text1Ref.bottom)
})
Button(onClick = {}, modifier = Modifier.padding(top = 8.dp)
.constrainAs(btn1Ref) {
end.linkTo(edit1Ref.end)
top.linkTo(edit1Ref.bottom)
}
)
TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp)
.constrainAs(btn2Ref) {
end.linkTo(btn1Ref.start)
baseline.linkTo(btn1Ref.baseline)
}
)
}
ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) {
val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs()
Text("Nome", modifier = Modifier.constrainAs(text1Ref) {
top.linkTo(parent.top)
centerHorizontallyTo(parent)
})
TextField(modifier = Modifier.padding(top = 8.dp)
.constrainAs(edit1Ref) {
start.linkTo(parent.start)
end.linkTo(parent.end)
top.linkTo(text1Ref.bottom)
})
Button(onClick = {}, modifier = Modifier.padding(top = 8.dp)
.constrainAs(btn1Ref) {
end.linkTo(edit1Ref.end)
top.linkTo(edit1Ref.bottom)
}
)
TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp)
.constrainAs(btn2Ref) {
end.linkTo(btn1Ref.start)
baseline.linkTo(btn1Ref.baseline)
}
)
}
ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) {
val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs()
Text("Nome", modifier = Modifier.constrainAs(text1Ref) {
top.linkTo(parent.top)
centerHorizontallyTo(parent)
})
TextField(modifier = Modifier.padding(top = 8.dp)
.constrainAs(edit1Ref) {
start.linkTo(parent.start)
end.linkTo(parent.end)
top.linkTo(text1Ref.bottom)
})
Button(onClick = {}, modifier = Modifier.padding(top = 8.dp)
.constrainAs(btn1Ref) {
end.linkTo(edit1Ref.end)
top.linkTo(edit1Ref.bottom)
}
)
TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp)
.constrainAs(btn2Ref) {
end.linkTo(btn1Ref.start)
baseline.linkTo(btn1Ref.baseline)
}
)
}
app.sli.do/event/djw2yt2e
Button
Button(
content = { Text("Button") },
onClick = {}
)
OutlinedButton(
content = { Text("OutlinedButton") },
onClick = {}
)
TextButton(
content = { Text("TextButton") },
onClick = {}
)
app.sli.do/event/djw2yt2e
Card + Ripple + Clickable
@Composable
fun UserItem(user: User) {
Card(
elevation = 4.dp,
shape = RoundedCornerShape(4.dp),
modifier = Modifier.fillMaxWidth().padding(8.dp)
.clickable(
onClick = {
// Click event
}
)
) {
// Card Content
}
}
app.sli.do/event/djw2yt2e
Image
Image(
asset = imageResource(R.drawable.recife),
contentScale = ContentScale.FillHeight
)
Image(
asset = vectorResource(id = R.drawable.ic_android),
contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(Color.Cyan)
)
CoilImage(
modifier = Modifier
.size(96.dp)
.clip(CircleShape),
data = photoUrl
)
Scaffold
Scaffold(
topBar = {...},
drawerContent = {...},
bodyContent = {...},
floatingActionButton = {...},
bottomBar = {...}
)
TopAppBar(
backgroundColor = MaterialTheme.colors.primary,
contentColor = Color.Yellow,
title = { Text(text = "Compose") },
actions = {
IconButton(
onClick = {},
icon = { Icon(Icons.Default.Search) }
)
DropdownMenu(…)
},
navigationIcon = {
IconButton(
icon = { Icon(Icons.Default.Menu) },
onClick = {
scaffoldState.drawerState.open()
}
)
}
)
FloatingActionButton(
onClick = {},
icon = { Icon(Icons.Filled.Add) },
backgroundColor = Color.Red,
contentColor = Color.White,
shape = CutCornerShape(
topLeft = 20.dp, bottomRight = 20.dp
)
)
BottomAppBar(
backgroundColor = MaterialTheme.colors.primary,
content = {
BottomNavigationItem(
icon = { Icon(Icons.Filled.Home) },
selected = selectedTab == 0,
onSelect = { selectedTab = 0 },
selectedContentColor = Color.White,
unselectedContentColor = Color.DarkGray,
label = { Text(text = "Home") }
)
BottomNavigationItem(…)
}
)
BottomAppBar(
backgroundColor = MaterialTheme.colors.primary,
content = {
BottomNavigationItem(
icon = { Icon(Icons.Filled.Home) },
selected = selectedTab == 0,
onSelect = { selectedTab = 0 },
selectedContentColor = Color.White,
unselectedContentColor = Color.DarkGray,
label = { Text(text = "Home") }
)
BottomNavigationItem(…)
}
)
app.sli.do/event/djw2yt2e
State
• Single Source of Truth
• O Componente é atualizado quando o seu estado muda
var nameState by remember { mutableStateOf("") }
TextField(
value = nameState,
label = { Text("Digite seu nome") },
onValueChange = { s: String ->
nameState = s
}
)
app.sli.do/event/djw2yt2e
Switch & Checkbox
var isChecked by remember { mutableStateOf(true) }
Checkbox(
checked = isChecked,
onCheckedChange = { isChecked = it }
)
var isOn by remember { mutableStateOf(true) }
Switch(
checked = isOn,
onCheckedChange = { isOn = it }
)
app.sli.do/event/djw2yt2e
RadioButton
val options = listOf("Opção 1", "Opção 2", "Opção 3")
var currentOption by remember { mutableStateOf("Opção 1") }
options.forEach { text ->
Row(
Modifier.fillMaxWidth()
) {
RadioButton(
selected = (text == currentOption),
onClick = { currentOption = text }
)
Text(text = text)
}
}
app.sli.do/event/djw2yt2e
State
data class Score(
var team: String,
var score: Int
)
app.sli.do/event/djw2yt2e
State
class Score(
team: String,
score: Int
) {
var team by mutableStateOf(team)
var score by mutableStateOf(score)
}
app.sli.do/event/djw2yt2e
class Score(
team: String,
score: Int
) {
var team by mutableStateOf(team)
var score by mutableStateOf(score)
}
@Composable
fun TeamScore(score: Score) {
Column(horizontalGravity = Alignment.CenterHorizontally) {
Text(text = score.team, style = MaterialTheme.typography.h6)
Button(
content = { Text("+") },
onClick = { score.score += 1 }
)
Text(text = score.score.toString(), style = MaterialTheme.typography.h5)
Button(
content = { Text("-") },
onClick = { score.score = max(score.score - 1, 0) }
)
}
}
@Composable
fun ScoreScreen(homeScore: Score, visitorScore: Score) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalGravity = Alignment.CenterHorizontally
) {
Row {
TeamScore(score = homeScore)
Text(text = "x",
modifier = Modifier.padding(horizontal = 8.dp),
style = MaterialTheme.typography.h6)
TeamScore(score = visitorScore)
}
OutlinedButton(
modifier = Modifier.padding(top = 16.dp),
content = { Text("Reset") },
onClick = {
homeScore.score = 0
visitorScore.score = 0
}
)
}
}
https://link.medium.com/Uax00dnd31
app.sli.do/event/djw2yt2e
Testes
dependencies {
...
testImplementation 'junit:junit:4.13'
androidTestImplementation "androidx.ui:ui-test:$compose_version"
}
<activity
android:name="androidx.activity.ComponentActivity" />
@Composable
fun TeamScore(score: Score) {
Column(horizontalGravity = Alignment.CenterHorizontally) {
Text(text = score.team, style = MaterialTheme.typography.h6)
Button(
content = { Text("+") },
onClick = { score.score += 1 }
)
Text(text = score.score.toString(), style = MaterialTheme.typography.h5)
Button(
content = { Text("-") },
onClick = { score.score = max(score.score - 1, 0) }
)
}
}
@Composable
fun TeamScore(score: Score) {
Column(horizontalGravity = Alignment.CenterHorizontally) {
Text(text = score.team, style = MaterialTheme.typography.h6)
Button(
content = { Text("+") },
modifier = Modifier.testTag("TeamScore_Inc"),
onClick = { score.score += 1 }
)
Text(text = score.score.toString(), style = MaterialTheme.typography.h5)
Button(
content = { Text("-") },
modifier = Modifier.testTag("TeamScore_Dec"),
onClick = { score.score = max(score.score - 1, 0) }
)
}
}
@RunWith(JUnit4::class)
class TeamScoreTest {
@get:Rule
val composeTestRule = createComposeRule()
fun setContent(score: Score) {
composeTestRule.setContent {
MaterialTheme {
TeamScore(score)
}
}
}
@Test
fun teamNameVisible() {
val score = Score(team = "Corinthians", score = 0)
setContent(score)
onNodeWithText("Corinthians").assertIsDisplayed()
}
@RunWith(JUnit4::class)
class TeamScoreTest {
@get:Rule
val composeTestRule = createComposeRule()
fun setContent(score: Score) {
composeTestRule.setContent {
MaterialTheme {
TeamScore(score)
}
}
}
@Test
fun teamNameVisible() {
val score = Score(team = "Corinthians", score = 0)
setContent(score)
onNodeWithText("Corinthians").assertIsDisplayed()
}
@RunWith(JUnit4::class)
class TeamScoreTest {
@get:Rule
val composeTestRule = createComposeRule()
fun setContent(score: Score) {
composeTestRule.setContent {
MaterialTheme {
TeamScore(score)
}
}
}
@Test
fun teamNameVisible() {
val score = Score(team = "Corinthians", score = 0)
setContent(score)
onNodeWithText("Corinthians").assertIsDisplayed()
}
@Test
fun testIncrementDecrement() {
val score = Score(team = "Corinthians", score = 0)
setContent(score)
onNodeWithTag("TeamScore_Inc")
.performClick()
.performClick()
onNodeWithText("2").assertIsDisplayed()
runOnIdle {
assertEquals(2, score.score)
}
onNodeWithTag("TeamScore_Dec")
.performClick()
onNodeWithText("1").assertIsDisplayed()
runOnIdle {
assertEquals(1, score.score)
}
}
@Test
fun testIncrementDecrement() {
val score = Score(team = "Corinthians", score = 0)
setContent(score)
onNodeWithTag("TeamScore_Inc")
.performClick()
.performClick()
onNodeWithText("2").assertIsDisplayed()
runOnIdle {
assertEquals(2, score.score)
}
onNodeWithTag("TeamScore_Dec")
.performClick()
onNodeWithText("1").assertIsDisplayed()
runOnIdle {
assertEquals(1, score.score)
}
}
@Test
fun testIncrementDecrement() {
val score = Score(team = "Corinthians", score = 0)
setContent(score)
onNodeWithTag("TeamScore_Inc")
.performClick()
.performClick()
onNodeWithText("2").assertIsDisplayed()
runOnIdle {
assertEquals(2, score.score)
}
onNodeWithTag("TeamScore_Dec")
.performClick()
onNodeWithText("1").assertIsDisplayed()
runOnIdle {
assertEquals(1, score.score)
}
}
@Test
fun testIncrementDecrement() {
val score = Score(team = "Corinthians", score = 0)
setContent(score)
onNodeWithTag("TeamScore_Inc")
.performClick()
.performClick()
onNodeWithText("2").assertIsDisplayed()
runOnIdle {
assertEquals(2, score.score)
}
onNodeWithTag("TeamScore_Dec")
.performClick()
onNodeWithText("1").assertIsDisplayed()
runOnIdle {
assertEquals(1, score.score)
}
}
app.sli.do/event/djw2yt2e
ScrollableColumn
ScrollableColumn {
for (i in 0..200) {
Text(
"Item: $i",
modifier = Modifier.padding(8.dp).fillMaxWidth()
)
}
}
app.sli.do/event/djw2yt2e
Lists
@Composable
fun UserList(users: List<User>) {
LazyColumnFor(
items = users,
modifier = Modifier.fillMaxSize()) {
UserItem(user = it)
}
}
app.sli.do/event/djw2yt2e
Lists
@Composable
fun UserList(users: List<User>) {
LazyColumnForIndexed(
items = users,
itemContent = { index, item ->
UserItem(user = item, index = index)
}
)
}
Interoperabilidade
app.sli.do/event/djw2yt2e
Em fragments…
class MyFragment: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return ComposeView(requireContext()).apply {
setContent {
AppTheme {
SeuComposable()
}
}
}
}
}
app.sli.do/event/djw2yt2e
Em fragments…
class MyFragment: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return ComposeView(requireContext()).apply {
setContent {
AppTheme {
SeuComposable()
}
}
}
}
}
app.sli.do/event/djw2yt2e
Em arquivos de layout
<androidx.compose.ui.platform.ComposeView
android:id="@+id/my_composable"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
findViewById<ComposeView>(R.id.my_composable).setContent {
MaterialTheme {
Surface {
Text(text = "Hello!")
}
}
}
app.sli.do/event/djw2yt2e
@Composable
fun MyCalendar(onDateUpdate: (Date) -> Unit) {
AndroidView(
viewBlock = { context: Context ->
val view =
LayoutInflater.from(context).inflate(R.layout.my_layout, null, false)
val textView = view.findViewById<TextView>(R.id.txtDate)
val calendarView = view.findViewById<CalendarView>(R.id.calendarView)
calendarView?.setOnDateChangeListener { cv, year, month, day ->
val date = Calendar.getInstance().apply {
set(year, month, day)
}.time
textView?.text = date.toString()
onDateUpdate(date)
}
view
},
update = { view ->
// Update view
}
)
}
app.sli.do/event/djw2yt2e
@Composable
fun MyCalendar(onDateUpdate: (Date) -> Unit) {
AndroidView(
viewBlock = { context: Context ->
val view =
LayoutInflater.from(context).inflate(R.layout.my_layout, null, false)
val textView = view.findViewById<TextView>(R.id.txtDate)
val calendarView = view.findViewById<CalendarView>(R.id.calendarView)
calendarView?.setOnDateChangeListener { cv, year, month, day ->
val date = Calendar.getInstance().apply {
set(year, month, day)
}.time
textView?.text = date.toString()
onDateUpdate(date)
}
view
},
update = { view ->
// Update view
}
)
}
app.sli.do/event/djw2yt2e
@Composable
fun MyCalendar(onDateUpdate: (Date) -> Unit) {
AndroidView(
viewBlock = { context: Context ->
val view =
LayoutInflater.from(context).inflate(R.layout.my_layout, null, false)
val textView = view.findViewById<TextView>(R.id.txtDate)
val calendarView = view.findViewById<CalendarView>(R.id.calendarView)
calendarView?.setOnDateChangeListener { cv, year, month, day ->
val date = Calendar.getInstance().apply {
set(year, month, day)
}.time
textView?.text = date.toString()
onDateUpdate(date)
}
view
},
update = { view ->
// Update view
}
)
}
app.sli.do/event/djw2yt2e
Resources
• stringResources(R.string.your_string)
• dimensionResource(R.dimen.padding_small)
• colorResource(R.color.blue)
• …
Mudança de
estado
app.sli.do/event/djw2yt2e
Observando Estado
• LiveData.observeAsState
• Flow.collectAsState
• Observable.subscribeAsState
app.sli.do/event/djw2yt2e
LiveData
@Composable
fun UserScreen(
usersLiveData: LiveData<List<UserBinding>>,
onSaveUser: (UserBinding) -> Unit,
onDeleteUser: (UserBinding) -> Unit
) {
val users by usersLiveData.observeAsState()
Column(modifier = Modifier.fillMaxSize()) {
InputPanel(currentUser, onInsertUser = { user ->
onSaveUser(user)
})
UserList(
users = users ?: emptyList(),
onDeleteUser = onDeleteUser
)
}
}
app.sli.do/event/djw2yt2e
LiveData
@Composable
fun UserScreen(
usersLiveData: LiveData<List<UserBinding>>,
onSaveUser: (UserBinding) -> Unit,
onDeleteUser: (UserBinding) -> Unit
) {
val users by usersLiveData.observeAsState()
Column(modifier = Modifier.fillMaxSize()) {
InputPanel(currentUser, onInsertUser = { user ->
onSaveUser(user)
})
UserList(
users = users ?: emptyList(),
onDeleteUser = onDeleteUser
)
}
}
app.sli.do/event/djw2yt2e
LiveData
@Composable
fun UserScreen(
usersLiveData: LiveData<List<UserBinding>>,
onSaveUser: (UserBinding) -> Unit,
onDeleteUser: (UserBinding) -> Unit
) {
val users by usersLiveData.observeAsState()
Column(modifier = Modifier.fillMaxSize()) {
InputPanel(currentUser, onInsertUser = { user ->
onSaveUser(user)
})
UserList(
users = users ?: emptyList(),
onDeleteUser = onDeleteUser
)
}
}
app.sli.do/event/djw2yt2e
LiveData
@Composable
fun UserScreen(
usersLiveData: LiveData<List<UserBinding>>,
onSaveUser: (UserBinding) -> Unit,
onDeleteUser: (UserBinding) -> Unit
) {
val users by usersLiveData.observeAsState()
Column(modifier = Modifier.fillMaxSize()) {
InputPanel(currentUser, onInsertUser = { user ->
onSaveUser(user)
})
UserList(
users = users ?: emptyList(),
onDeleteUser = onDeleteUser
)
}
}
app.sli.do/event/djw2yt2e
LiveData
@Composable
fun UserScreen(
usersLiveData: LiveData<List<UserBinding>>,
onSaveUser: (UserBinding) -> Unit,
onDeleteUser: (UserBinding) -> Unit
) {
val users by usersLiveData.observeAsState()
Column(modifier = Modifier.fillMaxSize()) {
InputPanel(currentUser, onInsertUser = { user ->
onSaveUser(user)
})
UserList(
users = users ?: emptyList(),
onDeleteUser = onDeleteUser
)
}
}
app.sli.do/event/djw2yt2e
LiveData
UserScreen(
usersLiveData = viewModel.allUsers,
onSaveUser = { user ->
viewModel.saveUser(user)
},
onDeleteUser = { user ->
viewModel.deleteUser(user)
}
)
@Composable
fun UserScreen(
usersLiveData: LiveData<List<UserBinding>>,
onSaveUser: (UserBinding) -> Unit,
onDeleteUser: (UserBinding) -> Unit
) { … }
Coroutines
app.sli.do/event/djw2yt2e
Coroutines
@Composable
fun MyComposable() {
val welcomeMsg = remember { mutableStateOf("") }
launchInComposition {
val s = withContext(Dispatchers.IO) {
delay(5_000)
"Hello Compose!"
}
welcomeMsg.value = s
}
Text(text = welcomeMsg.value)
}
launchInComposition lança uma
coroutine quando a composição é
adicionada e a cancela automaticamente
quando a execução deixa a composição.
app.sli.do/event/djw2yt2e
Coroutines
@Composable
fun MyComposable() {
val scope = rememberCoroutineScope()
val count = remember { mutableStateOf(0) }
Text(text = "Current count: ${count.value}")
Button(onClick = {
scope.launch {
for (i in 1..10) {
withContext(Dispatchers.IO) {
delay(1_000)
}
count.value = i
}
}
}, content = { Text("Start!") })
}
rememberCoroutineScope retorna
o CoroutineScope associado ao ponto
específico da composição. Deve ser usado
para lançar coroutines em resposta à
eventos de callback
app.sli.do/event/djw2yt2e
Roadmap
app.sli.do/event/djw2yt2e
Conclusão
• A ideia do Compose parece ser muito interessante, pois segue o
paradigma moderno de desenvolvimento de UI.
• Podemos pensar em UI em outras plataformas?
• Está em versão alpha, logo NÃO USE EM PRODUÇÃO!
• Quando será que teremos todos os recursos existentes no Toolkit atual?
E quando teremos a mesma quantidade de componentes third party?
• Nós, como desenvolvedores devemos estar preparados. 

Pois desaprender é mais difícil que aprender 😉
app.sli.do/event/djw2yt2e
Referências
• Página oficial do Jetpack Compose

https://developer.android.com/jetpack/compose
• Codelab Jetpack Compose

https://codelabs.developers.google.com/codelabs/jetpack-compose-
basics/#0
• Jetpack Compose Samples

https://github.com/android/compose-samples
• Romain Guy Sample

https://github.com/romainguy/sample-materials-shop
app.sli.do/event/djw2yt2e
Referências
• Lista de classes do Compose

https://developer.android.com/reference/kotlin/androidx/ui/classes
• Compose Academy

https://compose.academy/
• Classic Android to Jetpack (by Vinay Gaba)

https://jetpackcompose.app/
• Canal #Compose no Slack do Kotlin

slack.kotlinlang.org (#compose)
app.sli.do/event/djw2yt2e
Referências
• Understanding Compose (Android Dev Summit 2019)

https://www.youtube.com/watch?v=Q9MtlmmN4Q0
• What’s new in Jetpack Compose (Android Dev Summit 2019)

https://www.youtube.com/watch?v=dtm2h-_sNDQ
• Jetpack Compose (#Android11 - 2020)

https://www.youtube.com/watch?v=U5BwfqBpiWU
• Jetpack Compose - Next Gen Kotlin UI Toolkit for Android (Right?)

https://www.youtube.com/watch?v=I5zRmCheVVg
app.sli.do/event/djw2yt2e
Referências
• Thinking in Compose

https://www.youtube.com/watch?v=SMOhl9RK0BA
• Repositório do Jetpack Compose

https://android.googlesource.com/platform/frameworks/support/+/refs/
heads/androidx-master-dev/compose/
• Request Features & Bug Tracker

https://issuetracker.google.com/issues/new?component=612128
Obrigado!
Nelson Glauber
@nglauber

More Related Content

PPTX
Kotlin Jetpack Tutorial
PDF
Jetpack Compose a new way to implement UI on Android
PDF
Declarative UIs with Jetpack Compose
PDF
Kotlin Coroutines in Practice @ KotlinConf 2018
PDF
Jetpack Compose - A Lightning Tour
PPTX
Android jetpack compose | Declarative UI
PDF
Android Jetpack Compose - Turkey 2021
PDF
Jetpack compose
Kotlin Jetpack Tutorial
Jetpack Compose a new way to implement UI on Android
Declarative UIs with Jetpack Compose
Kotlin Coroutines in Practice @ KotlinConf 2018
Jetpack Compose - A Lightning Tour
Android jetpack compose | Declarative UI
Android Jetpack Compose - Turkey 2021
Jetpack compose

What's hot (20)

PDF
Try Jetpack Compose
PDF
Deep dive into Coroutines on JVM @ KotlinConf 2017
PDF
Android Jetpack
PDF
Angular - Chapter 4 - Data and Event Handling
PDF
React lecture
PPTX
PDF
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
PPTX
Angular modules in depth
PPTX
State management in react applications (Statecharts)
PPTX
PPTX
PDF
Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...
PPTX
Jetpack Compose.pptx
PDF
Flask Introduction - Python Meetup
ODP
Angular 4 The new Http Client Module
PPTX
Nodejs functions & modules
PDF
Presentation swagger
PDF
Angular - Chapter 1 - Introduction
PDF
Basics of Model/View Qt programming
 
PDF
Angular Advanced Routing
Try Jetpack Compose
Deep dive into Coroutines on JVM @ KotlinConf 2017
Android Jetpack
Angular - Chapter 4 - Data and Event Handling
React lecture
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Angular modules in depth
State management in react applications (Statecharts)
Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...
Jetpack Compose.pptx
Flask Introduction - Python Meetup
Angular 4 The new Http Client Module
Nodejs functions & modules
Presentation swagger
Angular - Chapter 1 - Introduction
Basics of Model/View Qt programming
 
Angular Advanced Routing
Ad

Similar to Jetpack Compose a nova forma de implementar UI no Android (20)

PDF
compose_speaker_session.pdf
PPTX
Compose Camp Day 2.pptx
PPTX
Compose camp 2.pptx
PDF
What's new in android: jetpack compose 2024
PDF
Something old, Something new.pdf
PDF
Compose Camp Day 3 PPT.pptx.pdf
PDF
Jetpack Compose - Hands-on February 2020
PPTX
Day 2.pptx
PDF
Andriod Blueprint @ AISSMS Institute Of Information Techonology
PPTX
Modern app development with Jetpack Compose.pptx
PDF
Mobile Programming - 6 Textfields, Button, Showing Snackbars and Lists
PPTX
The Workflow Pattern, Composed (2021)
PDF
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
PPTX
Google I/O 2019 - what's new in Android Q and Jetpack
PDF
Mobile Programming - 2 Jetpack Compose
PPTX
Kotlin With JetPack Compose Presentation
PDF
Compose Camp - Session2.pdf
PDF
Mobile Programming - 4 Modifiers and Image Card
PPTX
Jetpack Compose - Android’s modern toolkit for building native UI
KEY
Scala on Your Phone
compose_speaker_session.pdf
Compose Camp Day 2.pptx
Compose camp 2.pptx
What's new in android: jetpack compose 2024
Something old, Something new.pdf
Compose Camp Day 3 PPT.pptx.pdf
Jetpack Compose - Hands-on February 2020
Day 2.pptx
Andriod Blueprint @ AISSMS Institute Of Information Techonology
Modern app development with Jetpack Compose.pptx
Mobile Programming - 6 Textfields, Button, Showing Snackbars and Lists
The Workflow Pattern, Composed (2021)
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
Google I/O 2019 - what's new in Android Q and Jetpack
Mobile Programming - 2 Jetpack Compose
Kotlin With JetPack Compose Presentation
Compose Camp - Session2.pdf
Mobile Programming - 4 Modifiers and Image Card
Jetpack Compose - Android’s modern toolkit for building native UI
Scala on Your Phone
Ad

More from Nelson Glauber Leal (20)

PDF
Insights no desenvolvimento Android para 2024
PDF
Seu primeiro app Android e iOS com Compose Multiplatform
PDF
Desenvolvimento Moderno de Aplicações Android 2023
PDF
Novidades incríveis do Android em 2023
PDF
Novidades das Bibliotecas Jetpack do Android (2021)
PDF
Aplicações assíncronas no Android com
Coroutines & Jetpack
PDF
Aplicações assíncronas no Android com
Coroutines & Jetpack
PDF
O que é preciso para ser um desenvolvedor Android
PDF
Arquitetando seu app Android com Jetpack
PDF
Arquitetando seu app Android com Jetpack
PDF
Aplicações Assíncronas no Android com Coroutines e Jetpack
PDF
Mastering Kotlin Standard Library
PDF
Aplicações assíncronas no Android com Coroutines & Jetpack
PDF
Introdução ao Desenvolvimento Android com Kotlin
PDF
Persisting Data on SQLite using Room
PDF
Arquitetando seu aplicativo Android com Jetpack
PDF
Desenvolvimento Moderno de Aplicativos Android
PDF
Desenvolvimento Moderno de aplicativos Android
PDF
Turbinando o desenvolvimento Android com Kotlin
PDF
Tudo que você precisa saber sobre Constraint Layout
Insights no desenvolvimento Android para 2024
Seu primeiro app Android e iOS com Compose Multiplatform
Desenvolvimento Moderno de Aplicações Android 2023
Novidades incríveis do Android em 2023
Novidades das Bibliotecas Jetpack do Android (2021)
Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & Jetpack
O que é preciso para ser um desenvolvedor Android
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
Aplicações Assíncronas no Android com Coroutines e Jetpack
Mastering Kotlin Standard Library
Aplicações assíncronas no Android com Coroutines & Jetpack
Introdução ao Desenvolvimento Android com Kotlin
Persisting Data on SQLite using Room
Arquitetando seu aplicativo Android com Jetpack
Desenvolvimento Moderno de Aplicativos Android
Desenvolvimento Moderno de aplicativos Android
Turbinando o desenvolvimento Android com Kotlin
Tudo que você precisa saber sobre Constraint Layout

Recently uploaded (20)

PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PPTX
Transform Your Business with a Software ERP System
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PPTX
ISO 45001 Occupational Health and Safety Management System
PPTX
Odoo POS Development Services by CandidRoot Solutions
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
System and Network Administraation Chapter 3
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PPTX
Online Work Permit System for Fast Permit Processing
PPTX
ai tools demonstartion for schools and inter college
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Transform Your Business with a Software ERP System
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
ISO 45001 Occupational Health and Safety Management System
Odoo POS Development Services by CandidRoot Solutions
PTS Company Brochure 2025 (1).pdf.......
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Internet Downloader Manager (IDM) Crack 6.42 Build 41
System and Network Administraation Chapter 3
Operating system designcfffgfgggggggvggggggggg
Odoo Companies in India – Driving Business Transformation.pdf
Online Work Permit System for Fast Permit Processing
ai tools demonstartion for schools and inter college
Softaken Excel to vCard Converter Software.pdf
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
How to Choose the Right IT Partner for Your Business in Malaysia
Upgrade and Innovation Strategies for SAP ERP Customers

Jetpack Compose a nova forma de implementar UI no Android

  • 1. Jetpack Compose uma nova forma de implementar UI no Android Nelson Glauber @nglauber
  • 2. Jetpack Compose uma nova forma de implementar UI no Android Nelson Glauber @nglauber
  • 3. Jetpack Compose é um moderno conjunto de ferramentas para construir interfaces gráficas para aplicações Android de forma declarativa utilizando Kotlin. app.sli.do/event/djw2yt2e
  • 4. app.sli.do/event/djw2yt2e UI Imperativa x UI Declarativa • UI Imperativa é onde uma entidade de UI (com uma classe View, por exemplo) é totalmente construída e posteriormente é modificada usando métodos e/ou propriedades públicas. • Na UI Declarativa, o desenvolvedor descreve o estado da UI e deixa o transicionamento desse estado por conta de um framework.
  • 5. app.sli.do/event/djw2yt2e Por que? • Todas as APIs do Android evoluíram, menos o UI toolkit.
 Room, View Model, Paging, Work Manager, Camera, Navigation, … • Não é simples criar uma custom view… Componente.kt, componente.xml, attrs.xml, styles.xml…
  • 6. app.sli.do/event/djw2yt2e Jetpack Compose • Segue uma abordagem declarativa similar ao SwiftUI, ReactNative, e Flutter. • É uma biblioteca separada do S.O. • É uma nova forma se se pensar na elaboração da UI: componentes ao invés de telas; Composição no lugar de Herança. • Compatível com aplicações Android existentes, podendo ser adotado progressivamente. • EXPERIMENTAL! Ainda está em versão alpha desde… ontem 😅
  • 7. app.sli.do/event/djw2yt2e Benefícios • No Compose, a UI é totalmente escrita em Kotlin e nós sabemos organizar e refatorar código melhor que arquivos XML :) • Qual o maior trabalho no front-end? 
 Deixar UI e dados sincronizados e consistentes (Data Binding, Live Data, …). • O UI toolkit atual não está nem aí pros teus problemas! 
 Ele tem o próprio estado e só avisa que o seu estado interno mudou.
  • 10. app.sli.do/event/djw2yt2e @Composable • É maneira de criar um “custom component”. 
 Simplesmente criando uma função! • @Composable não usa processamento de anotação. É uma instrução para o plugin do compilador do Kotlin. • Só pode ser chamada dentro de um contexto de uma @Composable function.
  • 11. app.sli.do/event/djw2yt2e setContent class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AppTheme { Greeting("Android") } } } } @Composable fun Greeting(name: String) { Text(text = "Hello $name!") }
  • 12. app.sli.do/event/djw2yt2e setContent class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AppTheme { Greeting("Android") } } } } @Composable fun Greeting(name: String) { Text(text = "Hello $name!") }
  • 13. app.sli.do/event/djw2yt2e setContent class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AppTheme { Greeting("Android") } } } } @Composable fun Greeting(name: String) { Text(text = "Hello $name!") }
  • 14. app.sli.do/event/djw2yt2e setContent class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AppTheme { Greeting("Android") } } } } @Composable fun Greeting(name: String) { Text(text = "Hello $name!") }
  • 15. app.sli.do/event/djw2yt2e setContent class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AppTheme { Greeting("Android") } } } } @Composable fun Greeting(name: String) { Text(stringResource(R.string.hello, name)) }
  • 16. app.sli.do/event/djw2yt2e Material Theme • Define o tema da aplicação por meio de cores, estilos, fontes e decoração de componentes. • Normalmente é o elemento raiz da tela. setContent { AppTheme { Greeting("Android") } }
  • 17. app.sli.do/event/djw2yt2e Material Theme @Composable fun AppTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val colors = if (darkTheme) { DarkColorPalette } else { LightColorPalette } MaterialTheme( colors = colors, typography = typography, shapes = shapes, content = content ) } private val DarkColorPalette = darkColors( primary = purple200, primaryVariant = purple700, secondary = teal200 ) private val LightColorPalette = lightColors( primary = purple500, primaryVariant = purple700, secondary = teal200 )
  • 19. app.sli.do/event/djw2yt2e Modifiers • Decoram um elemento • Provê parâmetros de layout • Comportamentos adicionais • São encadeados e a ordem importa!
  • 20. val shape = CutCornerShape(topLeft = 16.dp, bottomRight = 16.dp) Text( text = "Text 1", style = TextStyle( color = Color.White, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center), modifier = Modifier.fillMaxWidth() .padding(16.dp) .border(2.dp, MaterialTheme.colors.secondary, shape) .padding(1.dp) .background(MaterialTheme.colors.primary, shape) .clickable(onClick = { // Click event }) .padding(16.dp) )
  • 21. val shape = CutCornerShape(topLeft = 16.dp, bottomRight = 16.dp) Text( text = "Text 1", style = TextStyle( color = Color.White, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center), modifier = Modifier.fillMaxWidth() .padding(16.dp) .border(2.dp, MaterialTheme.colors.secondary, shape) .padding(1.dp) .background(MaterialTheme.colors.primary, shape) .clickable(onClick = { // Click event }) .padding(16.dp) )
  • 22. val shape = CutCornerShape(topLeft = 16.dp, bottomRight = 16.dp) Text( text = "Text 1", style = TextStyle( color = Color.White, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center), modifier = Modifier.fillMaxWidth() .padding(16.dp) .border(2.dp, MaterialTheme.colors.secondary, shape) .padding(1.dp) .background(MaterialTheme.colors.primary, shape) .clickable(onClick = { // Click event }) .padding(16.dp) )
  • 23. val shape = CutCornerShape(topLeft = 16.dp, bottomRight = 16.dp) Text( text = "Text 1", style = TextStyle( color = Color.White, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center), modifier = Modifier.fillMaxWidth() .padding(16.dp) .border(2.dp, MaterialTheme.colors.secondary, shape) .padding(1.dp) .background(MaterialTheme.colors.primary, shape) .clickable(onClick = { // Click event }) .padding(16.dp) )
  • 24. val shape = RoundedCornerShape(8.dp) Text( text = "Text 1", style = TextStyle( color = Color.White, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center), modifier = Modifier.fillMaxWidth() .padding(16.dp) .border(2.dp, MaterialTheme.colors.secondary, shape) .padding(1.dp) .background(MaterialTheme.colors.primary, shape) .clickable(onClick = { // Click event }) .padding(16.dp) )
  • 25. val shape = CircleShape Text( text = "Text 1", style = TextStyle( color = Color.White, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center), modifier = Modifier.fillMaxWidth() .padding(16.dp) .border(2.dp, MaterialTheme.colors.secondary, shape) .padding(1.dp) .background(MaterialTheme.colors.primary, shape) .clickable(onClick = { // Click event }) .padding(16.dp) )
  • 27. Stack(modifier = Modifier.fillMaxWidth()) { Column( modifier = Modifier .padding(16.dp) .fillMaxWidth() ) { Text("Column Text 1") Text("Column Text 2") Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly ) { Text(text = "Row Text 1") Text(text = "Row Text 2") } } Text( "Stack Text", modifier = Modifier .gravity(Alignment.TopEnd) .padding(end = 16.dp, top = 16.dp) ) }
  • 28. Stack(modifier = Modifier.fillMaxWidth()) { Column( modifier = Modifier .padding(16.dp) .fillMaxWidth() ) { Text("Column Text 1") Text("Column Text 2") Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly ) { Text(text = "Row Text 1") Text(text = "Row Text 2") } } Text( "Stack Text", modifier = Modifier .gravity(Alignment.TopEnd) .padding(end = 16.dp, top = 16.dp) ) }
  • 29. Stack(modifier = Modifier.fillMaxWidth()) { Column( modifier = Modifier .padding(16.dp) .fillMaxWidth() ) { Text("Column Text 1") Text("Column Text 2") Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly ) { Text(text = "Row Text 1") Text(text = "Row Text 2") } } Text( "Stack Text", modifier = Modifier .gravity(Alignment.TopEnd) .padding(end = 16.dp, top = 16.dp) ) }
  • 30. Stack(modifier = Modifier.fillMaxWidth()) { Column( modifier = Modifier .padding(16.dp) .fillMaxWidth() ) { Text("Column Text 1") Text("Column Text 2") Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly ) { Text(text = "Row Text 1") Text(text = "Row Text 2") } } Text( "Stack Text", modifier = Modifier .gravity(Alignment.TopEnd) .padding(end = 16.dp, top = 16.dp) ) }
  • 32. ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) { val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs() Text("Nome", modifier = Modifier.constrainAs(text1Ref) { top.linkTo(parent.top) centerHorizontallyTo(parent) }) TextField(modifier = Modifier.padding(top = 8.dp) .constrainAs(edit1Ref) { start.linkTo(parent.start) end.linkTo(parent.end) top.linkTo(text1Ref.bottom) }) Button(onClick = {}, modifier = Modifier.padding(top = 8.dp) .constrainAs(btn1Ref) { end.linkTo(edit1Ref.end) top.linkTo(edit1Ref.bottom) } ) TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp) .constrainAs(btn2Ref) { end.linkTo(btn1Ref.start) baseline.linkTo(btn1Ref.baseline) } ) }
  • 33. ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) { val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs() Text("Nome", modifier = Modifier.constrainAs(text1Ref) { top.linkTo(parent.top) centerHorizontallyTo(parent) }) TextField(modifier = Modifier.padding(top = 8.dp) .constrainAs(edit1Ref) { start.linkTo(parent.start) end.linkTo(parent.end) top.linkTo(text1Ref.bottom) }) Button(onClick = {}, modifier = Modifier.padding(top = 8.dp) .constrainAs(btn1Ref) { end.linkTo(edit1Ref.end) top.linkTo(edit1Ref.bottom) } ) TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp) .constrainAs(btn2Ref) { end.linkTo(btn1Ref.start) baseline.linkTo(btn1Ref.baseline) } ) }
  • 34. ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) { val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs() Text("Nome", modifier = Modifier.constrainAs(text1Ref) { top.linkTo(parent.top) centerHorizontallyTo(parent) }) TextField(modifier = Modifier.padding(top = 8.dp) .constrainAs(edit1Ref) { start.linkTo(parent.start) end.linkTo(parent.end) top.linkTo(text1Ref.bottom) }) Button(onClick = {}, modifier = Modifier.padding(top = 8.dp) .constrainAs(btn1Ref) { end.linkTo(edit1Ref.end) top.linkTo(edit1Ref.bottom) } ) TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp) .constrainAs(btn2Ref) { end.linkTo(btn1Ref.start) baseline.linkTo(btn1Ref.baseline) } ) }
  • 35. ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) { val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs() Text("Nome", modifier = Modifier.constrainAs(text1Ref) { top.linkTo(parent.top) centerHorizontallyTo(parent) }) TextField(modifier = Modifier.padding(top = 8.dp) .constrainAs(edit1Ref) { start.linkTo(parent.start) end.linkTo(parent.end) top.linkTo(text1Ref.bottom) }) Button(onClick = {}, modifier = Modifier.padding(top = 8.dp) .constrainAs(btn1Ref) { end.linkTo(edit1Ref.end) top.linkTo(edit1Ref.bottom) } ) TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp) .constrainAs(btn2Ref) { end.linkTo(btn1Ref.start) baseline.linkTo(btn1Ref.baseline) } ) }
  • 36. ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) { val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs() Text("Nome", modifier = Modifier.constrainAs(text1Ref) { top.linkTo(parent.top) centerHorizontallyTo(parent) }) TextField(modifier = Modifier.padding(top = 8.dp) .constrainAs(edit1Ref) { start.linkTo(parent.start) end.linkTo(parent.end) top.linkTo(text1Ref.bottom) }) Button(onClick = {}, modifier = Modifier.padding(top = 8.dp) .constrainAs(btn1Ref) { end.linkTo(edit1Ref.end) top.linkTo(edit1Ref.bottom) } ) TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp) .constrainAs(btn2Ref) { end.linkTo(btn1Ref.start) baseline.linkTo(btn1Ref.baseline) } ) }
  • 37. ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) { val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs() Text("Nome", modifier = Modifier.constrainAs(text1Ref) { top.linkTo(parent.top) centerHorizontallyTo(parent) }) TextField(modifier = Modifier.padding(top = 8.dp) .constrainAs(edit1Ref) { start.linkTo(parent.start) end.linkTo(parent.end) top.linkTo(text1Ref.bottom) }) Button(onClick = {}, modifier = Modifier.padding(top = 8.dp) .constrainAs(btn1Ref) { end.linkTo(edit1Ref.end) top.linkTo(edit1Ref.bottom) } ) TextButton(onClick = {}, modifier = Modifier.padding(end = 8.dp) .constrainAs(btn2Ref) { end.linkTo(btn1Ref.start) baseline.linkTo(btn1Ref.baseline) } ) }
  • 38. app.sli.do/event/djw2yt2e Button Button( content = { Text("Button") }, onClick = {} ) OutlinedButton( content = { Text("OutlinedButton") }, onClick = {} ) TextButton( content = { Text("TextButton") }, onClick = {} )
  • 39. app.sli.do/event/djw2yt2e Card + Ripple + Clickable @Composable fun UserItem(user: User) { Card( elevation = 4.dp, shape = RoundedCornerShape(4.dp), modifier = Modifier.fillMaxWidth().padding(8.dp) .clickable( onClick = { // Click event } ) ) { // Card Content } }
  • 40. app.sli.do/event/djw2yt2e Image Image( asset = imageResource(R.drawable.recife), contentScale = ContentScale.FillHeight ) Image( asset = vectorResource(id = R.drawable.ic_android), contentScale = ContentScale.Fit, colorFilter = ColorFilter.tint(Color.Cyan) ) CoilImage( modifier = Modifier .size(96.dp) .clip(CircleShape), data = photoUrl )
  • 41. Scaffold Scaffold( topBar = {...}, drawerContent = {...}, bodyContent = {...}, floatingActionButton = {...}, bottomBar = {...} )
  • 42. TopAppBar( backgroundColor = MaterialTheme.colors.primary, contentColor = Color.Yellow, title = { Text(text = "Compose") }, actions = { IconButton( onClick = {}, icon = { Icon(Icons.Default.Search) } ) DropdownMenu(…) }, navigationIcon = { IconButton( icon = { Icon(Icons.Default.Menu) }, onClick = { scaffoldState.drawerState.open() } ) } )
  • 43. FloatingActionButton( onClick = {}, icon = { Icon(Icons.Filled.Add) }, backgroundColor = Color.Red, contentColor = Color.White, shape = CutCornerShape( topLeft = 20.dp, bottomRight = 20.dp ) )
  • 44. BottomAppBar( backgroundColor = MaterialTheme.colors.primary, content = { BottomNavigationItem( icon = { Icon(Icons.Filled.Home) }, selected = selectedTab == 0, onSelect = { selectedTab = 0 }, selectedContentColor = Color.White, unselectedContentColor = Color.DarkGray, label = { Text(text = "Home") } ) BottomNavigationItem(…) } )
  • 45. BottomAppBar( backgroundColor = MaterialTheme.colors.primary, content = { BottomNavigationItem( icon = { Icon(Icons.Filled.Home) }, selected = selectedTab == 0, onSelect = { selectedTab = 0 }, selectedContentColor = Color.White, unselectedContentColor = Color.DarkGray, label = { Text(text = "Home") } ) BottomNavigationItem(…) } )
  • 46. app.sli.do/event/djw2yt2e State • Single Source of Truth • O Componente é atualizado quando o seu estado muda var nameState by remember { mutableStateOf("") } TextField( value = nameState, label = { Text("Digite seu nome") }, onValueChange = { s: String -> nameState = s } )
  • 47. app.sli.do/event/djw2yt2e Switch & Checkbox var isChecked by remember { mutableStateOf(true) } Checkbox( checked = isChecked, onCheckedChange = { isChecked = it } ) var isOn by remember { mutableStateOf(true) } Switch( checked = isOn, onCheckedChange = { isOn = it } )
  • 48. app.sli.do/event/djw2yt2e RadioButton val options = listOf("Opção 1", "Opção 2", "Opção 3") var currentOption by remember { mutableStateOf("Opção 1") } options.forEach { text -> Row( Modifier.fillMaxWidth() ) { RadioButton( selected = (text == currentOption), onClick = { currentOption = text } ) Text(text = text) } }
  • 50. app.sli.do/event/djw2yt2e State class Score( team: String, score: Int ) { var team by mutableStateOf(team) var score by mutableStateOf(score) }
  • 51. app.sli.do/event/djw2yt2e class Score( team: String, score: Int ) { var team by mutableStateOf(team) var score by mutableStateOf(score) }
  • 52. @Composable fun TeamScore(score: Score) { Column(horizontalGravity = Alignment.CenterHorizontally) { Text(text = score.team, style = MaterialTheme.typography.h6) Button( content = { Text("+") }, onClick = { score.score += 1 } ) Text(text = score.score.toString(), style = MaterialTheme.typography.h5) Button( content = { Text("-") }, onClick = { score.score = max(score.score - 1, 0) } ) } }
  • 53. @Composable fun ScoreScreen(homeScore: Score, visitorScore: Score) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalGravity = Alignment.CenterHorizontally ) { Row { TeamScore(score = homeScore) Text(text = "x", modifier = Modifier.padding(horizontal = 8.dp), style = MaterialTheme.typography.h6) TeamScore(score = visitorScore) } OutlinedButton( modifier = Modifier.padding(top = 16.dp), content = { Text("Reset") }, onClick = { homeScore.score = 0 visitorScore.score = 0 } ) } }
  • 55. app.sli.do/event/djw2yt2e Testes dependencies { ... testImplementation 'junit:junit:4.13' androidTestImplementation "androidx.ui:ui-test:$compose_version" } <activity android:name="androidx.activity.ComponentActivity" />
  • 56. @Composable fun TeamScore(score: Score) { Column(horizontalGravity = Alignment.CenterHorizontally) { Text(text = score.team, style = MaterialTheme.typography.h6) Button( content = { Text("+") }, onClick = { score.score += 1 } ) Text(text = score.score.toString(), style = MaterialTheme.typography.h5) Button( content = { Text("-") }, onClick = { score.score = max(score.score - 1, 0) } ) } }
  • 57. @Composable fun TeamScore(score: Score) { Column(horizontalGravity = Alignment.CenterHorizontally) { Text(text = score.team, style = MaterialTheme.typography.h6) Button( content = { Text("+") }, modifier = Modifier.testTag("TeamScore_Inc"), onClick = { score.score += 1 } ) Text(text = score.score.toString(), style = MaterialTheme.typography.h5) Button( content = { Text("-") }, modifier = Modifier.testTag("TeamScore_Dec"), onClick = { score.score = max(score.score - 1, 0) } ) } }
  • 58. @RunWith(JUnit4::class) class TeamScoreTest { @get:Rule val composeTestRule = createComposeRule() fun setContent(score: Score) { composeTestRule.setContent { MaterialTheme { TeamScore(score) } } } @Test fun teamNameVisible() { val score = Score(team = "Corinthians", score = 0) setContent(score) onNodeWithText("Corinthians").assertIsDisplayed() }
  • 59. @RunWith(JUnit4::class) class TeamScoreTest { @get:Rule val composeTestRule = createComposeRule() fun setContent(score: Score) { composeTestRule.setContent { MaterialTheme { TeamScore(score) } } } @Test fun teamNameVisible() { val score = Score(team = "Corinthians", score = 0) setContent(score) onNodeWithText("Corinthians").assertIsDisplayed() }
  • 60. @RunWith(JUnit4::class) class TeamScoreTest { @get:Rule val composeTestRule = createComposeRule() fun setContent(score: Score) { composeTestRule.setContent { MaterialTheme { TeamScore(score) } } } @Test fun teamNameVisible() { val score = Score(team = "Corinthians", score = 0) setContent(score) onNodeWithText("Corinthians").assertIsDisplayed() }
  • 61. @Test fun testIncrementDecrement() { val score = Score(team = "Corinthians", score = 0) setContent(score) onNodeWithTag("TeamScore_Inc") .performClick() .performClick() onNodeWithText("2").assertIsDisplayed() runOnIdle { assertEquals(2, score.score) } onNodeWithTag("TeamScore_Dec") .performClick() onNodeWithText("1").assertIsDisplayed() runOnIdle { assertEquals(1, score.score) } }
  • 62. @Test fun testIncrementDecrement() { val score = Score(team = "Corinthians", score = 0) setContent(score) onNodeWithTag("TeamScore_Inc") .performClick() .performClick() onNodeWithText("2").assertIsDisplayed() runOnIdle { assertEquals(2, score.score) } onNodeWithTag("TeamScore_Dec") .performClick() onNodeWithText("1").assertIsDisplayed() runOnIdle { assertEquals(1, score.score) } }
  • 63. @Test fun testIncrementDecrement() { val score = Score(team = "Corinthians", score = 0) setContent(score) onNodeWithTag("TeamScore_Inc") .performClick() .performClick() onNodeWithText("2").assertIsDisplayed() runOnIdle { assertEquals(2, score.score) } onNodeWithTag("TeamScore_Dec") .performClick() onNodeWithText("1").assertIsDisplayed() runOnIdle { assertEquals(1, score.score) } }
  • 64. @Test fun testIncrementDecrement() { val score = Score(team = "Corinthians", score = 0) setContent(score) onNodeWithTag("TeamScore_Inc") .performClick() .performClick() onNodeWithText("2").assertIsDisplayed() runOnIdle { assertEquals(2, score.score) } onNodeWithTag("TeamScore_Dec") .performClick() onNodeWithText("1").assertIsDisplayed() runOnIdle { assertEquals(1, score.score) } }
  • 65. app.sli.do/event/djw2yt2e ScrollableColumn ScrollableColumn { for (i in 0..200) { Text( "Item: $i", modifier = Modifier.padding(8.dp).fillMaxWidth() ) } }
  • 66. app.sli.do/event/djw2yt2e Lists @Composable fun UserList(users: List<User>) { LazyColumnFor( items = users, modifier = Modifier.fillMaxSize()) { UserItem(user = it) } }
  • 67. app.sli.do/event/djw2yt2e Lists @Composable fun UserList(users: List<User>) { LazyColumnForIndexed( items = users, itemContent = { index, item -> UserItem(user = item, index = index) } ) }
  • 69. app.sli.do/event/djw2yt2e Em fragments… class MyFragment: Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return ComposeView(requireContext()).apply { setContent { AppTheme { SeuComposable() } } } } }
  • 70. app.sli.do/event/djw2yt2e Em fragments… class MyFragment: Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return ComposeView(requireContext()).apply { setContent { AppTheme { SeuComposable() } } } } }
  • 71. app.sli.do/event/djw2yt2e Em arquivos de layout <androidx.compose.ui.platform.ComposeView android:id="@+id/my_composable" android:layout_width="wrap_content" android:layout_height="wrap_content" /> findViewById<ComposeView>(R.id.my_composable).setContent { MaterialTheme { Surface { Text(text = "Hello!") } } }
  • 72. app.sli.do/event/djw2yt2e @Composable fun MyCalendar(onDateUpdate: (Date) -> Unit) { AndroidView( viewBlock = { context: Context -> val view = LayoutInflater.from(context).inflate(R.layout.my_layout, null, false) val textView = view.findViewById<TextView>(R.id.txtDate) val calendarView = view.findViewById<CalendarView>(R.id.calendarView) calendarView?.setOnDateChangeListener { cv, year, month, day -> val date = Calendar.getInstance().apply { set(year, month, day) }.time textView?.text = date.toString() onDateUpdate(date) } view }, update = { view -> // Update view } ) }
  • 73. app.sli.do/event/djw2yt2e @Composable fun MyCalendar(onDateUpdate: (Date) -> Unit) { AndroidView( viewBlock = { context: Context -> val view = LayoutInflater.from(context).inflate(R.layout.my_layout, null, false) val textView = view.findViewById<TextView>(R.id.txtDate) val calendarView = view.findViewById<CalendarView>(R.id.calendarView) calendarView?.setOnDateChangeListener { cv, year, month, day -> val date = Calendar.getInstance().apply { set(year, month, day) }.time textView?.text = date.toString() onDateUpdate(date) } view }, update = { view -> // Update view } ) }
  • 74. app.sli.do/event/djw2yt2e @Composable fun MyCalendar(onDateUpdate: (Date) -> Unit) { AndroidView( viewBlock = { context: Context -> val view = LayoutInflater.from(context).inflate(R.layout.my_layout, null, false) val textView = view.findViewById<TextView>(R.id.txtDate) val calendarView = view.findViewById<CalendarView>(R.id.calendarView) calendarView?.setOnDateChangeListener { cv, year, month, day -> val date = Calendar.getInstance().apply { set(year, month, day) }.time textView?.text = date.toString() onDateUpdate(date) } view }, update = { view -> // Update view } ) }
  • 77. app.sli.do/event/djw2yt2e Observando Estado • LiveData.observeAsState • Flow.collectAsState • Observable.subscribeAsState
  • 78. app.sli.do/event/djw2yt2e LiveData @Composable fun UserScreen( usersLiveData: LiveData<List<UserBinding>>, onSaveUser: (UserBinding) -> Unit, onDeleteUser: (UserBinding) -> Unit ) { val users by usersLiveData.observeAsState() Column(modifier = Modifier.fillMaxSize()) { InputPanel(currentUser, onInsertUser = { user -> onSaveUser(user) }) UserList( users = users ?: emptyList(), onDeleteUser = onDeleteUser ) } }
  • 79. app.sli.do/event/djw2yt2e LiveData @Composable fun UserScreen( usersLiveData: LiveData<List<UserBinding>>, onSaveUser: (UserBinding) -> Unit, onDeleteUser: (UserBinding) -> Unit ) { val users by usersLiveData.observeAsState() Column(modifier = Modifier.fillMaxSize()) { InputPanel(currentUser, onInsertUser = { user -> onSaveUser(user) }) UserList( users = users ?: emptyList(), onDeleteUser = onDeleteUser ) } }
  • 80. app.sli.do/event/djw2yt2e LiveData @Composable fun UserScreen( usersLiveData: LiveData<List<UserBinding>>, onSaveUser: (UserBinding) -> Unit, onDeleteUser: (UserBinding) -> Unit ) { val users by usersLiveData.observeAsState() Column(modifier = Modifier.fillMaxSize()) { InputPanel(currentUser, onInsertUser = { user -> onSaveUser(user) }) UserList( users = users ?: emptyList(), onDeleteUser = onDeleteUser ) } }
  • 81. app.sli.do/event/djw2yt2e LiveData @Composable fun UserScreen( usersLiveData: LiveData<List<UserBinding>>, onSaveUser: (UserBinding) -> Unit, onDeleteUser: (UserBinding) -> Unit ) { val users by usersLiveData.observeAsState() Column(modifier = Modifier.fillMaxSize()) { InputPanel(currentUser, onInsertUser = { user -> onSaveUser(user) }) UserList( users = users ?: emptyList(), onDeleteUser = onDeleteUser ) } }
  • 82. app.sli.do/event/djw2yt2e LiveData @Composable fun UserScreen( usersLiveData: LiveData<List<UserBinding>>, onSaveUser: (UserBinding) -> Unit, onDeleteUser: (UserBinding) -> Unit ) { val users by usersLiveData.observeAsState() Column(modifier = Modifier.fillMaxSize()) { InputPanel(currentUser, onInsertUser = { user -> onSaveUser(user) }) UserList( users = users ?: emptyList(), onDeleteUser = onDeleteUser ) } }
  • 83. app.sli.do/event/djw2yt2e LiveData UserScreen( usersLiveData = viewModel.allUsers, onSaveUser = { user -> viewModel.saveUser(user) }, onDeleteUser = { user -> viewModel.deleteUser(user) } ) @Composable fun UserScreen( usersLiveData: LiveData<List<UserBinding>>, onSaveUser: (UserBinding) -> Unit, onDeleteUser: (UserBinding) -> Unit ) { … }
  • 85. app.sli.do/event/djw2yt2e Coroutines @Composable fun MyComposable() { val welcomeMsg = remember { mutableStateOf("") } launchInComposition { val s = withContext(Dispatchers.IO) { delay(5_000) "Hello Compose!" } welcomeMsg.value = s } Text(text = welcomeMsg.value) } launchInComposition lança uma coroutine quando a composição é adicionada e a cancela automaticamente quando a execução deixa a composição.
  • 86. app.sli.do/event/djw2yt2e Coroutines @Composable fun MyComposable() { val scope = rememberCoroutineScope() val count = remember { mutableStateOf(0) } Text(text = "Current count: ${count.value}") Button(onClick = { scope.launch { for (i in 1..10) { withContext(Dispatchers.IO) { delay(1_000) } count.value = i } } }, content = { Text("Start!") }) } rememberCoroutineScope retorna o CoroutineScope associado ao ponto específico da composição. Deve ser usado para lançar coroutines em resposta à eventos de callback
  • 88. app.sli.do/event/djw2yt2e Conclusão • A ideia do Compose parece ser muito interessante, pois segue o paradigma moderno de desenvolvimento de UI. • Podemos pensar em UI em outras plataformas? • Está em versão alpha, logo NÃO USE EM PRODUÇÃO! • Quando será que teremos todos os recursos existentes no Toolkit atual? E quando teremos a mesma quantidade de componentes third party? • Nós, como desenvolvedores devemos estar preparados. 
 Pois desaprender é mais difícil que aprender 😉
  • 89. app.sli.do/event/djw2yt2e Referências • Página oficial do Jetpack Compose
 https://developer.android.com/jetpack/compose • Codelab Jetpack Compose
 https://codelabs.developers.google.com/codelabs/jetpack-compose- basics/#0 • Jetpack Compose Samples
 https://github.com/android/compose-samples • Romain Guy Sample
 https://github.com/romainguy/sample-materials-shop
  • 90. app.sli.do/event/djw2yt2e Referências • Lista de classes do Compose
 https://developer.android.com/reference/kotlin/androidx/ui/classes • Compose Academy
 https://compose.academy/ • Classic Android to Jetpack (by Vinay Gaba)
 https://jetpackcompose.app/ • Canal #Compose no Slack do Kotlin
 slack.kotlinlang.org (#compose)
  • 91. app.sli.do/event/djw2yt2e Referências • Understanding Compose (Android Dev Summit 2019)
 https://www.youtube.com/watch?v=Q9MtlmmN4Q0 • What’s new in Jetpack Compose (Android Dev Summit 2019)
 https://www.youtube.com/watch?v=dtm2h-_sNDQ • Jetpack Compose (#Android11 - 2020)
 https://www.youtube.com/watch?v=U5BwfqBpiWU • Jetpack Compose - Next Gen Kotlin UI Toolkit for Android (Right?)
 https://www.youtube.com/watch?v=I5zRmCheVVg
  • 92. app.sli.do/event/djw2yt2e Referências • Thinking in Compose
 https://www.youtube.com/watch?v=SMOhl9RK0BA • Repositório do Jetpack Compose
 https://android.googlesource.com/platform/frameworks/support/+/refs/ heads/androidx-master-dev/compose/ • Request Features & Bug Tracker
 https://issuetracker.google.com/issues/new?component=612128