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

What's hot

Jetpack Compose beginner.pdf
Jetpack Compose beginner.pdfJetpack Compose beginner.pdf
Jetpack Compose beginner.pdf
AayushmaAgrawal
 
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesThreading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Lauren Yew
 
Jetpack Compose - Android’s modern toolkit for building native UI
Jetpack Compose - Android’s modern toolkit for building native UIJetpack Compose - Android’s modern toolkit for building native UI
Jetpack Compose - Android’s modern toolkit for building native UI
Gilang Ramadhan
 
Kotlin Basics & Introduction to Jetpack Compose.pptx
Kotlin Basics & Introduction to Jetpack Compose.pptxKotlin Basics & Introduction to Jetpack Compose.pptx
Kotlin Basics & Introduction to Jetpack Compose.pptx
takshilkunadia
 
Jetpack Compose.pptx
Jetpack Compose.pptxJetpack Compose.pptx
Jetpack Compose.pptx
GDSCVJTI
 
Jetpack Compose - A Lightning Tour
Jetpack Compose - A Lightning TourJetpack Compose - A Lightning Tour
Jetpack Compose - A Lightning Tour
Matthew Clarke
 
Introduction to Kotlin coroutines
Introduction to Kotlin coroutinesIntroduction to Kotlin coroutines
Introduction to Kotlin coroutines
Roman Elizarov
 
Modern app development with Jetpack Compose.pptx
Modern app development with Jetpack Compose.pptxModern app development with Jetpack Compose.pptx
Modern app development with Jetpack Compose.pptx
Md Shamsul Arafin Mahtab
 
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
JassGroup TICS
 
Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in Kotlin
Alexey Soshin
 
Introduction to kotlin coroutines
Introduction to kotlin coroutinesIntroduction to kotlin coroutines
Introduction to kotlin coroutines
NAVER Engineering
 
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Derek Lee
 
Kotlin Coroutines and Android sitting in a tree
Kotlin Coroutines and Android sitting in a treeKotlin Coroutines and Android sitting in a tree
Kotlin Coroutines and Android sitting in a tree
Kai Koenig
 
KMM survival guide: how to tackle struggles between Kotlin and Swift
KMM survival guide: how to tackle struggles between Kotlin and SwiftKMM survival guide: how to tackle struggles between Kotlin and Swift
KMM survival guide: how to tackle struggles between Kotlin and Swift
Commit University
 
Android kotlin coroutines
Android kotlin coroutinesAndroid kotlin coroutines
Android kotlin coroutines
Bipin Vayalu
 
The Kotlin Programming Language
The Kotlin Programming LanguageThe Kotlin Programming Language
The Kotlin Programming Language
intelliyole
 
Introducing Async/Await
Introducing Async/AwaitIntroducing Async/Await
Introducing Async/Await
Valeri Karpov
 
Introduction to kotlin
Introduction to kotlinIntroduction to kotlin
Introduction to kotlin
Shaul Rosenzwieg
 
A quick and fast intro to Kotlin
A quick and fast intro to Kotlin A quick and fast intro to Kotlin
A quick and fast intro to Kotlin
XPeppers
 
Spring Boot & Actuators
Spring Boot & ActuatorsSpring Boot & Actuators
Spring Boot & Actuators
VMware Tanzu
 

What's hot (20)

Jetpack Compose beginner.pdf
Jetpack Compose beginner.pdfJetpack Compose beginner.pdf
Jetpack Compose beginner.pdf
 
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesThreading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
 
Jetpack Compose - Android’s modern toolkit for building native UI
Jetpack Compose - Android’s modern toolkit for building native UIJetpack Compose - Android’s modern toolkit for building native UI
Jetpack Compose - Android’s modern toolkit for building native UI
 
Kotlin Basics & Introduction to Jetpack Compose.pptx
Kotlin Basics & Introduction to Jetpack Compose.pptxKotlin Basics & Introduction to Jetpack Compose.pptx
Kotlin Basics & Introduction to Jetpack Compose.pptx
 
Jetpack Compose.pptx
Jetpack Compose.pptxJetpack Compose.pptx
Jetpack Compose.pptx
 
Jetpack Compose - A Lightning Tour
Jetpack Compose - A Lightning TourJetpack Compose - A Lightning Tour
Jetpack Compose - A Lightning Tour
 
Introduction to Kotlin coroutines
Introduction to Kotlin coroutinesIntroduction to Kotlin coroutines
Introduction to Kotlin coroutines
 
Modern app development with Jetpack Compose.pptx
Modern app development with Jetpack Compose.pptxModern app development with Jetpack Compose.pptx
Modern app development with Jetpack Compose.pptx
 
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
 
Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in Kotlin
 
