Kicking Back with Compose
for Android TV
Joe Birch @HiThereJoe
Android @ Bu
ff
er
Running @ Droid.Academy
👋
What we’ll learn
📺 Foundations of Android TV
📺 Layout Composables for Android TV
📺 Material Composables for Android TV
📺 Sharing composables across TV and Mobile
150 million
monthly active devices
running Android TV
(smart TVs and chromecast devices)
Android TV
Navigation
Focus
Immersive
Compose, as you know it
implementation 'androidx.tv:tv-foundation:1.0.0-alpha07'
implementation ‘androidx.tv:tv-material:1.0.0-alpha07'
Lazy Layouts
TV Lazy Row
@Composable
fun TvLazyRow(
modifier: Modifier = Modifier,
state: TvLazyListState = rememberTvLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
horizontalArrangement: Arrangement.Horizontal =
if (!reverseLayout) Arrangement.Start else Arrangement.End,
verticalAlignment: Alignment.Vertical = Alignment.Top,
userScrollEnabled: Boolean = true,
pivotOffsets: PivotOffsets = PivotOffsets(),
content: TvLazyListScope.() -> Unit
)
TvLazyRow(
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(18.dp)
) {
items(data) { cardItem ->
...
}
}
📺 Display horizontally scrolling content
📺 Use to display content in feeds
📺 In most cases, prioritise Row over Column for
content browsing
TV Lazy Row
📺 Display horizontally scrolling content
📺 Use to display content in feeds
📺 In most cases, prioritise Row over Column for
content browsing
TV Lazy Row
📺 Display horizontally scrolling content
📺 Use to display content in feeds
📺 In most cases, prioritise Row over Column for
content browsing
TV Lazy Row
TV Lazy Column
@Composable
fun TvLazyColumn(
modifier: Modifier = Modifier,
state: TvLazyListState = rememberTvLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
userScrollEnabled: Boolean = true,
pivotOffsets: PivotOffsets = PivotOffsets(),
content: TvLazyListScope.() -> Unit
)
TvLazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(18.dp)
) {
items(data) { cardItem ->
...
}
}
TV Lazy or Lazy 🤔
@Composable
fun TvLazyColumn(
modifier: Modifier = Modifier,
state: TvLazyListState = rememberTvLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = …,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
userScrollEnabled: Boolean = true,
pivotOffsets: PivotOffsets = PivotOffsets(),
content: TvLazyListScope.() -> Unit
)
@Composable
fun LazyColumn(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = …,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit
)
@Composable
fun TvLazyColumn(
modifier: Modifier = Modifier,
state: TvLazyListState = rememberTvLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = …,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
userScrollEnabled: Boolean = true,
pivotOffsets: PivotOffsets = PivotOffsets(),
content: TvLazyListScope.() -> Unit
)
@Composable
fun LazyColumn(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = …,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit
)
@Composable
fun TvLazyColumn(
modifier: Modifier = Modifier,
state: TvLazyListState = rememberTvLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = …,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
userScrollEnabled: Boolean = true,
pivotOffsets: PivotOffsets = PivotOffsets(),
content: TvLazyListScope.() -> Unit
)
@Composable
fun LazyColumn(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = …,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean = true,
content: LazyListScope.() -> Unit
)
Lazy Row
TV Lazy Row
Lazy Row
TV Lazy Row
📺 Use TV Lazy over Lazy layouts to ensure TV specific
behaviours are automatically applied
📺 Familiar API for composing child items
📺 Enforce TV composable properties and scope
TV Lazy Layouts
📺 Use TV Lazy over Lazy layouts to ensure TV specific
behaviours are automatically applied
📺 Familiar API for composing child items
📺 Enforce TV composable properties and scope
TV Lazy Layouts
📺 Use TV Lazy over Lazy layouts to ensure TV specific
behaviours are automatically applied
📺 Familiar API for composing child items
📺 Enforce TV composable properties and scope
TV Lazy Layouts
Lazy Grids
@Composable
fun TvLazyVerticalGrid(
columns: TvGridCells,
modifier: Modifier = Modifier,
state: TvLazyGridState = rememberTvLazyGridState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
userScrollEnabled: Boolean = true,
pivotOffsets: PivotOffsets = PivotOffsets(),
content: TvLazyGridScope.() -> Unit
)
TvLazyVerticalGrid(
columns = TvGridCells.Fixed(6),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(16.dp)
) {
items(items) {
…
}
}
TvLazyVerticalGrid(
columns = TvGridCells.Fixed(6),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(16.dp)
)
TvLazyVerticalGrid(
columns = TvGridCells.Adaptive(120.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(16.dp)
)
TvLazyHorizontalGrid(
Rows = TvGridCells.Fixed(3),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(16.dp)
) {
…
}
📺 Display large collections of related content
📺 Use tabs to offer content filtering
📺 Use adaptive cell sizing to scale based on available
space
TV Grids
📺 Display large collections of related content
📺 Use tabs to offer content filtering
📺 Use adaptive cell sizing to scale based on available
space
TV Grids
📺 Display large collections of related content
📺 Use tabs to offer content filtering
📺 Use adaptive cell sizing to scale based on available
space
TV Grids
Carousels
@Composable
fun Carousel(
itemCount: Int,
modifier: Modifier = Modifier,
carouselState: CarouselState = remember { CarouselState() },
autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis,
contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform,
contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform,
carouselIndicator:
@Composable BoxScope.() -> Unit = {
…
},
content: @Composable AnimatedContentScope.(index: Int) -> Unit
)
@Composable
fun Carousel(
itemCount: Int,
modifier: Modifier = Modifier,
carouselState: CarouselState = remember { CarouselState() },
autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis,
contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform,
contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform,
carouselIndicator:
@Composable BoxScope.() -> Unit = {
CarouselDefaults.IndicatorRow(
itemCount = itemCount,
activeItemIndex = carouselState.activeItemIndex,
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp),
)
},
content: @Composable AnimatedContentScope.(index: Int) -> Unit
)
@Composable
fun Carousel(
itemCount: Int,
modifier: Modifier = Modifier,
carouselState: CarouselState = remember { CarouselState() },
autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis,
contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform,
contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform,
carouselIndicator:
@Composable BoxScope.() -> Unit = {
CarouselDefaults.IndicatorRow(
itemCount = itemCount,
activeItemIndex = carouselState.activeItemIndex,
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp),
)
},
content: @Composable AnimatedContentScope.(index: Int) -> Unit
)
@Composable
fun Carousel(
itemCount: Int,
modifier: Modifier = Modifier,
carouselState: CarouselState = remember { CarouselState() },
autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis,
contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform,
contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform,
carouselIndicator:
@Composable BoxScope.() -> Unit = {
CarouselDefaults.IndicatorRow(
itemCount = itemCount,
activeItemIndex = carouselState.activeItemIndex,
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp),
)
},
content: @Composable AnimatedContentScope.(index: Int) -> Unit
)
@Composable
fun Carousel(
itemCount: Int,
modifier: Modifier = Modifier,
carouselState: CarouselState = remember { CarouselState() },
autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis,
contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform,
contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform,
carouselIndicator:
@Composable BoxScope.() -> Unit = {
CarouselDefaults.IndicatorRow(
itemCount = itemCount,
activeItemIndex = carouselState.activeItemIndex,
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp),
)
},
content: @Composable AnimatedContentScope.(index: Int) -> Unit
)
Carousel(
itemCount = backgrounds.size,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 100.dp, vertical = 80.dp)
.height(280.dp)
.border(1.dp, Color.LightGray, RoundedCornerShape(16.dp))
) { itemIndex ->
…
}
📺 Highlight featured content
📺 Always provide a way to access the content
📺 Not a replacement for content feeds
Carousel
📺 Highlight featured content
📺 Always provide a way to access the content
📺 Not a replacement for content feeds
Carousel
📺 Highlight featured content
📺 Always provide a way to access the content
📺 Not a replacement for content feeds
Carousel
Immersive Lists
@Composable
fun ImmersiveList(
background:
@Composable ImmersiveListBackgroundScope.(index: Int, listHasFocus: Boolean) -> Unit,
modifier: Modifier = Modifier,
listAlignment: Alignment = Alignment.BottomEnd,
list: @Composable ImmersiveListScope.() -> Unit,
)
ImmersiveList(
modifier = Modifier
.height(immersiveListHeight + cardHeight / 2)
.fillMaxWidth(),
background = { index, _ ->
…
}
) {
}
ImmersiveList(
modifier = Modifier
.height(immersiveListHeight + cardHeight / 2)
.fillMaxWidth(),
background = { index, _ ->
…
}
) {
TvLazyRow {
backgrounds.forEachIndexed { index, backgroundColor ->
item {
Box(
modifier = Modifier
…
.immersiveListItem(index)
)
}
}
}
}
ImmersiveList(
modifier = Modifier
.height(immersiveListHeight + cardHeight / 2)
.fillMaxWidth(),
background = { index, _ ->
…
}
) {
TvLazyRow {
backgrounds.forEachIndexed { index, backgroundColor ->
item {
Box(
modifier = Modifier
…
.immersiveListItem(index)
)
}
}
}
}
📺 Give immersive focus to a selected item
📺 Show more information for content
📺 Greater efficiency for the user
Immersive List
📺 Give immersive focus to a selected item
📺 Show more information for content
📺 Greater efficiency for the user
Immersive List
📺 Give immersive focus to a selected item
📺 Show more information for content
📺 Greater efficiency for the user
Immersive List
Navigation
Navigation Drawer
@Composable
fun NavigationDrawer(
drawerContent: @Composable (DrawerValue) -> Unit,
modifier: Modifier = Modifier,
drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
content: @Composable () -> Unit
)
Modal Navigation Drawer
@Composable
fun ModalNavigationDrawer(
drawerContent: @Composable (DrawerValue) -> Unit,
modifier: Modifier = Modifier,
drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
scrimColor: Color = LocalColorScheme.current.scrim.copy(alpha = 0.5f),
content: @Composable () -> Unit
)
Tabs
@Composable
fun TabRow(
selectedTabIndex: Int,
modifier: Modifier = Modifier,
containerColor: Color = TabRowDefaults.ContainerColor,
contentColor: Color = TabRowDefaults.contentColor(),
separator: @Composable () -> Unit = { TabRowDefaults.TabSeparator() },
indicator: @Composable …,
tabs: @Composable () -> Unit
)
@Composable
fun Tab(
selected: Boolean,
onFocus: () -> Unit,
modifier: Modifier = Modifier,
onClick: () -> Unit = { },
enabled: Boolean = true,
colors: TabColors = TabDefaults.pillIndicatorTabColors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable RowScope.() -> Unit
)
val tabs = listOf("View All", "Action", "Sci-Fi", "Drama", "Thriller")
var selectedTabIndex by remember { mutableIntStateOf(0) }
TabRow(
selectedTabIndex = selectedTabIndex
) {
tabs.forEachIndexed { index, tab ->
Tab(
selected = selectedTabIndex == index,
onFocus = {
selectedTabIndex = index
},
) {
Text(…)
}
}
}
Material Components
Card
@Composable
fun WideCardLayout(
imageCard: @Composable (interactionSource: MutableInteractionSource) -> Unit,
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
subtitle: @Composable () -> Unit = {},
description: @Composable () -> Unit = {},
contentColor: CardLayoutColors = CardLayoutDefaults.contentColor(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)
@Composable
fun StandardCardLayout(
imageCard: @Composable (interactionSource: MutableInteractionSource) -> Unit,
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
subtitle: @Composable () -> Unit = {},
description: @Composable () -> Unit = {},
contentColor: CardLayoutColors = CardLayoutDefaults.contentColor(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)
@Composable
fun ClassicCard(
onClick: () -> Unit,
image: @Composable BoxScope.() -> Unit,
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
onLongClick: (() -> Unit)? = null,
subtitle: @Composable () -> Unit = {},
description: @Composable () -> Unit = {},
shape: CardShape = CardDefaults.shape(),
colors: CardColors = CardDefaults.colors(),
scale: CardScale = CardDefaults.scale(),
border: CardBorder = CardDefaults.border(),
glow: CardGlow = CardDefaults.glow(),
contentPadding: PaddingValues = PaddingValues(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)
@Composable
fun WideClassicCard(
onClick: () -> Unit,
image: @Composable BoxScope.() -> Unit,
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
onLongClick: (() -> Unit)? = null,
subtitle: @Composable () -> Unit = {},
description: @Composable () -> Unit = {},
shape: CardShape = CardDefaults.shape(),
colors: CardColors = CardDefaults.colors(),
scale: CardScale = CardDefaults.scale(),
border: CardBorder = CardDefaults.border(),
glow: CardGlow = CardDefaults.glow(),
contentPadding: PaddingValues = PaddingValues(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)
Surface
@Composable
fun Surface(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
onLongClick: (() -> Unit)? = null,
tonalElevation: Dp = Elevation.Level0,
shape: ToggleableSurfaceShape = ToggleableSurfaceDefaults.shape(),
colors: ToggleableSurfaceColors = ToggleableSurfaceDefaults.colors(),
scale: ToggleableSurfaceScale = ToggleableSurfaceDefaults.scale(),
border: ToggleableSurfaceBorder = ToggleableSurfaceDefaults.border(),
glow: ToggleableSurfaceGlow = ToggleableSurfaceDefaults.glow(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable (BoxScope.() -> Unit)
)
@Composable
fun Surface(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
onLongClick: (() -> Unit)? = null,
tonalElevation: Dp = Elevation.Level0,
shape: ToggleableSurfaceShape = ToggleableSurfaceDefaults.shape(),
colors: ToggleableSurfaceColors = ToggleableSurfaceDefaults.colors(),
scale: ToggleableSurfaceScale = ToggleableSurfaceDefaults.scale(),
border: ToggleableSurfaceBorder = ToggleableSurfaceDefaults.border(),
glow: ToggleableSurfaceGlow = ToggleableSurfaceDefaults.glow(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable (BoxScope.() -> Unit)
)
Switch
@Composable
fun Switch(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
thumbContent: (@Composable () -> Unit)? = null,
enabled: Boolean = true,
colors: SwitchColors = SwitchDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
)
📺 Button
📺 Checkbox
📺 Radio Button
📺 Wide Button
Material Components
Reusing Composables
Mobile App
Component
Library
Material Card
Mobile App
Component
Library
Material Card
TV App
Material TV Card
TV App
Material TV Card
Mobile App
Material Card
Component
Library
Card Content
TV App
Material TV Card
Mobile App
Material Card
Component
Library
Card Content
@Composable
fun TvMovieCard() {
Card(...) {
MySharedContent()
}
}
@Composable
fun MovieCard() {
Card(...) {
MySharedContent()
}
}
@Composable
fun MySharedContent() {
...
}
That’s all
Practical Jetpack Compose
Build 12 independent projects, interacting
with a vast range of essential Compose
APIs.
ComposeBook.com
25% o
ff
using code DROIDCON

Kicking Back with Compose for Android TV

  • 1.
    Kicking Back withCompose for Android TV
  • 2.
    Joe Birch @HiThereJoe Android@ Bu ff er Running @ Droid.Academy 👋
  • 3.
    What we’ll learn 📺Foundations of Android TV 📺 Layout Composables for Android TV 📺 Material Composables for Android TV 📺 Sharing composables across TV and Mobile
  • 4.
    150 million monthly activedevices running Android TV (smart TVs and chromecast devices)
  • 5.
  • 6.
  • 10.
  • 15.
  • 21.
    Compose, as youknow it implementation 'androidx.tv:tv-foundation:1.0.0-alpha07' implementation ‘androidx.tv:tv-material:1.0.0-alpha07'
  • 23.
  • 24.
  • 26.
    @Composable fun TvLazyRow( modifier: Modifier= Modifier, state: TvLazyListState = rememberTvLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, horizontalArrangement: Arrangement.Horizontal = if (!reverseLayout) Arrangement.Start else Arrangement.End, verticalAlignment: Alignment.Vertical = Alignment.Top, userScrollEnabled: Boolean = true, pivotOffsets: PivotOffsets = PivotOffsets(), content: TvLazyListScope.() -> Unit )
  • 27.
    TvLazyRow( contentPadding = PaddingValues(16.dp), horizontalArrangement= Arrangement.spacedBy(18.dp) ) { items(data) { cardItem -> ... } }
  • 28.
    📺 Display horizontallyscrolling content 📺 Use to display content in feeds 📺 In most cases, prioritise Row over Column for content browsing TV Lazy Row
  • 29.
    📺 Display horizontallyscrolling content 📺 Use to display content in feeds 📺 In most cases, prioritise Row over Column for content browsing TV Lazy Row
  • 30.
    📺 Display horizontallyscrolling content 📺 Use to display content in feeds 📺 In most cases, prioritise Row over Column for content browsing TV Lazy Row
  • 31.
  • 33.
    @Composable fun TvLazyColumn( modifier: Modifier= Modifier, state: TvLazyListState = rememberTvLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, userScrollEnabled: Boolean = true, pivotOffsets: PivotOffsets = PivotOffsets(), content: TvLazyListScope.() -> Unit )
  • 34.
    TvLazyColumn( contentPadding = PaddingValues(16.dp), verticalArrangement= Arrangement.spacedBy(18.dp) ) { items(data) { cardItem -> ... } }
  • 35.
    TV Lazy orLazy 🤔
  • 36.
    @Composable fun TvLazyColumn( modifier: Modifier= Modifier, state: TvLazyListState = rememberTvLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = …, horizontalAlignment: Alignment.Horizontal = Alignment.Start, userScrollEnabled: Boolean = true, pivotOffsets: PivotOffsets = PivotOffsets(), content: TvLazyListScope.() -> Unit ) @Composable fun LazyColumn( modifier: Modifier = Modifier, state: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = …, horizontalAlignment: Alignment.Horizontal = Alignment.Start, flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit )
  • 37.
    @Composable fun TvLazyColumn( modifier: Modifier= Modifier, state: TvLazyListState = rememberTvLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = …, horizontalAlignment: Alignment.Horizontal = Alignment.Start, userScrollEnabled: Boolean = true, pivotOffsets: PivotOffsets = PivotOffsets(), content: TvLazyListScope.() -> Unit ) @Composable fun LazyColumn( modifier: Modifier = Modifier, state: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = …, horizontalAlignment: Alignment.Horizontal = Alignment.Start, flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit )
  • 38.
    @Composable fun TvLazyColumn( modifier: Modifier= Modifier, state: TvLazyListState = rememberTvLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = …, horizontalAlignment: Alignment.Horizontal = Alignment.Start, userScrollEnabled: Boolean = true, pivotOffsets: PivotOffsets = PivotOffsets(), content: TvLazyListScope.() -> Unit ) @Composable fun LazyColumn( modifier: Modifier = Modifier, state: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = …, horizontalAlignment: Alignment.Horizontal = Alignment.Start, flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit )
  • 39.
  • 40.
  • 41.
    📺 Use TVLazy over Lazy layouts to ensure TV specific behaviours are automatically applied 📺 Familiar API for composing child items 📺 Enforce TV composable properties and scope TV Lazy Layouts
  • 42.
    📺 Use TVLazy over Lazy layouts to ensure TV specific behaviours are automatically applied 📺 Familiar API for composing child items 📺 Enforce TV composable properties and scope TV Lazy Layouts
  • 43.
    📺 Use TVLazy over Lazy layouts to ensure TV specific behaviours are automatically applied 📺 Familiar API for composing child items 📺 Enforce TV composable properties and scope TV Lazy Layouts
  • 44.
  • 45.
    @Composable fun TvLazyVerticalGrid( columns: TvGridCells, modifier:Modifier = Modifier, state: TvLazyGridState = rememberTvLazyGridState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, userScrollEnabled: Boolean = true, pivotOffsets: PivotOffsets = PivotOffsets(), content: TvLazyGridScope.() -> Unit )
  • 46.
    TvLazyVerticalGrid( columns = TvGridCells.Fixed(6), verticalArrangement= Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(16.dp) ) { items(items) { … } }
  • 48.
    TvLazyVerticalGrid( columns = TvGridCells.Fixed(6), verticalArrangement= Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(16.dp) )
  • 50.
    TvLazyVerticalGrid( columns = TvGridCells.Adaptive(120.dp), verticalArrangement= Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(16.dp) )
  • 52.
    TvLazyHorizontalGrid( Rows = TvGridCells.Fixed(3), verticalArrangement= Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(16.dp) ) { … }
  • 54.
    📺 Display largecollections of related content 📺 Use tabs to offer content filtering 📺 Use adaptive cell sizing to scale based on available space TV Grids
  • 55.
    📺 Display largecollections of related content 📺 Use tabs to offer content filtering 📺 Use adaptive cell sizing to scale based on available space TV Grids
  • 56.
    📺 Display largecollections of related content 📺 Use tabs to offer content filtering 📺 Use adaptive cell sizing to scale based on available space TV Grids
  • 57.
  • 59.
    @Composable fun Carousel( itemCount: Int, modifier:Modifier = Modifier, carouselState: CarouselState = remember { CarouselState() }, autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis, contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform, contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform, carouselIndicator: @Composable BoxScope.() -> Unit = { … }, content: @Composable AnimatedContentScope.(index: Int) -> Unit )
  • 60.
    @Composable fun Carousel( itemCount: Int, modifier:Modifier = Modifier, carouselState: CarouselState = remember { CarouselState() }, autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis, contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform, contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform, carouselIndicator: @Composable BoxScope.() -> Unit = { CarouselDefaults.IndicatorRow( itemCount = itemCount, activeItemIndex = carouselState.activeItemIndex, modifier = Modifier .align(Alignment.BottomEnd) .padding(16.dp), ) }, content: @Composable AnimatedContentScope.(index: Int) -> Unit )
  • 61.
    @Composable fun Carousel( itemCount: Int, modifier:Modifier = Modifier, carouselState: CarouselState = remember { CarouselState() }, autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis, contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform, contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform, carouselIndicator: @Composable BoxScope.() -> Unit = { CarouselDefaults.IndicatorRow( itemCount = itemCount, activeItemIndex = carouselState.activeItemIndex, modifier = Modifier .align(Alignment.BottomEnd) .padding(16.dp), ) }, content: @Composable AnimatedContentScope.(index: Int) -> Unit )
  • 62.
    @Composable fun Carousel( itemCount: Int, modifier:Modifier = Modifier, carouselState: CarouselState = remember { CarouselState() }, autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis, contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform, contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform, carouselIndicator: @Composable BoxScope.() -> Unit = { CarouselDefaults.IndicatorRow( itemCount = itemCount, activeItemIndex = carouselState.activeItemIndex, modifier = Modifier .align(Alignment.BottomEnd) .padding(16.dp), ) }, content: @Composable AnimatedContentScope.(index: Int) -> Unit )
  • 63.
    @Composable fun Carousel( itemCount: Int, modifier:Modifier = Modifier, carouselState: CarouselState = remember { CarouselState() }, autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis, contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform, contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform, carouselIndicator: @Composable BoxScope.() -> Unit = { CarouselDefaults.IndicatorRow( itemCount = itemCount, activeItemIndex = carouselState.activeItemIndex, modifier = Modifier .align(Alignment.BottomEnd) .padding(16.dp), ) }, content: @Composable AnimatedContentScope.(index: Int) -> Unit )
  • 64.
    Carousel( itemCount = backgrounds.size, modifier= Modifier .fillMaxWidth() .padding(horizontal = 100.dp, vertical = 80.dp) .height(280.dp) .border(1.dp, Color.LightGray, RoundedCornerShape(16.dp)) ) { itemIndex -> … }
  • 65.
    📺 Highlight featuredcontent 📺 Always provide a way to access the content 📺 Not a replacement for content feeds Carousel
  • 66.
    📺 Highlight featuredcontent 📺 Always provide a way to access the content 📺 Not a replacement for content feeds Carousel
  • 67.
    📺 Highlight featuredcontent 📺 Always provide a way to access the content 📺 Not a replacement for content feeds Carousel
  • 68.
  • 70.
    @Composable fun ImmersiveList( background: @Composable ImmersiveListBackgroundScope.(index:Int, listHasFocus: Boolean) -> Unit, modifier: Modifier = Modifier, listAlignment: Alignment = Alignment.BottomEnd, list: @Composable ImmersiveListScope.() -> Unit, )
  • 71.
    ImmersiveList( modifier = Modifier .height(immersiveListHeight+ cardHeight / 2) .fillMaxWidth(), background = { index, _ -> … } ) { }
  • 72.
    ImmersiveList( modifier = Modifier .height(immersiveListHeight+ cardHeight / 2) .fillMaxWidth(), background = { index, _ -> … } ) { TvLazyRow { backgrounds.forEachIndexed { index, backgroundColor -> item { Box( modifier = Modifier … .immersiveListItem(index) ) } } } }
  • 73.
    ImmersiveList( modifier = Modifier .height(immersiveListHeight+ cardHeight / 2) .fillMaxWidth(), background = { index, _ -> … } ) { TvLazyRow { backgrounds.forEachIndexed { index, backgroundColor -> item { Box( modifier = Modifier … .immersiveListItem(index) ) } } } }
  • 74.
    📺 Give immersivefocus to a selected item 📺 Show more information for content 📺 Greater efficiency for the user Immersive List
  • 75.
    📺 Give immersivefocus to a selected item 📺 Show more information for content 📺 Greater efficiency for the user Immersive List
  • 76.
    📺 Give immersivefocus to a selected item 📺 Show more information for content 📺 Greater efficiency for the user Immersive List
  • 77.
  • 78.
  • 79.
    @Composable fun NavigationDrawer( drawerContent: @Composable(DrawerValue) -> Unit, modifier: Modifier = Modifier, drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed), content: @Composable () -> Unit )
  • 81.
  • 82.
    @Composable fun ModalNavigationDrawer( drawerContent: @Composable(DrawerValue) -> Unit, modifier: Modifier = Modifier, drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed), scrimColor: Color = LocalColorScheme.current.scrim.copy(alpha = 0.5f), content: @Composable () -> Unit )
  • 84.
  • 85.
    @Composable fun TabRow( selectedTabIndex: Int, modifier:Modifier = Modifier, containerColor: Color = TabRowDefaults.ContainerColor, contentColor: Color = TabRowDefaults.contentColor(), separator: @Composable () -> Unit = { TabRowDefaults.TabSeparator() }, indicator: @Composable …, tabs: @Composable () -> Unit ) @Composable fun Tab( selected: Boolean, onFocus: () -> Unit, modifier: Modifier = Modifier, onClick: () -> Unit = { }, enabled: Boolean = true, colors: TabColors = TabDefaults.pillIndicatorTabColors(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit )
  • 86.
    val tabs =listOf("View All", "Action", "Sci-Fi", "Drama", "Thriller") var selectedTabIndex by remember { mutableIntStateOf(0) } TabRow( selectedTabIndex = selectedTabIndex ) { tabs.forEachIndexed { index, tab -> Tab( selected = selectedTabIndex == index, onFocus = { selectedTabIndex = index }, ) { Text(…) } } }
  • 88.
  • 89.
  • 90.
    @Composable fun WideCardLayout( imageCard: @Composable(interactionSource: MutableInteractionSource) -> Unit, title: @Composable () -> Unit, modifier: Modifier = Modifier, subtitle: @Composable () -> Unit = {}, description: @Composable () -> Unit = {}, contentColor: CardLayoutColors = CardLayoutDefaults.contentColor(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } )
  • 91.
    @Composable fun StandardCardLayout( imageCard: @Composable(interactionSource: MutableInteractionSource) -> Unit, title: @Composable () -> Unit, modifier: Modifier = Modifier, subtitle: @Composable () -> Unit = {}, description: @Composable () -> Unit = {}, contentColor: CardLayoutColors = CardLayoutDefaults.contentColor(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } )
  • 92.
    @Composable fun ClassicCard( onClick: ()-> Unit, image: @Composable BoxScope.() -> Unit, title: @Composable () -> Unit, modifier: Modifier = Modifier, onLongClick: (() -> Unit)? = null, subtitle: @Composable () -> Unit = {}, description: @Composable () -> Unit = {}, shape: CardShape = CardDefaults.shape(), colors: CardColors = CardDefaults.colors(), scale: CardScale = CardDefaults.scale(), border: CardBorder = CardDefaults.border(), glow: CardGlow = CardDefaults.glow(), contentPadding: PaddingValues = PaddingValues(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } )
  • 93.
    @Composable fun WideClassicCard( onClick: ()-> Unit, image: @Composable BoxScope.() -> Unit, title: @Composable () -> Unit, modifier: Modifier = Modifier, onLongClick: (() -> Unit)? = null, subtitle: @Composable () -> Unit = {}, description: @Composable () -> Unit = {}, shape: CardShape = CardDefaults.shape(), colors: CardColors = CardDefaults.colors(), scale: CardScale = CardDefaults.scale(), border: CardBorder = CardDefaults.border(), glow: CardGlow = CardDefaults.glow(), contentPadding: PaddingValues = PaddingValues(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } )
  • 94.
  • 96.
    @Composable fun Surface( checked: Boolean, onCheckedChange:(Boolean) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, onLongClick: (() -> Unit)? = null, tonalElevation: Dp = Elevation.Level0, shape: ToggleableSurfaceShape = ToggleableSurfaceDefaults.shape(), colors: ToggleableSurfaceColors = ToggleableSurfaceDefaults.colors(), scale: ToggleableSurfaceScale = ToggleableSurfaceDefaults.scale(), border: ToggleableSurfaceBorder = ToggleableSurfaceDefaults.border(), glow: ToggleableSurfaceGlow = ToggleableSurfaceDefaults.glow(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable (BoxScope.() -> Unit) )
  • 97.
    @Composable fun Surface( checked: Boolean, onCheckedChange:(Boolean) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, onLongClick: (() -> Unit)? = null, tonalElevation: Dp = Elevation.Level0, shape: ToggleableSurfaceShape = ToggleableSurfaceDefaults.shape(), colors: ToggleableSurfaceColors = ToggleableSurfaceDefaults.colors(), scale: ToggleableSurfaceScale = ToggleableSurfaceDefaults.scale(), border: ToggleableSurfaceBorder = ToggleableSurfaceDefaults.border(), glow: ToggleableSurfaceGlow = ToggleableSurfaceDefaults.glow(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable (BoxScope.() -> Unit) )
  • 99.
  • 101.
    @Composable fun Switch( checked: Boolean, onCheckedChange:((Boolean) -> Unit)?, modifier: Modifier = Modifier, thumbContent: (@Composable () -> Unit)? = null, enabled: Boolean = true, colors: SwitchColors = SwitchDefaults.colors(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, )
  • 103.
    📺 Button 📺 Checkbox 📺Radio Button 📺 Wide Button Material Components
  • 104.
  • 105.
  • 106.
  • 107.
    TV App Material TVCard Mobile App Material Card Component Library Card Content
  • 108.
    TV App Material TVCard Mobile App Material Card Component Library Card Content
  • 109.
    @Composable fun TvMovieCard() { Card(...){ MySharedContent() } } @Composable fun MovieCard() { Card(...) { MySharedContent() } } @Composable fun MySharedContent() { ... }
  • 110.
  • 111.
    Practical Jetpack Compose Build12 independent projects, interacting with a vast range of essential Compose APIs. ComposeBook.com 25% o ff using code DROIDCON