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

Adaptive Layout for Any Screen with Jetpack Compose [Dicoding Developer Conference]

Adaptive Layout for Any Screen with Jetpack Compose [Dicoding Developer Conference]

Outline:
Material Design Guidance
Adaptive Navigation in Compose
Canonical Layout in Compose
Foldable Phone
App Quality

Ahmad Arif Faizin

February 11, 2023
Tweet

More Decks by Ahmad Arif Faizin

Other Decks in Technology

Transcript

  1. Adaptive Layout
    for Any Screen
    with Jetpack
    Compose
    Ahmad Arif
    Faizin
    Curriculum Developer
    at Dicoding

    View full-size slide

  2. Ahmad Arif Faizin
    Curriculum Developer at Dicoding
    Adaptive Layout for
    Any Screen with
    Jetpack Compose

    View full-size slide

  3. ● Material Design
    Guidance
    ● Adaptive Navigation in
    Compose
    ● List-Detail in Compose
    ● Foldable Device
    ● Large Screen App Quality
    Outline

    View full-size slide

  4. Source: Blog - What's new in foldables, tablets, and large screens

    View full-size slide

  5. Mobile App
    Developers
    Be Like…

    View full-size slide

  6. Source: Blog - What's new in foldables, tablets, and large screens

    View full-size slide

  7. 270M
    Active large screen
    Android devices

    View full-size slide

  8. Apps are Optimizing for Large Screen
    goo.gle/large-screens-case-studies

    View full-size slide

  9. Build your app for the
    ecosystem increase
    your reach

    View full-size slide

  10. Material Design
    Guidance
    01

    View full-size slide

  11. Understanding Anatomy
    1. Margin 2. Column 3. Gutter

    View full-size slide

  12. Breakpoints
    Breakpoint
    range (dp)
    Portrait Landscape
    Window size
    class
    Columns
    Minimum
    margins*
    0-599 Handset
    Phone in
    portrait
    Compact 4 8
    600-839
    Foldable
    Small tablet
    Foldable
    Small tablet
    Medium 12 12
    840+ Large tablet
    Large tablet
    Desktop
    Expanded 12 32

    View full-size slide

  13. Avoid using
    physical/hardware values
    for making layout
    decisions!

    View full-size slide

  14. Use
    Window size
    for layout
    decision!

    View full-size slide

  15. Material Measurement

    View full-size slide

  16. Proven Design Pattern: Canonical Layout

    View full-size slide

  17. List-Detail
    ● Best for browsing content and
    quickly seeing details.
    ● Recommended for 840+dp
    ● Key use case
    ○ Text message + conversation
    ○ File browser + open folder
    ○ Musical artist + album detail

    View full-size slide

  18. Feed
    ● Equivalent content elements in
    a configurable grid for quick,
    convenient viewing of a large
    amount of content.
    ● Recommended for 600+dp
    ● Key use case
    ○ News
    ○ Social media

    View full-size slide

  19. Supporting Panel
    ● The screen is divided between a
    focus panel (left) and a supporting
    panel (right).
    ● Recommended for 600+dp
    ● Key use case
    ○ Productivity
    ○ Document editing and
    commenting
    ○ Content and media browsing

    View full-size slide

  20. Component Swapping

    View full-size slide

  21. “Reach” Your Users on Large Screens

    View full-size slide

  22. Window size class Few items Many items
    compact width bottom navigation bar navigation drawer
    (leading edge or bottom)
    medium width navigation rail navigation drawer
    (leading edge)
    expanded width persistent navigation
    drawer (leading edge)
    persistent navigation
    drawer (leading edge)
    Adaptive Navigation

    View full-size slide

  23. Navigation Rail
    ● Side navigation
    component that
    displays three to
    seven app
    destinations
    ● Better ergonomics
    Easier reach on
    large screen
    ● Available in
    Material3

    View full-size slide

  24. Adaptive Navigation
    in Compose
    02

    View full-size slide

  25. Resizable
    Emulator

    View full-size slide

  26. Window Size
    Classes API

    View full-size slide

  27. WindowSizeClass Initialization
    implementation "androidx.compose.material3:material3-window-size-class:1.0.1"
    @OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
    ReplyTheme {
    val widthSizeClass = calculateWindowSizeClass(this).widthSizeClass
    ...
    }
    }
    }

    View full-size slide

  28. WindowSizeClass Implementation
    val navigationType: ReplyNavigationType //enum class
    when (widthSizeClass) {
    WindowWidthSizeClass.Compact -> {
    navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
    }
    WindowWidthSizeClass.Medium -> {
    navigationType = ReplyNavigationType.NAVIGATION_RAIL
    }
    WindowWidthSizeClass.Expanded -> {
    navigationType = ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
    }
    else -> {
    navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
    }
    }

    View full-size slide

  29. Navigation Visibility Logic
    if (navigationType == PERMANENT_NAVIGATION_DRAWER) {
    PermanentNavigationDrawer(...) { ReplyAppContent(navigationType) }
    } else {
    ModalNavigationDrawer(...) { ReplyAppContent(navigationType)}
    }
    ...
    @Composable
    fun ReplyAppContent(navigationType: ReplyNavigationType) {
    Row(...) {
    AnimatedVisibility(visible = navigationType == NAVIGATION_RAIL) {
    NavigationRail() {...}
    }
    Column(...) {
    ListOnlyContent(...)
    AnimatedVisibility(visible = navigationType == BOTTOM_NAVIGATION) {
    NavigationBar() {...}
    }
    }

    View full-size slide

  30. Composable Preview

    View full-size slide

  31. List-Detail in
    Compose
    03

    View full-size slide

  32. ContentType Classification
    val contentType: ReplyContentType //enum class
    contentType = if (widthSizeClass == WindowWidthSizeClass.Expanded) {
    ReplyContentType.LIST_AND_DETAIL
    } else {
    ReplyContentType.LIST_ONLY
    }

    View full-size slide

  33. Previous Logic
    @Composable
    fun ReplyAppContent(navigationType: ReplyNavigationType) {
    Row(...) {
    AnimatedVisibility(visible = navigationType == NAVIGATION_RAIL) {
    NavigationRail() {...}
    }
    Column(...) {
    ListOnlyContent(...)
    AnimatedVisibility(visible = navigationType == BOTTOM_NAVIGATION) {
    NavigationBar() {...}
    }
    }
    }
    }

    View full-size slide

  34. Additional Content Type Logic
    if (contentType == ReplyContentType.LIST_AND_DETAIL) {
    Row {
    ListOnlyContent()
    DetailContent(selectedItemId)
    }
    } else {
    if (replyHomeUIState.isShowingHomepage) {
    ListOnlyContent()
    } else {
    DetailContent(selectedItemId)
    }
    }

    View full-size slide

  35. Screen State Logic in ViewModel
    //ketika item pada list dipilih
    fun updateDetailsScreenStates(email: Email) {
    _uiState.update {
    it.copy(
    currentSelectedEmail = email,
    isShowingHomepage = false
    )
    }
    }

    View full-size slide

  36. Wait a
    moment!

    View full-size slide

  37. Navigation Component vs Manual Navigation

    View full-size slide

  38. BackHandler
    if (contentType == ReplyContentType.LIST_AND_DETAIL) {
    ...
    } else {
    if (selectedItemId != null) {
    DetailContent(selectedItemId)
    BackHandler { /* handle returning to ListOnlyContent() */ }
    } else {
    ListOnlyContent()
    }
    }

    View full-size slide

  39. Additional Screen State Logic in ViewModel
    fun updateDetailsScreenStates(email: Email) {
    _uiState.update {
    it.copy(
    currentSelectedEmail = email,
    isShowingHomepage = false
    )
    }
    }
    fun resetHomeScreenStates() {
    _uiState.update {
    it.copy(
    currentSelectedEmail = it.currentSelectedEmail,
    isShowingHomepage = true
    )
    }
    }

    View full-size slide

  40. Is everything
    okay now?

    View full-size slide

  41. Foldable
    Device
    04

    View full-size slide

  42. Foldable
    Emulator
    *Use Microsoft Surface
    Duo emulator for dual
    screen emulator

    View full-size slide

  43. Device Posture
    windowLayoutInfo
    ● state()
    HALF_OPENED/FLAT
    ● orientation()
    VERTICAL /
    HORIZONTAL
    ● occlusionType()
    FULL/NONE
    ● isSeparating()
    ● bounds.rect()
    Book
    TableTop
    Tent
    Normal

    View full-size slide

  44. Device Posture Classification
    val devicePostureFlow = WindowInfoTracker.getOrCreate(this).windowLayoutInfo(this)
    .flowWithLifecycle(this.lifecycle)
    .map { layoutInfo ->
    val foldingFeature = layoutInfo.displayFeatures
    .filterIsInstance().firstOrNull()
    when {
    isTableTopPosture(foldingFeature) -> DevicePosture.TableTopPosture
    else -> DevicePosture.NormalPosture
    }
    }
    .stateIn(
    scope = lifecycleScope,
    started = SharingStarted.Eagerly,
    initialValue = DevicePosture.NormalPosture //enum class
    )

    View full-size slide

  45. Device Posture Decision
    fun isTableTopPosture(foldFeature: FoldingFeature?): Boolean {
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
    foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
    }
    fun isBookPosture(foldFeature: FoldingFeature?): Boolean {
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
    foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
    }
    fun isSeparating(foldFeature: FoldingFeature?): Boolean {
    return foldFeature?.state == FoldingFeature.State.FLAT &&
    foldFeature.isSeparating
    }

    View full-size slide

  46. if (devicePosture == DevicePosture.TableTopPosture) {
    PlayerContentTableTop()
    } else {
    PlayerContentRegular()
    }

    View full-size slide

  47. Large Screen
    App Quality
    05

    View full-size slide

  48. Quality Tiers
    ● Tier 3 (Basic) 😐
    — Large screen ready
    ● Tier 2 (Better) 🙂
    — Large screen optimized
    ● Tier 1 (Best) 😃
    — Large screen differentiated

    View full-size slide

  49. Tier 3 (Basic) — Large screen ready
    ● Full screen
    ○ But app layout not ideal
    ● Basic support for external input
    ○ Keyboard
    ○ Mouse
    ○ Trackpad
    ● Handles configuration changes
    and retains or restores its state

    View full-size slide

  50. Tier 2 (Better) — Large screen optimized
    ● Layout Optimization
    ○ Leading edge navigation
    ○ Canonical Layout
    ○ Scaled Grid layouts
    ● Enhanced support for external
    input
    ○ Shortcut
    ○ Action by keyboard
    ○ Zooming using mouse
    ○ Hover

    View full-size slide

  51. Responsive UI elements

    View full-size slide

  52. Tier 3 (Best) — Large screen differentiated
    ● Multitasking
    ● Foldable posture
    ● Drag & Drop
    ● Stylus input
    ● Picture-in-picture mode

    View full-size slide

  53. Dialog Box Placement

    View full-size slide

  54. Test Your App
    // Checks start_layout is on the left of end_layout with a vertical folding
    feature.
    @Test
    fun testDeviceOpen_Vertical() {
    activityRule.scenario.onActivity { activity ->
    val feature = FoldingFeature(
    activity = activity,
    state = HALF_OPENED,
    orientation = VERTICAL)
    val expected = TestWindowLayoutInfo(listOf(feature))
    publisherRule.overrideWindowLayoutInfo(expected)
    }
    onView(withId(R.id.start_layout))
    .check(isCompletelyLeftOf(withId(R.id.end_layout)))
    }

    View full-size slide

  55. Further
    Learning

    View full-size slide

  56. https://m3.material.io/foundations/adaptive-design/canonical-layouts
    https://developer.android.com/large-screens/gallery
    https://www.youtube.com/watch?v=Z4h9cvlE66E
    https://github.com/android/user-interface-samples/tree/main/CanonicalLayouts
    https://codelabs.developers.google.com/jetpack-compose-adaptability
    https://www.youtube.com/watch?v=LTLQhC6VadI
    https://android-developers.googleblog.com/2021/05/whats-new-in-foldables-tablets-and.html
    https://android-developers.googleblog.com/2022/11/reach-your-users-on-large-screens.html
    https://developer.android.com/guide/topics/large-screens/large-screen-cookbook
    Reference

    View full-size slide

  57. Bukan hanya otak dan otot aja
    yang perlu adaptive,
    tapi juga UI~
    @arif_faizin, 2023

    View full-size slide

  58. Thank you
    Deck will be available at
    https://speakerdeck.com/arifaizin

    View full-size slide