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
Ad

More Related Content

What's hot (20)

Java EE 7 Batch processing in the Real World
Java EE 7 Batch processing in the Real WorldJava EE 7 Batch processing in the Real World
Java EE 7 Batch processing in the Real World
Roberto Cortez
 
Py.test
Py.testPy.test
Py.test
soasme
 
Jetpack Compose - A Lightning Tour
Jetpack Compose - A Lightning TourJetpack Compose - A Lightning Tour
Jetpack Compose - A Lightning Tour
Matthew Clarke
 
Jetpack Compose.pptx
Jetpack Compose.pptxJetpack Compose.pptx
Jetpack Compose.pptx
GDSCVJTI
 
Puppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing ToolPuppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing Tool
Miki Lombardi
 
Object Oriented Javascript
Object Oriented JavascriptObject Oriented Javascript
Object Oriented Javascript
NexThoughts Technologies
 
Building MAUI UI in C#.pptx
Building MAUI UI in C#.pptxBuilding MAUI UI in C#.pptx
Building MAUI UI in C#.pptx
Brandon Minnick, MBA
 
Android Jetpack Compose - Turkey 2021
Android Jetpack Compose - Turkey 2021Android Jetpack Compose - Turkey 2021
Android Jetpack Compose - Turkey 2021
Nelson Glauber Leal
 
Fabric.js — Building a Canvas Library
Fabric.js — Building a Canvas LibraryFabric.js — Building a Canvas Library
Fabric.js — Building a Canvas Library
Juriy Zaytsev
 
Le Wagon - UI components design
Le Wagon - UI components designLe Wagon - UI components design
Le Wagon - UI components design
Boris Paillard
 
Coroutines for Kotlin Multiplatform in Practise
Coroutines for Kotlin Multiplatform in PractiseCoroutines for Kotlin Multiplatform in Practise
Coroutines for Kotlin Multiplatform in Practise
Christian Melchior
 
Introduction to React JS
Introduction to React JSIntroduction to React JS
Introduction to React JS
Bethmi Gunasekara
 
Jetpack Compose beginner.pdf
Jetpack Compose beginner.pdfJetpack Compose beginner.pdf
Jetpack Compose beginner.pdf
AayushmaAgrawal
 
Webpack Introduction
Webpack IntroductionWebpack Introduction
Webpack Introduction
Anjali Chawla
 
React native
React nativeReact native
React native
Mohammed El Rafie Tarabay
 
An Intro into webpack
An Intro into webpackAn Intro into webpack
An Intro into webpack
Squash Apps Pvt Ltd
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS Architecture
Eyal Vardi
 
Javascript projects Course
Javascript projects CourseJavascript projects Course
Javascript projects Course
Laurence Svekis ✔
 
Basic Html Tags Tutorial For Kids
Basic Html Tags Tutorial For KidsBasic Html Tags Tutorial For Kids
Basic Html Tags Tutorial For Kids
Olivia Moran
 
Javascript
JavascriptJavascript
Javascript
Vibhor Grover
 
Java EE 7 Batch processing in the Real World
Java EE 7 Batch processing in the Real WorldJava EE 7 Batch processing in the Real World
Java EE 7 Batch processing in the Real World
Roberto Cortez
 
Py.test
Py.testPy.test
Py.test
soasme
 
Jetpack Compose - A Lightning Tour
Jetpack Compose - A Lightning TourJetpack Compose - A Lightning Tour
Jetpack Compose - A Lightning Tour
Matthew Clarke
 
Jetpack Compose.pptx
Jetpack Compose.pptxJetpack Compose.pptx
Jetpack Compose.pptx
GDSCVJTI
 
Puppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing ToolPuppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing Tool
Miki Lombardi
 
Android Jetpack Compose - Turkey 2021
Android Jetpack Compose - Turkey 2021Android Jetpack Compose - Turkey 2021
Android Jetpack Compose - Turkey 2021
Nelson Glauber Leal
 
Fabric.js — Building a Canvas Library
Fabric.js — Building a Canvas LibraryFabric.js — Building a Canvas Library
Fabric.js — Building a Canvas Library
Juriy Zaytsev
 
Le Wagon - UI components design
Le Wagon - UI components designLe Wagon - UI components design
Le Wagon - UI components design
Boris Paillard
 