Introduction to kotlin coroutines
Introduction to kotlin coroutinesIntroduction to kotlin coroutines
Introduction to kotlin coroutines
 
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
Why I ❤️ Kotlin Multiplatform (and want YOU to also ❤️ Kotlin Multiplatform)
 
Kotlin Coroutines and Android sitting in a tree
Kotlin Coroutines and Android sitting in a treeKotlin Coroutines and Android sitting in a tree
Kotlin Coroutines and Android sitting in a tree
 
KMM survival guide: how to tackle struggles between Kotlin and Swift
KMM survival guide: how to tackle struggles between Kotlin and SwiftKMM survival guide: how to tackle struggles between Kotlin and Swift
KMM survival guide: how to tackle struggles between Kotlin and Swift
 
Android kotlin coroutines
Android kotlin coroutinesAndroid kotlin coroutines
Android kotlin coroutines
 
The Kotlin Programming Language
The Kotlin Programming LanguageThe Kotlin Programming Language
The Kotlin Programming Language
 
Introducing Async/Await
Introducing Async/AwaitIntroducing Async/Await
Introducing Async/Await
 
Introduction to kotlin
Introduction to kotlinIntroduction to kotlin
Introduction to kotlin
 
A quick and fast intro to Kotlin
A quick and fast intro to Kotlin A quick and fast intro to Kotlin
A quick and fast intro to Kotlin
 
Spring Boot & Actuators
Spring Boot & ActuatorsSpring Boot & Actuators
Spring Boot & Actuators
 

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

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
 
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
 
PyData NYC 2019
PyData NYC 2019PyData NYC 2019
PyData NYC 2019
Li Jin
 

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
 
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
 
shiny.pdf
shiny.pdfshiny.pdf
shiny.pdf
 
Jetpack Compose - Hands-on February 2020
Jetpack Compose - Hands-on February 2020Jetpack Compose - Hands-on February 2020
Jetpack Compose - Hands-on February 2020
 
Simple React Todo List
Simple React Todo ListSimple React Todo List
Simple React Todo List
 
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
 
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
 
20230721_OKC_Meetup_MuleSoft.pptx
20230721_OKC_Meetup_MuleSoft.pptx20230721_OKC_Meetup_MuleSoft.pptx
20230721_OKC_Meetup_MuleSoft.pptx
 
Quick Intro to Android Development
Quick Intro to Android DevelopmentQuick Intro to Android Development
Quick Intro to Android Development
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
 
Qt Workshop
Qt WorkshopQt Workshop
Qt Workshop
 
Writing DSL with Applicative Functors
Writing DSL with Applicative FunctorsWriting DSL with Applicative Functors
Writing DSL with Applicative Functors
 
Android L01 - Warm Up
Android L01 - Warm UpAndroid L01 - Warm Up
Android L01 - Warm Up
 
Software Language Design & Engineering
Software Language Design & EngineeringSoftware Language Design & Engineering
Software Language Design & Engineering
 
mobl
moblmobl
mobl
 
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
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
 
Refactoring
RefactoringRefactoring
Refactoring
 
PyData NYC 2019
PyData NYC 2019PyData NYC 2019
PyData NYC 2019
 

More from 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
 
Persistência de Dados no SQLite com Room
Persistência de Dados no SQLite com RoomPersistência de Dados no SQLite com Room
Persistência de Dados no SQLite com Room
Nelson Glauber Leal
 

More from Nelson Glauber Leal (20)

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
 
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
 
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
 
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)
 
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
 
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
 
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
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
 
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
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard Library
 
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
 
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
 
Persisting Data on SQLite using Room
Persisting Data on SQLite using RoomPersisting Data on SQLite using Room
Persisting Data on SQLite using Room
 
Arquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackArquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com Jetpack
 
Desenvolvimento Moderno de Aplicativos Android
Desenvolvimento Moderno de Aplicativos AndroidDesenvolvimento Moderno de Aplicativos Android
Desenvolvimento Moderno de Aplicativos Android
 
Desenvolvimento Moderno de aplicativos Android
Desenvolvimento Moderno de aplicativos AndroidDesenvolvimento Moderno de aplicativos Android
Desenvolvimento Moderno de aplicativos Android
 
Turbinando o desenvolvimento Android com Kotlin
Turbinando o desenvolvimento Android com KotlinTurbinando o desenvolvimento Android com Kotlin
Turbinando o desenvolvimento Android com Kotlin
 
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
 
Persistência de Dados no SQLite com Room
Persistência de Dados no SQLite com RoomPersistência de Dados no SQLite com Room
Persistência de Dados no SQLite com Room
 

Recently uploaded

