Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Discovering Jetpack Compose

B6bf611a9e4f70a8455cb175339e0587?s=47 Gerard
February 28, 2020

Discovering Jetpack Compose

Jetpack Compose will be the new declarative UI to build Android apps. Based on this new paradigm, let's see how we can use it with some simple samples!

B6bf611a9e4f70a8455cb175339e0587?s=128

Gerard

February 28, 2020
Tweet

Transcript

  1. @GerardPaligot DISCOVERING JETPACK COMPOSE

  2. ⚠ DISCLAIMER

  3. WHAT’S JETPACK COMPOSE? 100% Kotlin Compiler Open Source Declarative UI

    toolkit Android Team
  4. THINK DIFFERENT.

  5. None
  6. MovieMetadata Rating TopAppBar Poster PosterNoted MovieItem

  7. None
  8. None
  9. None
  10. None
  11. App MovieHome Home Section Home Section Home Section Data Events

  12. App MovieHome Home Section Home Section Home Section Data Events

  13. PLACING ELEMENTS

  14. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { }
  15. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Text( text = title ) }
  16. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Text( text = title, style = MaterialTheme.typography.h6 ) }
  17. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Text( text = title, style = MaterialTheme.typography.h6, maxLines = 2, overflow = TextOverflow.Ellipsis ) }
  18. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text( text = title, style = MaterialTheme.typography.h6, maxLines = 2, overflow = TextOverflow.Ellipsis ) } }
  19. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) } }
  20. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) genres.forEach { Tag(text = it) } } }
  21. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) Row { genres.forEach { Tag(text = it) } } } }
  22. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) Row(modifier = Modifier.padding(top = 5.dp)) { genres.forEach { Tag(text = it) } } } }
  23. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) Row(modifier = Modifier.padding(top = 5.dp)) { ... } } }
  24. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) Row(modifier = Modifier.padding(top = 5.dp)) { ... } val year = releaseDate.getCalendar().get(Calendar.YEAR) val time = "${runtime / 60}h ${runtime % 60}min" Text( text ="$year - $time", modifier = Modifier.padding(top = 5.dp) ) } }
  25. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) Row(modifier = Modifier.padding(top = 5.dp)) { ... } val year = releaseDate.getCalendar().get(Calendar.YEAR) val time = "${runtime / 60}h ${runtime % 60}min" Text( text = if (runtime != 0) "$year - $time" else "$year", modifier = Modifier.padding(top = 5.dp) ) } }
  26. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) Row(modifier = Modifier.padding(top = 5.dp)) { ... } ProvideEmphasis( emphasis = AmbientEmphasisLevels.current.medium ) { val year = releaseDate.getCalendar().get(Calendar.YEAR) val time = "${runtime / 60}h ${runtime % 60}min" Text( text = if (runtime != 0) "$year - $time" else "$year", modifier = Modifier.padding(top = 5.dp) ) } } }
  27. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int ) { Column { Text(...) Row(modifier = Modifier.padding(top = 5.dp)) { ... } ProvideEmphasis( emphasis = AmbientEmphasisLevels.current.medium ) { ... } } }
  28. @Composable fun MovieMetadata( title: String, genres: List<String>, releaseDate: String, runtime:

    Int, modifier: Modifier = Modifier ) { Column(modifier = modifier) { Text(...) Row(modifier = Modifier.padding(top = 5.dp)) { ... } ProvideEmphasis( emphasis = AmbientEmphasisLevels.current.medium ) { ... } } }
  29. LET’S CODE A LIST

  30. @Composable fun PosterNoted( posterUrl: String, voteAverage: Int, ratingSize: Dp, ratingAlignment:

    Alignment, onClick: () -> Unit = {} )
  31. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { }
  32. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { PosterNoted( posterUrl = movie.pictureUrl, voteAverage = movie.percentage, ratingSize = 50.dp, ratingAlignment = Alignment.TopEnd ) }
  33. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { Box( modifier = Modifier .width(130.dp) .aspectRatio(0.7f) ) { PosterNoted( posterUrl = movie.pictureUrl, voteAverage = movie.percentage, ratingSize = 50.dp, ratingAlignment = Alignment.TopEnd ) } }
  34. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { Box( modifier = Modifier .width(130.dp) .aspectRatio(0.7f) ) { … } }
  35. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { Box(modifier = modifier) { Box( modifier = Modifier .width(130.dp) .aspectRatio(0.7f) ) { ... } } }
  36. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { val posterWidth = 130 val height = (posterWidth / 0.7f).roundToInt() Box(modifier = modifier) { Surface( modifier = Modifier .fillMaxWidth() .preferredHeight((height - 30).dp) ) { } Box( modifier = Modifier .width(posterWidth.dp) .aspectRatio(0.7f) ) { ... } } }
  37. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { val posterWidth = 130 val height = (posterWidth / 0.7f).roundToInt() Box(modifier = modifier) { Surface( modifier = Modifier .padding(top = 60.dp) .fillMaxWidth() .preferredHeight((height - 30).dp) ) { } Box( modifier = Modifier .width(posterWidth.dp) .aspectRatio(0.7f) ) { ... } } }
  38. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { val posterWidth = 130 val height = (posterWidth / 0.7f).roundToInt() Box(modifier = modifier) { Surface( modifier = Modifier .padding(top = 60.dp) .fillMaxWidth() .preferredHeight((height - 30).dp) .clickable(onClick = { onClick(movie) }) ) { } Box( modifier = Modifier .width(posterWidth.dp) .aspectRatio(0.7f) ) { ... } } }
  39. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { val posterWidth = 130 val height = (posterWidth / 0.7f).roundToInt() Box(modifier = modifier) { Surface( modifier = Modifier .padding(top = 60.dp) .fillMaxWidth() .preferredHeight((height - 30).dp) .clickable(onClick = { onClick(movie) }), shape = RoundedCornerShape(8.dp) ) { } Box( modifier = Modifier .width(posterWidth.dp) .aspectRatio(0.7f) ) { ... } } }
  40. @Composable fun MovieItem( movie: Movie, modifier: Modifier = Modifier, onClick:

    (movie: Movie) -> Unit ) { val posterWidth = 130 val height = (posterWidth / 0.7f).roundToInt() Box(modifier = modifier) { Surface( modifier = Modifier .padding(top = 60.dp) .fillMaxWidth() .preferredHeight((height - 30).dp) .clickable(onClick = { onClick(movie) }), shape = RoundedCornerShape(8.dp), elevation = 5.dp ) { } Box( modifier = Modifier .width(posterWidth.dp) .aspectRatio(0.7f) ) { ... } } }
  41. val posterWidth = 130 val height = (posterWidth / 0.7f).roundToInt()

    Box(modifier = modifier) { Surface( modifier = Modifier .padding(top = 60.dp) .fillMaxWidth() .preferredHeight((height - 30).dp) .clickable(onClick = { onClick(movie) }), shape = RoundedCornerShape(8.dp), elevation = 5.dp ) { } Box( modifier = Modifier .width(posterWidth.dp) .aspectRatio(0.7f) ) { ... } } }
  42. val posterWidth = 130 val height = (posterWidth / 0.7f).roundToInt()

    Box(modifier = modifier) { Surface( modifier = Modifier .padding(top = 60.dp) .fillMaxWidth() .preferredHeight((height - 30).dp) .clickable(onClick = { onClick(movie) }), shape = RoundedCornerShape(8.dp), elevation = 5.dp ) { } Box( modifier = Modifier .padding(start = 30.dp) .width(posterWidth.dp) .aspectRatio(0.7f) ) { ... } } }
  43. val posterWidth = 130 val height = (posterWidth / 0.7f).roundToInt()

    Box(modifier = modifier) { Surface( modifier = Modifier .padding(top = 60.dp) .fillMaxWidth() .preferredHeight((height - 30).dp) .clickable(onClick = { onClick(movie) }), shape = RoundedCornerShape(8.dp), elevation = 5.dp ) { MovieMetadata( title = movie.title, genres = movie.genres, releaseDate = movie.releaseDate, runtime = movie.runtime, modifier = Modifier.padding(start = (posterWidth + 15).dp) .align(alignment = Alignment.CenterStart) ) } Box( modifier = Modifier .padding(start = 30.dp) .width(posterWidth.dp) .aspectRatio(0.7f) ) { ... } }
  44. @Composable fun MovieList( title: String, movies: List<Movie>, onClick: (movie: Movie)

    -> Unit ) { }
  45. @Composable fun MovieList( title: String, movies: List<Movie>, onClick: (movie: Movie)

    -> Unit ) { ScrollableColumn { movies.forEach { MovieItem( movie = it, modifier = Modifier .padding(start = 10.dp, top = 10.dp, end = 10.dp), onClick = onClick ) } } }
  46. @Composable fun MovieList( title: String, movies: List<Movie>, onClick: (movie: Movie)

    -> Unit ) { LazyColumnFor(items = movies) { MovieItem( movie = it, modifier = Modifier .padding(start = 10.dp, top = 10.dp, end = 10.dp), onClick = onClick ) } }
  47. @Composable fun MovieList( title: String, movies: List<Movie>, onClick: (movie: Movie)

    -> Unit ) { MovieScaffold(title = title) { LazyColumnFor(items = movies) { MovieItem( movie = it, modifier = Modifier .padding(start = 10.dp, top = 10.dp, end = 10.dp), onClick = onClick ) } } }
  48. @Composable fun Scaffold( modifier: Modifier = Modifier, scaffoldState: ScaffoldState =

    rememberScaffoldState(), topBar: @Composable () -> Unit = emptyContent(), bottomBar: @Composable () -> Unit = emptyContent(), snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) }, floatingActionButton: @Composable () -> Unit = emptyContent(), floatingActionButtonPosition: FabPosition = FabPosition.End, isFloatingActionButtonDocked: Boolean = false, drawerContent: @Composable (ColumnScope.() -> Unit)? = null, drawerGesturesEnabled: Boolean = true, drawerShape: Shape = MaterialTheme.shapes.large, drawerElevation: Dp = DrawerConstants.DefaultElevation, drawerBackgroundColor: Color = MaterialTheme.colors.surface, drawerContentColor: Color = contentColorFor(drawerBackgroundColor), drawerScrimColor: Color = DrawerConstants.defaultScrimColor, backgroundColor: Color = MaterialTheme.colors.background, contentColor: Color = contentColorFor(backgroundColor), bodyContent: @Composable (PaddingValues) -> Unit )
  49. LET’S CODE A LIST WITH ACTUAL FRAMEWORK UI

  50. <?xml version="1.0" encoding="utf-8"?> <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" />

  51. <?xml version="1.0" encoding="utf-8"?> <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="80dp"> <androidx.constraintlayout.widget.ConstraintLayout

    android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/picture" android:layout_width="50dp" android:layout_height="50dp" android:layout_marginStart="20dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:srcCompat="@tools:sample/avatars" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_begin="100dp" />
  52. <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="20dp" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/price" app:layout_constraintStart_toStartOf="@+id/guideline3" app:layout_constraintTop_toTopOf="parent"> <TextView

    android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Decathlon.Subtitle2" android:textColor="@color/colorOnSurface" /> <TextView android:id="@+id/type" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Decathlon.Subtitle2" android:textColor="@color/grey" /> </LinearLayout>
  53. <TextView android:id="@+id/price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="20dp" android:textAppearance="@style/TextAppearance.Decathlon.Subtitle2" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="50$"

    /> </androidx.constraintlayout.widget.ConstraintLayout> </com.google.android.material.card.MaterialCardView>
  54. data class Activity( @DrawableRes val picture: Int, val displayName: String,

    val type: String, val price: Int ) class ActivityListAdapter(val items: MutableList<Activity>) : RecyclerView.Adapter<ActivityListAdapter.ActivityViewHolder>() { val onClick: LiveData<Activity> get() = this._onClick private val _onClick: MutableLiveData<Activity> by lazy { return@lazy MutableLiveData<Activity>() }
  55. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ActivityViewHolder = ActivityViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_activity, parent,

    false)) override fun getItemCount(): Int = items.size override fun onBindViewHolder(holder: ActivityViewHolder, position: Int) { val activity = items[position] holder.bind(activity, View.OnClickListener { _onClick.value = activity }) } fun update(items: List<Activity>) { this.items.clear() this.items.addAll(items) notifyDataSetChanged() }
  56. class ActivityViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(activity: Activity, onClickListener:

    View.OnClickListener) { itemView.setOnClickListener(onClickListener) itemView.picture.setImageDrawable(ContextCompat.getDrawable(itemView.context, activity.picture)) itemView.name.text = activity.displayName itemView.type.text = activity.type val color = if (activity.price > 0) ContextCompat.getColor(itemView.context, R.color.green) else ContextCompat.getColor(itemView.context, R.color.red) itemView.price.setTextColor(color) } } }
  57. LIGHT DARK

  58. val orange800 = Color(255, 150, 29) val orange900 = Color(255,

    118, 26) val blue400 = Color(26, 163, 255) private val LightColorPalette = lightColors( primary = orange900, primaryVariant = orange800, secondary = blue400, background = Color.White, surface = Color.White, onPrimary = Color.White, onSecondary = Color.Black, onBackground = Color.Black, onSurface = Color.Black, )
  59. val orange400 = Color(255, 205, 56) val blue800 = Color(0,

    98, 203) val blue900 = Color(0, 68, 171) val grayDark = Color(45, 47, 49) private val DarkColorPalette = darkColors( primary = blue900, primaryVariant = blue800, secondary = orange400, background = grayDark, surface = grayDark, onPrimary = Color.White, onSecondary = Color.Black, onBackground = Color.White, onSurface = Color.White )
  60. val typography = Typography( h5 = TextStyle( fontFamily = FontFamily.Default,

    fontWeight = FontWeight.W700, fontSize = 24.sp ), subtitle1 = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.W700, fontSize = 16.sp ), body1 = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 16.sp ), caption = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.W400, fontSize = 10.sp ) )
  61. val shapes = Shapes( small = RoundedCornerShape(2.dp), medium = RoundedCornerShape(4.dp),

    large = RoundedCornerShape(8.dp) )
  62. @Composable fun MaterialTheme( colors: Colors = MaterialTheme.colors, typography: Typography =

    MaterialTheme.typography, shapes: Shapes = MaterialTheme.shapes, content: @Composable () -> Unit )
  63. @Composable fun ExploringMoviesTheme( isDarkMode: Boolean = isSystemInDarkTheme(), content: @Composable() ()

    -> Unit ) { }
  64. @Composable fun ExploringMoviesTheme( isDarkMode: Boolean = isSystemInDarkTheme(), content: @Composable() ()

    -> Unit ) { MaterialTheme( content = content ) }
  65. @Composable fun ExploringMoviesTheme( isDarkMode: Boolean = isSystemInDarkTheme(), content: @Composable() ()

    -> Unit ) { MaterialTheme( colors = if (isDarkMode) DarkColorPalette else LightColorPalette, content = content ) }
  66. @Composable fun ExploringMoviesTheme( isDarkMode: Boolean = isSystemInDarkTheme(), content: @Composable() ()

    -> Unit ) { MaterialTheme( colors = if (isDarkMode) DarkColorPalette else LightColorPalette, typography = typography, content = content ) }
  67. @Composable fun ExploringMoviesTheme( isDarkMode: Boolean = isSystemInDarkTheme(), content: @Composable() ()

    -> Unit ) { MaterialTheme( colors = if (isDarkMode) DarkColorPalette else LightColorPalette, typography = typography, shapes = shapes, content = content ) }
  68. @Composable fun MovieList( title: String, movies: List<Movie>, onClick: (movie: Movie)

    -> Unit ) { MovieScaffold( title = title ){ LazyColumnFor(items = movies) { MovieItem( movie = it, modifier = Modifier .padding(start = 10.dp, top = 10.dp, end = 10.dp), onClick = onClick ) } } }
  69. @Composable fun MovieList( title: String, movies: List<Movie>, isDarkModeActive: Boolean, switchDarkMode:

    () -> Unit = {}, onClick: (movie: Movie) -> Unit ) { MovieScaffold( title = title, isDarkModeActive = isDarkModeActive, switchDarkMode = switchDarkMode ){ LazyColumnFor(items = movies) { MovieItem( movie = it, modifier = Modifier .padding(start = 10.dp, top = 10.dp, end = 10.dp), onClick = onClick ) } } }
  70. setContent { val isSystemDark = isSystemInDarkTheme() }

  71. setContent { val isSystemDark = isSystemInDarkTheme() val isDarkModeState = remember

    { mutableStateOf(isSystemDark) } }
  72. setContent { val isSystemDark = isSystemInDarkTheme() val isDarkModeState = remember

    { mutableStateOf(isSystemDark) } ExploringMoviesTheme(isDarkMode = isDarkModeState.value) { } }
  73. setContent { val isSystemDark = isSystemInDarkTheme() val isDarkModeState = remember

    { mutableStateOf(isSystemDark) } ExploringMoviesTheme(isDarkMode = isDarkModeState.value) { App( isDarkModeActive = isDarkModeState.value ) } }
  74. setContent { val isSystemDark = isSystemInDarkTheme() val isDarkModeState = remember

    { mutableStateOf(isSystemDark) } ExploringMoviesTheme(isDarkMode = isDarkModeState.value) { App( isDarkModeActive = isDarkModeState.value, switchDarkMode = { isDarkModeState.value = !isDarkModeState.value } ) } }
  75. OBSERVING DATA WITH VIEWMODEL

  76. class MovieViewModel : ViewModel() { fun getPopulars() = flow {

    ... } fun getDailyTrending() = flow { ... } fun getUpComing() = flow { ... } fun getMovieDetail(movieId: Int) = flow { ... } }
  77. ▸ LiveData.observeAsState() ▸ Flow.collectAsState() ▸ Observable.subscribeAsState()

  78. @Composable fun MyExample() { // Returns the same instance as

    long as the activity is alive, // just as if you grabbed the instance from an Activity or // Fragment val viewModel: MovieViewModel = viewModel() } @Composable fun MyExample2() { // Same instance as in MyExample val viewModel: MovieViewModel = viewModel() }
  79. @Composable fun MovieListViewModel( movieSection: MovieSection, isDarkModeActive: Boolean, switchDarkMode: () ->

    Unit = {}, onClick: (movie: Movie) -> Unit ) { MovieList( title = title, movies = movies.value, isDarkModeActive = isDarkModeActive, switchDarkMode = switchDarkMode, onClick = onClick ) }
  80. @Composable fun MovieListViewModel( movieSection: MovieSection, isDarkModeActive: Boolean, switchDarkMode: () ->

    Unit = {}, onClick: (movie: Movie) -> Unit ) { val viewModel: MovieViewModel = viewModel() MovieList( title = title, movies = movies.value, isDarkModeActive = isDarkModeActive, switchDarkMode = switchDarkMode, onClick = onClick ) }
  81. @Composable fun MovieListViewModel( movieSection: MovieSection, isDarkModeActive: Boolean, switchDarkMode: () ->

    Unit = {}, onClick: (movie: Movie) -> Unit ) { val viewModel: MovieViewModel = viewModel() val movies = when (movieSection) { MovieSection.UPCOMING -> viewModel.getUpComing() .collectAsState(initial = emptyList()) MovieSection.POPULAR -> viewModel.getPopulars() .collectAsState(initial = emptyList()) MovieSection.TRENDING -> viewModel.getDailyTrending() .collectAsState(initial = emptyList()) } MovieList( movies = movies.value, isDarkModeActive = isDarkModeActive, switchDarkMode = switchDarkMode, onClick = onClick ) }
  82. COMPOSABLE NAVIGATION

  83. val navController = rememberNavController() NavHost(navController, startDestination = "movies") { composable(route

    = "movies") { ... } composable( route = "sections/{sectionId}" ) { ... } composable( route = "movies/{movieId}" ) { ... } }
  84. val navController = rememberNavController() NavHost(navController, startDestination = "movies") { composable(route

    = "movies") { ... } composable( route = "sections/{sectionId}", arguments = listOf(navArgument("sectionId") { type = NavType.EnumType(MovieSection::class.java) }) ) { ... } composable( route = "movies/{movieId}" ) { ... } }
  85. val navController = rememberNavController() NavHost(navController, startDestination = "movies") { composable(route

    = "movies") { ... } composable( route = "sections/{sectionId}", arguments = listOf(navArgument("sectionId") { type = NavType.EnumType(MovieSection::class.java) }) ) { ... } composable( route = "movies/{movieId}", arguments = listOf(navArgument("movieId") { type = NavType.IntType }) ) { ... } }
  86. val navController = rememberNavController() NavHost(navController, startDestination = "movies") { composable(route

    = "movies") { MovieHomeViewModel( onViewAllClick = { navController.navigate("sections/$it") }, onClick = { navController.navigate("movies/${it.id}") } ) } composable( route = "sections/{sectionId}", arguments = listOf(navArgument("sectionId") { type = NavType.EnumType(MovieSection::class.java) }) ) { ... } composable( route = "movies/{movieId}", arguments = listOf(navArgument("movieId") { type = NavType.IntType }) ) { ... } }
  87. WHY SHOULD CARE ABOUT COMPOSE?

  88. WHY SHOULD CARE ABOUT COMPOSE? ▸ Android UI framework never

    changed since 2008 ▸ Need to wait very long time before bug fixes deployed in production ▸ Need to worry about keeping the View hierarchy as flat as possible ▸ Unbundled from OS ▸ Exit View and Fragment ▸ Clarify state ownership and event handling ▸ Writing less code
  89. WHAT’S NEXT WITH COMPOSE?

  90. SUPPORT HOT RELOAD WHAT’S NEXT WITH COMPOSE?

  91. COMPOSE FOR KMM WHAT’S NEXT WITH COMPOSE?

  92. slack.kotlinlang.org #compose Open Source Project github.com/androidx/androidx

  93. THANK YOU! @GerardPaligot

  94. REFERENCES ▸ Discovering Movies, GitHub Repository of Gerard Paligot https://github.com/GerardPaligot/discovering-movies

    ▸ Official documentation about Jetpack Compose, Developer Android Website https://developer.android.com/jetpack/compose/documentation ▸ Pathways Compose https://developer.android.com/courses/pathways/compose ▸ Compose Academy https://compose.academy/ ▸ Samples Compose app by community https://jetpackcompose.app/