Coroutines for Kotlin Multiplatform in Practise
Coroutines for Kotlin Multiplatform in PractiseCoroutines for Kotlin Multiplatform in Practise
Coroutines for Kotlin Multiplatform in Practise
Christian Melchior
 
Jetpack Compose beginner.pdf
Jetpack Compose beginner.pdfJetpack Compose beginner.pdf
Jetpack Compose beginner.pdf
AayushmaAgrawal
 
Webpack Introduction
Webpack IntroductionWebpack Introduction
Webpack Introduction
Anjali Chawla
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS Architecture
Eyal Vardi
 
Basic Html Tags Tutorial For Kids
Basic Html Tags Tutorial For KidsBasic Html Tags Tutorial For Kids
Basic Html Tags Tutorial For Kids
Olivia Moran
 

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

compose_speaker_session.pdf
compose_speaker_session.pdfcompose_speaker_session.pdf
compose_speaker_session.pdf
AnkurAgarwal151093
 
High Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScriptHigh Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScript
Leonardo Borges
 
shiny.pdf
shiny.pdfshiny.pdf
shiny.pdf
Ashwini Kalantri
 
Jetpack Compose - Hands-on February 2020
Jetpack Compose - Hands-on February 2020Jetpack Compose - Hands-on February 2020
Jetpack Compose - Hands-on February 2020
Pedro Veloso
 
Simple React Todo List
Simple React Todo ListSimple React Todo List
Simple React Todo List
Ritesh Chaudhari
 
CodeMash - Building Rich Apps with Groovy SwingBuilder
CodeMash - Building Rich Apps with Groovy SwingBuilderCodeMash - Building Rich Apps with Groovy SwingBuilder
CodeMash - Building Rich Apps with Groovy SwingBuilder
Andres Almiray
 
App_development22222222222222222222.pptx
App_development22222222222222222222.pptxApp_development22222222222222222222.pptx
App_development22222222222222222222.pptx
sameehamoogab
 
MOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app developmentMOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app development
anistar sung
 
20230721_OKC_Meetup_MuleSoft.pptx
20230721_OKC_Meetup_MuleSoft.pptx20230721_OKC_Meetup_MuleSoft.pptx
20230721_OKC_Meetup_MuleSoft.pptx
DianeKesler1
 
Quick Intro to Android Development
Quick Intro to Android DevelopmentQuick Intro to Android Development
Quick Intro to Android Development
Jussi Pohjolainen
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
Juan C Catalan
 
Qt Workshop
Qt WorkshopQt Workshop
Qt Workshop
Johan Thelin
 
Writing DSL with Applicative Functors
Writing DSL with Applicative FunctorsWriting DSL with Applicative Functors
Writing DSL with Applicative Functors
David Galichet
 
Android L01 - Warm Up
Android L01 - Warm UpAndroid L01 - Warm Up
Android L01 - Warm Up
Mohammad Shaker
 
Software Language Design & Engineering
Software Language Design & EngineeringSoftware Language Design & Engineering
Software Language Design & Engineering
Eelco Visser
 
mobl
moblmobl
mobl
zefhemel
 
Compose Camp Day 3 PPT.pptx.pdf
Compose Camp Day 3 PPT.pptx.pdfCompose Camp Day 3 PPT.pptx.pdf
Compose Camp Day 3 PPT.pptx.pdf
METDSC
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
toddbr
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
ssuserb6c2641
 
Refactoring
RefactoringRefactoring
Refactoring
Amir Barylko
 
High Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScriptHigh Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScript
Leonardo Borges
 
Jetpack Compose - Hands-on February 2020
Jetpack Compose - Hands-on February 2020Jetpack Compose - Hands-on February 2020
Jetpack Compose - Hands-on February 2020
Pedro Veloso
 
CodeMash - Building Rich Apps with Groovy SwingBuilder
CodeMash - Building Rich Apps with Groovy SwingBuilderCodeMash - Building Rich Apps with Groovy SwingBuilder
CodeMash - Building Rich Apps with Groovy SwingBuilder
Andres Almiray
 
App_development22222222222222222222.pptx
App_development22222222222222222222.pptxApp_development22222222222222222222.pptx
App_development22222222222222222222.pptx
sameehamoogab
 
MOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app developmentMOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app development
anistar sung
 
20230721_OKC_Meetup_MuleSoft.pptx
20230721_OKC_Meetup_MuleSoft.pptx20230721_OKC_Meetup_MuleSoft.pptx
20230721_OKC_Meetup_MuleSoft.pptx
DianeKesler1
 