Prada Group Reports Strong Growth in First Quarter …
Prada Group Reports Strong Growth in First Quarter …Prada Group Reports Strong Growth in First Quarter …
Prada Group Reports Strong Growth in First Quarter …
908dutch
 
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in CityGirls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
neshakor5152
 
ThaiPy meetup - Indexes and Django
ThaiPy meetup - Indexes and DjangoThaiPy meetup - Indexes and Django
ThaiPy meetup - Indexes and Django
akshesh doshi
 
VVIP Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 i...
VVIP Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 i...VVIP Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 i...
VVIP Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 i...
jealousviolet
 
🚂🚘 Premium Girls Call Ranchi 🛵🚡000XX00000 💃 Choose Best And Top Girl Service...
🚂🚘 Premium Girls Call Ranchi  🛵🚡000XX00000 💃 Choose Best And Top Girl Service...🚂🚘 Premium Girls Call Ranchi  🛵🚡000XX00000 💃 Choose Best And Top Girl Service...
🚂🚘 Premium Girls Call Ranchi 🛵🚡000XX00000 💃 Choose Best And Top Girl Service...
bahubalikumar09988
 
Celebrity Girls Call Mumbai 9920725232 Unlimited Short Providing Girls Servic...
Celebrity Girls Call Mumbai 9920725232 Unlimited Short Providing Girls Servic...Celebrity Girls Call Mumbai 9920725232 Unlimited Short Providing Girls Servic...
Celebrity Girls Call Mumbai 9920725232 Unlimited Short Providing Girls Servic...
45unexpected
 
ERP Software Solutions Provider in Coimbatore
ERP Software Solutions Provider in CoimbatoreERP Software Solutions Provider in Coimbatore
ERP Software Solutions Provider in Coimbatore
Nextskill Technologies
 
Artificial intelligence in customer services or chatbots
Artificial intelligence  in customer services or chatbotsArtificial intelligence  in customer services or chatbots
Artificial intelligence in customer services or chatbots
kayash1656
 
TEQnation 2024: Sustainable Software: May the Green Code Be with You
TEQnation 2024: Sustainable Software: May the Green Code Be with YouTEQnation 2024: Sustainable Software: May the Green Code Be with You
TEQnation 2024: Sustainable Software: May the Green Code Be with You
marcofolio
 
HIRE A HACKER FOR CHEATING HUSBAND/WIFE)
HIRE A HACKER FOR CHEATING HUSBAND/WIFE)HIRE A HACKER FOR CHEATING HUSBAND/WIFE)
HIRE A HACKER FOR CHEATING HUSBAND/WIFE)
josephinedrea942
 
High Girls Call Chennai 000XX00000 Provide Best And Top Girl Service And No1 ...
High Girls Call Chennai 000XX00000 Provide Best And Top Girl Service And No1 ...High Girls Call Chennai 000XX00000 Provide Best And Top Girl Service And No1 ...
High Girls Call Chennai 000XX00000 Provide Best And Top Girl Service And No1 ...
singhlata50dh
 
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
norina2645
 
A Step-by-Step Guide to Selecting the Right Automated Software Testing Tools.pdf
A Step-by-Step Guide to Selecting the Right Automated Software Testing Tools.pdfA Step-by-Step Guide to Selecting the Right Automated Software Testing Tools.pdf
A Step-by-Step Guide to Selecting the Right Automated Software Testing Tools.pdf
kalichargn70th171
 
Amadeus Travel API, Amadeus Booking API, Amadeus GDS
Amadeus Travel API, Amadeus Booking API, Amadeus GDSAmadeus Travel API, Amadeus Booking API, Amadeus GDS
Amadeus Travel API, Amadeus Booking API, Amadeus GDS
aadhiyaeliza
 
Girls Call Jogeshwari 9967584737 Provide Best And Top Girl Service And No1 in...
Girls Call Jogeshwari 9967584737 Provide Best And Top Girl Service And No1 in...Girls Call Jogeshwari 9967584737 Provide Best And Top Girl Service And No1 in...
Girls Call Jogeshwari 9967584737 Provide Best And Top Girl Service And No1 in...
simran hot girls
 
GT degree offer diploma Transcript
GT degree offer diploma TranscriptGT degree offer diploma Transcript
GT degree offer diploma Transcript
attueb
 
Il Data Streaming per un’AI real-time di nuova generazione
Il Data Streaming per un’AI real-time di nuova generazioneIl Data Streaming per un’AI real-time di nuova generazione
Il Data Streaming per un’AI real-time di nuova generazione
confluent
 
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdfAI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
Daniel Zivkovic
 
當測試開始左移
當測試開始左移當測試開始左移
當測試開始左移
Jersey (CHE-PING) Su
 
Top Chinese Government-backed APT Groups
Top Chinese Government-backed APT GroupsTop Chinese Government-backed APT Groups
Top Chinese Government-backed APT Groups
SOCRadar
 

