Slide 1

Slide 1 text

Adopting Jetpack Compose in Your Existing Project Bangkok Somkiat Khitwongwattana Android GDE

Slide 2

Slide 2 text

Less code Intuitive Accelerate development Powerful Why Jetpack Compose?

Slide 3

Slide 3 text

Less code Intuitive Accelerate development Powerful Why Jetpack Compose?

Slide 4

Slide 4 text

Build an UI with XML

Slide 5

Slide 5 text

class MainActivity : AppCompatActivity() { private val binding: ActivityMainBinding by lazy { ... } override fun onCreate(savedInstanceState: Bundle?) { ... binding.button.setOnClickListener { // Do something } } } View binding

Slide 6

Slide 6 text

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { ... setContent { ComposeAppTheme { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { Button(onClick = { // Do something }) { Text(text = "Button") Build an UI with Jetpack Compose

Slide 7

Slide 7 text

setContent { ComposeAppTheme { Box( ... ) { Button(onClick = { // Do something }) { Text(text = "Button") } } } } Build an UI with Jetpack Compose

Slide 8

Slide 8 text

Less code Intuitive Accelerate development Powerful Why Jetpack Compose?

Slide 9

Slide 9 text

Readability

Slide 10

Slide 10 text

Readability

Slide 11

Slide 11 text

val button1: Button = /* ... */ val button2: Button = /* ... */ val button3: Button = /* ... */ val isPurchased = false button1.visibility = if (isPurchased) View.GONE else View.VISIBLE button2.visibility = if (isPurchased) View.GONE else View.VISIBLE button3.visibility = if (isPurchased) View.VISIBLE else View.GONE Readability

Slide 12

Slide 12 text

val button1: Button = /* ... */ val button2: Button = /* ... */ val button3: Button = /* ... */ val isPurchased = true button1.visibility = if (isPurchased) View.GONE else View.VISIBLE button2.visibility = if (isPurchased) View.GONE else View.VISIBLE button3.visibility = if (isPurchased) View.VISIBLE else View.GONE Readability

Slide 13

Slide 13 text

val isPurchased: Boolean = ... Box( ... ) { if (isPurchased) { Button( ... ) { Text("Button 3") } } else { Row( ... ) { Button( ... ) { Text("Button 1") } Spacer( ... ) Button( ... ) { Text("Button 2") } } } } Readability

Slide 14

Slide 14 text

Less code Intuitive Accelerate development Powerful Why Jetpack Compose?

Slide 15

Slide 15 text

data class Item( val id: String, val text: String, ) Dynamic list UI

Slide 16

Slide 16 text

Dynamic list UI

Slide 17

Slide 17 text

Dynamic list UI

Slide 18

Slide 18 text

class ItemViewHolder( private val binding: LayoutListItemBinding ) : ViewHolder(binding.root) { fun bind(item: Item) { binding.textViewItem.text = item.text } } Dynamic list UI

Slide 19

Slide 19 text

class ListAdapter(private val items: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( LayoutListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) override fun getItemCount(): Int = items.size override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { items.getOrNull(position)?.let { holder.bind(it.text) } } } Dynamic list UI

Slide 20

Slide 20 text

val items: List = /* ... */ val context: Context = /* ... */ with(binding.recyclerView) { layoutManager = LinearLayoutManager( context, LinearLayoutManager.VERTICAL, false, ) adapter = ListAdapter(items) } Dynamic list UI

Slide 21

Slide 21 text

@Composable fun ListContent(items: List) { LazyColumn(modifier = Modifier.fillMaxSize()) { items( items = items, key = { item -> item.id } ) { item -> Text( modifier = Modifier.fillMaxWidth(), text = item.text, ) } } Dynamic list UI

Slide 22

Slide 22 text

Less code Intuitive Accelerate development Powerful Why Jetpack Compose?

Slide 23

Slide 23 text

var activated by remember { mutableStateOf(true) } val contentColor by animateColorAsState( ... ) val containerColor by animateColorAsState( ... ) val scale by animateFloatAsState( ... ) Button( modifier = Modifier.scale(scale), colors = ButtonDefaults.buttonColors( containerColor = containerColor, contentColor = contentColor, ), onClick = { activated = !activated }, ) { Text(if (activated) "Activated" else "Inactivated") } Animation

Slide 24

Slide 24 text

Jetpack Compose 1.7.5 is now available

Slide 25

Slide 25 text

Declarative programming paradigm Declarative paradigm shift Dynamic content Recomposition Thinking in Compose

Slide 26

Slide 26 text

Declarative paradigm shift ● Relatively stateless and don't expose setter or getter functions ● Update the UI by passing the different arguments ● Unidirectional data flow

Slide 27

Slide 27 text

State flows down

Slide 28

Slide 28 text

Event flows up

Slide 29

Slide 29 text

@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit) { Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up

Slide 30

Slide 30 text

@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit) { Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up

Slide 31

Slide 31 text

@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit) { Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up

Slide 32

Slide 32 text

@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit) { Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up

Slide 33

Slide 33 text

@Composable fun TopThreeItems(items: List) { Column { HeaderContent() items.take(3).forEach { item -> ItemContent(item) } } } Dynamic content

Slide 34

Slide 34 text

@Composable fun TopThreeItems(items: List) { Column { HeaderContent() items.take(3).forEach { item -> ItemContent(item) } } } Recomposition New value update

Slide 35

Slide 35 text

@Composable fun TopThreeItems(items: List) { Column { HeaderContent() items.take(3).forEach { item -> ItemContent(item) } } } Recomposition Skip

Slide 36

Slide 36 text

@Composable fun TopThreeItems(items: List) { Column { HeaderContent() items.take(3).forEach { item -> ItemContent(item) } } } Recomposition Recomposition

Slide 37

Slide 37 text

Get started Jetpack Compose for Android Developers https://developer.android.com/courses/jetpack-compose/course

Slide 38

Slide 38 text

Get started

Slide 39

Slide 39 text

Get started https://github.com/android/nowinandroid

Slide 40

Slide 40 text

Get started https://github.com/chrisbanes/tivi

Slide 41

Slide 41 text

Why should you adopt Jetpack Compose?

Slide 42

Slide 42 text

Chance for the big improvement Animate with a few lines of code Powerful preview in Android Studio Testable without view ID Friendly for sub-component creation Benefit for teams

Slide 43

Slide 43 text

Powerful preview in Android Studio

Slide 44

Slide 44 text

@Composable fun MainScreen( ... ) { Column( ... ) { Profile( modifier = Modifier.testTag("profile"), ... ) ... } } Testable without view ID

Slide 45

Slide 45 text

Testable without view ID

Slide 46

Slide 46 text

Testable without view ID

Slide 47

Slide 47 text

Testable without view ID

Slide 48

Slide 48 text

Friendly for sub-component creation

Slide 49

Slide 49 text

Fragment Friendly for sub-component creation Fragment Fragment Fragment ViewModel ViewModel ViewModel ViewModel ViewModel

Slide 50

Slide 50 text

View Friendly for sub-component creation View View View ViewModel

Slide 51

Slide 51 text

Compose Friendly for sub-component creation Compose Compose Compose ViewModel ViewModel ViewModel ViewModel ViewModel

Slide 52

Slide 52 text

@Composable fun FirstSection(viewModel: FirstViewModel = koinViewModel()) { ... } @Composable fun SecondSection(viewModel: SecondViewModel = koinViewModel()) { ... } @Composable fun ThirdSection(viewModel: ThirdViewModel = koinViewModel()) { ... } @Composable fun FourthSection(viewModel: FourthViewModel = koinViewModel()) { ... } Friendly for sub-component creation

Slide 53

Slide 53 text

@Composable fun MainScreen(viewModel: MainViewModel = koinViewModel()) { Column { FirstSection() SecondSection() ThirdSection() FourthSection() } } Friendly for sub-component creation

Slide 54

Slide 54 text

@Composable fun MainScreen(viewModel: MainViewModel = koinViewModel()) { LazyColumn { item(key = "first") { FirstSection() } item(key = "second") { SecondSection() } item(key = "third") { ThirdSection() } item(key = "fourth") { FourthSection() } } } Friendly for sub-component creation

Slide 55

Slide 55 text

Activity Result Requesting Runtime Permissions ViewModel Data Streams Asynchronous Operations Availability

Slide 56

Slide 56 text

Screen Navigation Dependency Injection Google Maps Navigation Drawer Bottom Sheet Availability

Slide 57

Slide 57 text

Nested Scrolling Material 3 Components Dialog Snackbar Image Loading Availability

Slide 58

Slide 58 text

WindowInsets Handling screen size changed Availability UI Testing

Slide 59

Slide 59 text

Compose preview generation with Gemini

Slide 60

Slide 60 text

Things to know Debug build performance Profile installer for better performance No cost from XML layout inflation Efficiently handle deep UI trees Smart recompositions

Slide 61

Slide 61 text

Sunflower app https://github.com/android/sunflower

Slide 62

Slide 62 text

APK Size Based on information from the Sunflower app Views only 2,252 KB 3,034 KB 2,966 KB Mixed Views and Compose Compose-only

Slide 63

Slide 63 text

Build Time Based on information from the Sunflower app Views only 299.47 ms 399.09 ms 342.16 ms Mixed Views and Compose Compose-only

Slide 64

Slide 64 text

Tivi app https://github.com/chrisbanes/tivi

Slide 65

Slide 65 text

Based on information from the Tivi app v1.0.0-rc01 APK Size Views only 4.14 MB 2.71 MB 2.32 MB Fragment and Compose Compose-only

Slide 66

Slide 66 text

Method Count Based on information from the Tivi app v1.0.0-rc01 Views only 40,029 35,165 23,689 Fragment and Compose Compose-only

Slide 67

Slide 67 text

In the other side Different knowledge Few performance overhead based on complexity Fewer third-party libraries compared to the View Limited legacy support

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

Approach to integrating Jetpack Compose New Features Simple Component or Screen Redesign Features

Slide 70

Slide 70 text

Fragment Layout Layout View Layout View View Compose

Slide 71

Slide 71 text

Activity Activity Activity Activity Activity View View View Compose

Slide 72

Slide 72 text

Activity Fragment Fragment Fragment Fragment View View View Compose

Slide 73

Slide 73 text

Activity Activity Activity Fragment Navigation View Compose Fragment View Compose Compose

Slide 74

Slide 74 text

Activity Activity Activity Fragment Navigation View Compose Fragment View Compose Compose

Slide 75

Slide 75 text

Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose Compose

Slide 76

Slide 76 text

Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose Compose

Slide 77

Slide 77 text

Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose Compose

Slide 78

Slide 78 text

Codebase Navigation Existing UI Component & Design System Analytics Testing Things to think about

Slide 79

Slide 79 text

Compose in Activity Fragment View Compose View in

Slide 80

Slide 80 text

class MainActivity: ComponentActivity() { override fun onCreate( ... ) { ... setContent { // In Compose world } } } Compose in Activity

Slide 81

Slide 81 text

class HomeFragment : Fragment() { override fun onCreateView( ... ): View { return ComposeView(requireContext()).apply { setViewCompositionStrategy( ... ) setContent { // In Compose world } } } } Compose in Fragment

Slide 82

Slide 82 text

Compose in View ...

Slide 83

Slide 83 text

val composeView = findViewById(R.id.composeView) composeView.apply { setViewCompositionStrategy( ... ) setContent { // In Compose world } } Compose in View

Slide 84

Slide 84 text

@Composable fun AndroidCustomView(title: String, onClick: () -> Unit) { AndroidView( factory = { context -> CustomView(context).apply { setOnClickListener(onClick) } }, update = { view -> view.setTitle(title) } ) View in Compose

Slide 85

Slide 85 text

Codebase preparation for seamless feature development Start with small, incremental changes Learning resources for future adopters Negotiate with the product's decision-maker To make it happen

Slide 86

Slide 86 text

API Maturity Learning resources Libraries & tools Backward compatibility Multiplatform supported Jetpack Compose is now production-ready Performance Wide Adoption

Slide 87

Slide 87 text

"Any code you built with views will become technical debt" Florina Muntenescu

Slide 88

Slide 88 text

Thank you Bangkok