Quick Intro to Android Development
Quick Intro to Android DevelopmentQuick Intro to Android Development
Quick Intro to Android Development
Jussi Pohjolainen
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
Juan C Catalan
 
Writing DSL with Applicative Functors
Writing DSL with Applicative FunctorsWriting DSL with Applicative Functors
Writing DSL with Applicative Functors
David Galichet
 
Software Language Design & Engineering
Software Language Design & EngineeringSoftware Language Design & Engineering
Software Language Design & Engineering
Eelco Visser
 
Compose Camp Day 3 PPT.pptx.pdf
Compose Camp Day 3 PPT.pptx.pdfCompose Camp Day 3 PPT.pptx.pdf
Compose Camp Day 3 PPT.pptx.pdf
METDSC
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
toddbr
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
ssuserb6c2641
 
Ad

More from Nelson Glauber Leal (20)

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

Recently uploaded (20)

Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and CollaborateMeet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Maxim Salnikov
 
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
steaveroggers
 
Exploring Code Comprehension in Scientific Programming: Preliminary Insight...
Exploring Code Comprehension  in Scientific Programming:  Preliminary Insight...Exploring Code Comprehension  in Scientific Programming:  Preliminary Insight...
Exploring Code Comprehension in Scientific Programming: Preliminary Insight...
University of Hawai‘i at Mānoa
 
Automation Techniques in RPA - UiPath Certificate
Automation Techniques in RPA - UiPath CertificateAutomation Techniques in RPA - UiPath Certificate
Automation Techniques in RPA - UiPath Certificate
VICTOR MAESTRE RAMIREZ
 
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New VersionPixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
saimabibi60507
 
Societal challenges of AI: biases, multilinguism and sustainability
Societal challenges of AI: biases, multilinguism and sustainabilitySocietal challenges of AI: biases, multilinguism and sustainability
Societal challenges of AI: biases, multilinguism and sustainability
Jordi Cabot
 
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
Andre Hora
 
Adobe Illustrator Crack FREE Download 2025 Latest Version
Adobe Illustrator Crack FREE Download 2025 Latest VersionAdobe Illustrator Crack FREE Download 2025 Latest Version
Adobe Illustrator Crack FREE Download 2025 Latest Version
kashifyounis067
 
How can one start with crypto wallet development.pptx
How can one start with crypto wallet development.pptxHow can one start with crypto wallet development.pptx
How can one start with crypto wallet development.pptx
laravinson24
 
Top 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docxTop 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docx
Portli
 
Why Orangescrum Is a Game Changer for Construction Companies in 2025
Why Orangescrum Is a Game Changer for Construction Companies in 2025Why Orangescrum Is a Game Changer for Construction Companies in 2025
Why Orangescrum Is a Game Changer for Construction Companies in 2025
Orangescrum
 
Adobe Lightroom Classic Crack FREE Latest link 2025
Adobe Lightroom Classic Crack FREE Latest link 2025Adobe Lightroom Classic Crack FREE Latest link 2025
Adobe Lightroom Classic Crack FREE Latest link 2025
kashifyounis067
 
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Lionel Briand
 
Proactive Vulnerability Detection in Source Code Using Graph Neural Networks:...
Proactive Vulnerability Detection in Source Code Using Graph Neural Networks:...Proactive Vulnerability Detection in Source Code Using Graph Neural Networks:...
Proactive Vulnerability Detection in Source Code Using Graph Neural Networks:...
Ranjan Baisak
 
Download YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full ActivatedDownload YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full Activated
saniamalik72555
 
EASEUS Partition Master Crack + License Code
EASEUS Partition Master Crack + License CodeEASEUS Partition Master Crack + License Code
EASEUS Partition Master Crack + License Code
aneelaramzan63
 
What Do Contribution Guidelines Say About Software Testing? (MSR 2025)
What Do Contribution Guidelines Say About Software Testing? (MSR 2025)What Do Contribution Guidelines Say About Software Testing? (MSR 2025)
What Do Contribution Guidelines Say About Software Testing? (MSR 2025)
Andre Hora
 
PDF Reader Pro Crack Latest Version FREE Download 2025
PDF Reader Pro Crack Latest Version FREE Download 2025PDF Reader Pro Crack Latest Version FREE Download 2025
PDF Reader Pro Crack Latest Version FREE Download 2025
mu394968
 