Recently uploaded (20)

Prada Group Reports Strong Growth in First Quarter …
Prada Group Reports Strong Growth in First Quarter …Prada Group Reports Strong Growth in First Quarter …
Prada Group Reports Strong Growth in First Quarter …
 
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in CityGirls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
 
ThaiPy meetup - Indexes and Django
ThaiPy meetup - Indexes and DjangoThaiPy meetup - Indexes and Django
ThaiPy meetup - Indexes and Django
 
VVIP Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 i...
VVIP Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 i...VVIP Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 i...
VVIP Girls Call Mumbai 9910780858 Provide Best And Top Girl Service And No1 i...
 
🚂🚘 Premium Girls Call Ranchi 🛵🚡000XX00000 💃 Choose Best And Top Girl Service...
🚂🚘 Premium Girls Call Ranchi  🛵🚡000XX00000 💃 Choose Best And Top Girl Service...🚂🚘 Premium Girls Call Ranchi  🛵🚡000XX00000 💃 Choose Best And Top Girl Service...
🚂🚘 Premium Girls Call Ranchi 🛵🚡000XX00000 💃 Choose Best And Top Girl Service...
 
Celebrity Girls Call Mumbai 9920725232 Unlimited Short Providing Girls Servic...
Celebrity Girls Call Mumbai 9920725232 Unlimited Short Providing Girls Servic...Celebrity Girls Call Mumbai 9920725232 Unlimited Short Providing Girls Servic...
Celebrity Girls Call Mumbai 9920725232 Unlimited Short Providing Girls Servic...
 
ERP Software Solutions Provider in Coimbatore
ERP Software Solutions Provider in CoimbatoreERP Software Solutions Provider in Coimbatore
ERP Software Solutions Provider in Coimbatore
 
Artificial intelligence in customer services or chatbots
Artificial intelligence  in customer services or chatbotsArtificial intelligence  in customer services or chatbots
Artificial intelligence in customer services or chatbots
 
TEQnation 2024: Sustainable Software: May the Green Code Be with You
TEQnation 2024: Sustainable Software: May the Green Code Be with YouTEQnation 2024: Sustainable Software: May the Green Code Be with You
TEQnation 2024: Sustainable Software: May the Green Code Be with You
 
HIRE A HACKER FOR CHEATING HUSBAND/WIFE)
HIRE A HACKER FOR CHEATING HUSBAND/WIFE)HIRE A HACKER FOR CHEATING HUSBAND/WIFE)
HIRE A HACKER FOR CHEATING HUSBAND/WIFE)
 
High Girls Call Chennai 000XX00000 Provide Best And Top Girl Service And No1 ...
High Girls Call Chennai 000XX00000 Provide Best And Top Girl Service And No1 ...High Girls Call Chennai 000XX00000 Provide Best And Top Girl Service And No1 ...
High Girls Call Chennai 000XX00000 Provide Best And Top Girl Service And No1 ...
 
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
 
A Step-by-Step Guide to Selecting the Right Automated Software Testing Tools.pdf
A Step-by-Step Guide to Selecting the Right Automated Software Testing Tools.pdfA Step-by-Step Guide to Selecting the Right Automated Software Testing Tools.pdf
A Step-by-Step Guide to Selecting the Right Automated Software Testing Tools.pdf
 
Amadeus Travel API, Amadeus Booking API, Amadeus GDS
Amadeus Travel API, Amadeus Booking API, Amadeus GDSAmadeus Travel API, Amadeus Booking API, Amadeus GDS
Amadeus Travel API, Amadeus Booking API, Amadeus GDS
 
Girls Call Jogeshwari 9967584737 Provide Best And Top Girl Service And No1 in...
Girls Call Jogeshwari 9967584737 Provide Best And Top Girl Service And No1 in...Girls Call Jogeshwari 9967584737 Provide Best And Top Girl Service And No1 in...
Girls Call Jogeshwari 9967584737 Provide Best And Top Girl Service And No1 in...
 
GT degree offer diploma Transcript
GT degree offer diploma TranscriptGT degree offer diploma Transcript
GT degree offer diploma Transcript
 
Il Data Streaming per un’AI real-time di nuova generazione
Il Data Streaming per un’AI real-time di nuova generazioneIl Data Streaming per un’AI real-time di nuova generazione
Il Data Streaming per un’AI real-time di nuova generazione
 
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdfAI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
 
當測試開始左移
當測試開始左移當測試開始左移
當測試開始左移
 
Top Chinese Government-backed APT Groups
Top Chinese Government-backed APT GroupsTop Chinese Government-backed APT Groups
Top Chinese Government-backed APT Groups
 

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