Who Watches the Watchmen (SciFiDevCon 2025)
Who Watches the Watchmen (SciFiDevCon 2025)Who Watches the Watchmen (SciFiDevCon 2025)
Who Watches the Watchmen (SciFiDevCon 2025)
Allon Mureinik
 
Solidworks Crack 2025 latest new + license code
Solidworks Crack 2025 latest new + license codeSolidworks Crack 2025 latest new + license code
Solidworks Crack 2025 latest new + license code
aneelaramzan63
 
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and CollaborateMeet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Maxim Salnikov
 
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
steaveroggers
 
Exploring Code Comprehension in Scientific Programming: Preliminary Insight...
Exploring Code Comprehension  in Scientific Programming:  Preliminary Insight...Exploring Code Comprehension  in Scientific Programming:  Preliminary Insight...
Exploring Code Comprehension in Scientific Programming: Preliminary Insight...
University of Hawai‘i at Mānoa
 
Automation Techniques in RPA - UiPath Certificate
Automation Techniques in RPA - UiPath CertificateAutomation Techniques in RPA - UiPath Certificate
Automation Techniques in RPA - UiPath Certificate
VICTOR MAESTRE RAMIREZ
 
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New VersionPixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
saimabibi60507
 
Societal challenges of AI: biases, multilinguism and sustainability
Societal challenges of AI: biases, multilinguism and sustainabilitySocietal challenges of AI: biases, multilinguism and sustainability
Societal challenges of AI: biases, multilinguism and sustainability
Jordi Cabot
 
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
Andre Hora
 
Adobe Illustrator Crack FREE Download 2025 Latest Version
Adobe Illustrator Crack FREE Download 2025 Latest VersionAdobe Illustrator Crack FREE Download 2025 Latest Version
Adobe Illustrator Crack FREE Download 2025 Latest Version
kashifyounis067
 
How can one start with crypto wallet development.pptx
How can one start with crypto wallet development.pptxHow can one start with crypto wallet development.pptx
How can one start with crypto wallet development.pptx
laravinson24
 
Top 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docxTop 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docx
Portli
 
Why Orangescrum Is a Game Changer for Construction Companies in 2025
Why Orangescrum Is a Game Changer for Construction Companies in 2025Why Orangescrum Is a Game Changer for Construction Companies in 2025
Why Orangescrum Is a Game Changer for Construction Companies in 2025
Orangescrum
 
Adobe Lightroom Classic Crack FREE Latest link 2025
Adobe Lightroom Classic Crack FREE Latest link 2025Adobe Lightroom Classic Crack FREE Latest link 2025
Adobe Lightroom Classic Crack FREE Latest link 2025
kashifyounis067
 
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Lionel Briand
 
Proactive Vulnerability Detection in Source Code Using Graph Neural Networks:...
Proactive Vulnerability Detection in Source Code Using Graph Neural Networks:...Proactive Vulnerability Detection in Source Code Using Graph Neural Networks:...
Proactive Vulnerability Detection in Source Code Using Graph Neural Networks:...
Ranjan Baisak
 
Download YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full ActivatedDownload YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full Activated
saniamalik72555
 
EASEUS Partition Master Crack + License Code
EASEUS Partition Master Crack + License CodeEASEUS Partition Master Crack + License Code
EASEUS Partition Master Crack + License Code
aneelaramzan63
 
What Do Contribution Guidelines Say About Software Testing? (MSR 2025)
What Do Contribution Guidelines Say About Software Testing? (MSR 2025)What Do Contribution Guidelines Say About Software Testing? (MSR 2025)
What Do Contribution Guidelines Say About Software Testing? (MSR 2025)
Andre Hora
 
PDF Reader Pro Crack Latest Version FREE Download 2025
PDF Reader Pro Crack Latest Version FREE Download 2025PDF Reader Pro Crack Latest Version FREE Download 2025
PDF Reader Pro Crack Latest Version FREE Download 2025
mu394968
 
Who Watches the Watchmen (SciFiDevCon 2025)
Who Watches the Watchmen (SciFiDevCon 2025)Who Watches the Watchmen (SciFiDevCon 2025)
Who Watches the Watchmen (SciFiDevCon 2025)
Allon Mureinik
 
Solidworks Crack 2025 latest new + license code
Solidworks Crack 2025 latest new + license codeSolidworks Crack 2025 latest new + license code
Solidworks Crack 2025 latest new + license code
aneelaramzan63
 

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