Mem-build aplikasi adaptif dengan Jetpack Compose

1. Pengantar

Dalam codelab ini, Anda akan mempelajari cara membangun aplikasi adaptif untuk ponsel, tablet, dan perangkat foldable, serta cara aplikasi tersebut meningkatkan keterjangkauan dengan Jetpack Compose. Anda juga akan mempelajari praktik terbaik untuk menggunakan komponen dan tema Material 3.

Sebelum mempelajarinya lebih lanjut, penting untuk memahami apa yang kami maksud dengan kemampuan beradaptasi.

Kemampuan adaptasi

UI untuk aplikasi Anda harus responsif agar dapat memperhitungkan berbagai ukuran jendela, orientasi, dan faktor bentuk. Tata letak adaptif berubah berdasarkan ruang layar yang tersedia untuknya. Perubahan ini bervariasi, mulai dari penyesuaian tata letak sederhana hingga mengisi ruang, memilih gaya navigasi masing-masing, sampai mengubah tata letak sepenuhnya untuk memanfaatkan ruang tambahan.

Untuk mempelajari lebih lanjut, lihat Desain adaptif.

Dalam codelab ini, Anda akan mempelajari cara menggunakan dan memikirkan kemampuan adaptasi saat menggunakan Jetpack Compose. Anda akan membuat aplikasi bernama Reply yang menunjukkan cara menerapkan kemampuan adaptasi untuk semua jenis layar, serta bagaimana kemampuan adaptasi dan keterjangkauan berfungsi bersama untuk memberi pengguna pengalaman yang optimal.

Yang akan Anda pelajari

  • Cara mendesain aplikasi untuk menargetkan semua ukuran jendela dengan Jetpack Compose.
  • Cara menargetkan aplikasi untuk berbagai perangkat foldable.
  • Cara menggunakan berbagai jenis navigasi untuk meningkatkan keterjangkauan dan aksesibilitas.
  • Cara menggunakan komponen Material 3 untuk memberikan pengalaman terbaik bagi setiap ukuran jendela.

Yang akan Anda butuhkan

Anda akan menggunakan Emulator yang dapat diubah ukurannya untuk codelab ini, yang memungkinkan Anda beralih antara berbagai jenis perangkat dan ukuran jendela.

Emulator yang dapat diubah ukurannya dengan opsi ponsel, perangkat foldable, tablet, dan desktop.

Jika Anda belum terbiasa dengan Compose, sebaiknya ikuti codelab Dasar-dasar Jetpack Compose sebelum menyelesaikan codelab ini.

Yang akan Anda bangun

  • Aplikasi klien email interaktif yang menggunakan praktik terbaik untuk desain yang dapat disesuaikan, navigasi Material yang berbeda, dan penggunaan ruang layar yang optimal.

Tampilan beberapa dukungan perangkat yang akan Anda capai dalam codelab ini

2. Memulai persiapan

Guna mendapatkan kode untuk codelab ini, clone repositori GitHub dari command line:

git clone https://github.com/android/codelab-android-compose.git
cd codelab-android-compose/AdaptiveUiCodelab

Atau, Anda dapat mendownload repositori sebagai file ZIP:

Sebaiknya Anda memulai dengan kode di cabang utama dan mengikuti codelab langkah demi langkah sesuai kemampuan Anda.

Membuka project di Android Studio

  1. Di jendela Welcome to Android Studio, pilih c01826594f360d94.pngOpen an Existing Project.
  2. Pilih folder <Download Location>/AdaptiveUiCodelab (pastikan Anda memilih direktori AdaptiveUiCodelab yang berisi build.gradle).
  3. Setelah Android Studio mengimpor project, uji apakah Anda dapat menjalankan cabang main.

Mempelajari kode awal

Kode cabang utama berisi paket ui. Anda akan menggunakan file berikut dalam paket tersebut:

  • MainActivity.kt - Aktivitas titik entri tempat Anda memulai aplikasi.
  • ReplyApp.kt - Berisi composable UI layar utama.
  • ReplyHomeViewModel.kt - Memberikan data dan status UI untuk konten aplikasi.
  • ReplyListContent.kt - Berisi composable untuk menyediakan daftar dan layar detail.

Anda akan fokus pada MainActivity.kt terlebih dahulu.

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )
        }
    }
}

Jika Anda menjalankan aplikasi ini di emulator yang dapat diubah ukurannya dan mencoba berbagai jenis perangkat, seperti ponsel atau tablet, UI hanya akan diperluas ke ruang tertentu, bukan memanfaatkan ruang layar atau memberikan ergonomi keterjangkauan.

Layar awal di ponsel

Tampilan awal yang direntangkan di tablet

Anda akan mengupdatenya untuk memanfaatkan ruang layar, meningkatkan kegunaan, dan meningkatkan pengalaman pengguna secara keseluruhan.

3. Membuat aplikasi mudah disesuaikan

Bagian ini memperkenalkan apa artinya membuat aplikasi mudah disesuaikan, dan komponen apa yang disediakan Material 3 untuk membuatnya lebih mudah. Hal ini juga mencakup jenis layar dan status yang akan Anda targetkan, termasuk ponsel, tablet, tablet besar, dan perangkat foldable.

Anda akan mulai dengan membahas dasar-dasar ukuran jendela, postur lipat, dan berbagai jenis opsi navigasi. Kemudian, Anda dapat menggunakan API ini di aplikasi agar lebih adaptif.

Ukuran jendela

Perangkat Android tersedia dalam berbagai bentuk dan ukuran, dari ponsel, perangkat foldable, hingga tablet dan perangkat ChromeOS. Untuk mendukung sebanyak mungkin ukuran jendela, UI Anda harus responsif dan adaptif. Untuk membantu Anda menemukan nilai minimum yang tepat untuk mengubah UI aplikasi, kami telah menentukan nilai titik henti sementara yang membantu mengklasifikasikan perangkat ke dalam class ukuran yang telah ditentukan sebelumnya (ringkas, sedang, dan diperluas), yang disebut class ukuran jendela. Class ini adalah sekumpulan titik henti sementara area pandang tidak berubah yang membantu Anda mendesain, mengembangkan, dan menguji tata letak aplikasi yang responsif dan adaptif.

Kategori ini dipilih secara khusus untuk menyeimbangkan kesederhanaan tata letak dengan fleksibilitas untuk mengoptimalkan aplikasi Anda untuk kasus yang unik. Class ukuran jendela selalu ditentukan oleh ruang layar yang tersedia untuk aplikasi, yang mungkin bukan seluruh layar fisik untuk multitasking atau segmentasi lainnya.

WindowWidthSizeClass untuk lebar yang ringkas, sedang, dan diperluas.

WindowHeightSizeClass untuk tinggi yang ringkas, sedang, dan diperluas.

Lebar dan tinggi diklasifikasikan secara terpisah, sehingga pada waktu tertentu aplikasi Anda akan memiliki dua class ukuran jendela, yaitu class untuk lebar dan class untuk tinggi. Lebar yang tersedia biasanya lebih penting daripada tinggi yang tersedia karena adanya scroll vertikal di mana saja, sehingga untuk kasus ini, Anda juga akan menggunakan class ukuran lebar.

Status lipat

Perangkat foldable menghadirkan lebih banyak situasi yang dapat disesuaikan oleh aplikasi Anda karena ukurannya yang bervariasi dan adanya engsel. Engsel dapat menutupi sebagian layar, sehingga area tersebut tidak cocok untuk menampilkan konten. Engsel juga dapat memisahkan, yang berarti ada dua tampilan fisik terpisah saat perangkat dibentangkan.

Postur perangkat foldable, datar dan setengah terbuka

Selain itu, pengguna dapat melihat layar bagian dalam sementara engsel terbuka sebagian, menghasilkan postur fisik yang berbeda berdasarkan orientasi lipatan: postur mode di atas meja (lipatan horizontal, ditampilkan di sebelah kanan pada gambar di atas) dan postur buku (lipatan vertikal).

Baca selengkapnya tentang engsel dan postur lipat.

Semua ini adalah hal yang perlu dipertimbangkan saat menerapkan tata letak adaptif yang mendukung perangkat foldable.

Mendapatkan informasi adaptif

Library adaptive Material3 menyediakan akses mudah ke informasi tentang jendela yang menjalankan aplikasi Anda.

  1. Tambahkan entri untuk artefak ini dan versinya ke file katalog versi:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0-beta01"

[libraries]
androidx-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "material3Adaptive" }
  1. Dalam file build modul aplikasi, tambahkan dependensi library baru, lalu lakukan sinkronisasi Gradle:

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive)
}

Sekarang, dalam cakupan composable apa pun, Anda dapat menggunakan currentWindowAdaptiveInfo() untuk mendapatkan objek WindowAdaptiveInfo yang berisi informasi seperti class ukuran jendela saat ini dan apakah perangkat dalam postur perangkat foldable seperti postur mode di atas meja.

Anda dapat mencobanya sekarang di MainActivity.

  1. Di onCreate() di dalam blok ReplyTheme, dapatkan info adaptif jendela dan tampilkan class ukuran dalam composable Text (Anda dapat menambahkannya setelah elemen ReplyApp()):

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )

            val adaptiveInfo = currentWindowAdaptiveInfo()
            val sizeClassText =
                "${adaptiveInfo.windowSizeClass.windowWidthSizeClass}\n" +
                "${adaptiveInfo.windowSizeClass.windowHeightSizeClass}"
            Text(
                text = sizeClassText,
                color = Color.Magenta,
                modifier = Modifier.padding(20.dp)
            )
        }
    }
}

Menjalankan aplikasi sekarang akan menampilkan class ukuran jendela yang dicetak di atas konten aplikasi. Jangan ragu untuk menjelajahi apa lagi yang disediakan di info adaptif jendela. Setelah itu, Anda dapat menghapus Text ini karena mencakup konten aplikasi dan tidak diperlukan untuk langkah berikutnya.

4. Navigasi dinamis

Sekarang Anda akan menyesuaikan navigasi aplikasi seiring perubahan status dan ukuran perangkat untuk meningkatkan keterjangkauan.

Keterjangkauan adalah kemampuan untuk menavigasi atau memulai interaksi dengan aplikasi tanpa memerlukan posisi tangan yang ekstrem atau mengubah penempatan tangan. Saat pengguna memegang ponsel, jari mereka biasanya berada di bagian bawah layar. Saat pengguna memegang perangkat foldable atau tablet yang terbuka, jari mereka biasanya dekat dengan bagian samping. Saat Anda mendesain aplikasi dan memutuskan tempat untuk menempatkan elemen UI interaktif dalam tata letak, pertimbangkan implikasi ergonomis dari berbagai region di layar.

  • Area mana yang nyaman untuk dijangkau sambil memegang perangkat?
  • Area mana yang hanya dapat dijangkau dengan menjulurkan jari, yang mungkin membuat tidak nyaman?
  • Area mana yang sulit dijangkau atau jauh dari tempat pengguna memegang perangkat?

Navigasi adalah hal pertama yang digunakan pengguna untuk berinteraksi dan berisi tindakan yang sangat penting terkait perjalanan penting pengguna, sehingga navigasi ini harus ditempatkan di area yang paling mudah dijangkau. Material menyediakan beberapa komponen yang membantu Anda menerapkan navigasi, bergantung pada class ukuran jendela perangkat.

Navigasi bawah

Navigasi bawah sangat cocok untuk ukuran ringkas, karena secara alami kita memegang perangkat dengan ibu jari yang dapat dengan mudah menjangkau semua titik sentuh navigasi bawah. Gunakan navigasi ini setiap kali Anda memiliki ukuran perangkat yang ringkas atau perangkat foldable dalam status lipatan yang ringkas.

Menu navigasi bawah dengan item

Untuk ukuran jendela dengan lebar sedang, kolom samping navigasi ideal untuk keterjangkauan karena ibu jari kita secara alami berada di sepanjang sisi perangkat. Anda juga dapat menggabungkan kolom samping navigasi dengan panel navigasi untuk menampilkan informasi selengkapnya.

Kolom samping navigasi dengan item

Panel navigasi menyediakan cara mudah guna melihat informasi mendetail untuk tab navigasi, dan mudah diakses saat Anda menggunakan tablet atau perangkat yang lebih besar. Ada dua jenis panel navigasi yang tersedia: panel navigasi modal dan panel navigasi permanen.

Panel navigasi modal

Anda dapat menggunakan panel navigasi modal untuk ponsel dan tablet berukuran ringkas hingga sedang karena dapat diperluas atau disembunyikan sebagai overlay pada konten. Kolom ini terkadang dapat digabungkan dengan kolom samping navigasi.

Panel navigasi modal dengan item

Panel navigasi permanen

Anda dapat menggunakan panel navigasi permanen untuk navigasi tetap di tablet, Chromebook, dan desktop berukuran besar.

Panel navigasi permanen dengan item

Menerapkan navigasi dinamis

Sekarang, Anda akan beralih antara jenis navigasi yang berbeda seiring perubahan status dan ukuran perangkat.

Saat ini, aplikasi selalu menampilkan NavigationBar di bawah konten layar, terlepas dari status perangkat. Sebagai gantinya, Anda dapat menggunakan komponen Material NavigationSuiteScaffold untuk otomatis beralih antar-komponen navigasi yang berbeda berdasarkan informasi seperti class ukuran jendela saat ini.

  1. Tambahkan dependensi Gradle untuk mendapatkan komponen ini dengan mengupdate katalog versi dan skrip build aplikasi, lalu jalankan sinkronisasi Gradle:

gradle/libs.versions.toml

[versions]
material3AdaptiveNavSuite = "1.3.0-beta01"

[libraries]
androidx-material3-adaptive-navigation-suite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite", version.ref = "material3AdaptiveNavSuite" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.navigation.suite)
}
  1. Temukan fungsi composable ReplyNavigationWrapper() di ReplyApp.kt dan ganti Column beserta kontennya dengan NavigationSuiteScaffold:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    NavigationSuiteScaffold(
        navigationSuiteItems = {
            ReplyDestination.entries.forEach {
                item(
                    selected = it == selectedDestination,
                    onClick = { /*TODO update selection*/ },
                    icon = {
                        Icon(
                            imageVector = it.icon,
                            contentDescription = stringResource(it.labelRes)
                        )
                    },
                    label = {
                        Text(text = stringResource(it.labelRes))
                    },
                )
            }
        }
    ) {
        content()
    }
}

Argumen navigationSuiteItems adalah blok yang memungkinkan Anda menambahkan item menggunakan fungsi item(), mirip dengan menambahkan item dalam LazyColumn. Di dalam lambda terakhir, kode ini memanggil content() yang diteruskan sebagai argumen ke ReplyNavigationWrapperUI().

Jalankan aplikasi di emulator dan coba ubah ukuran antara ponsel, perangkat foldable, dan tablet, dan Anda akan melihat menu navigasi berubah menjadi kolom samping navigasi dan kembali.

Pada jendela yang sangat lebar, seperti di tablet dalam mode lanskap, Anda mungkin ingin menampilkan panel navigasi permanen. NavigationSuiteScaffold mendukung ditampilkannya panel samping permanen, meskipun tidak ditampilkan dalam nilai WindowWidthSizeClass saat ini. Namun, Anda dapat melakukannya dengan sedikit perubahan.

  1. Tambahkan kode berikut tepat sebelum panggilan ke NavigationSuiteScaffold:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    val windowSize = with(LocalDensity.current) {
        currentWindowSize().toSize().toDpSize()
    }
    val layoutType = if (windowSize.width >= 1200.dp) {
        NavigationSuiteType.NavigationDrawer
    } else {
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(
            currentWindowAdaptiveInfo()
        )
    }

    NavigationSuiteScaffold(
        layoutType = layoutType,
        ...
    ) {
        content()
    }
}

Kode ini pertama-tama mendapatkan ukuran jendela dan mengonversinya menjadi unit DP menggunakan currentWindowSize() dan LocalDensity.current, lalu membandingkan lebar jendela untuk menentukan jenis tata letak UI navigasi. Jika lebar jendela minimal 1200.dp, maka akan digunakan NavigationSuiteType.NavigationDrawer. Jika tidak, cara ini akan kembali ke penghitungan default.

Saat Anda menjalankan aplikasi lagi di emulator yang dapat diubah ukurannya dan mencoba jenis yang berbeda, perhatikan bahwa setiap kali konfigurasi layar berubah atau Anda membuka perangkat foldable, navigasi akan berubah ke jenis yang sesuai untuk ukuran tersebut.

Menampilkan perubahan kemampuan adaptasi untuk berbagai ukuran perangkat.

Selamat, Anda telah mempelajari berbagai jenis navigasi untuk mendukung berbagai jenis ukuran dan status jendela.

Di bagian berikutnya, Anda akan mempelajari cara memanfaatkan area layar yang tersisa, bukan meregangkan item daftar yang sama dari tepi ke tepi.

5. Penggunaan ruang layar

Tidak masalah apakah Anda menjalankan aplikasi di tablet kecil, perangkat yang dibentangkan, atau tablet besar, layar akan direntangkan untuk mengisi ruang yang tersisa. Anda ingin memastikan bahwa Anda dapat memanfaatkan ruang layar tersebut untuk menampilkan info selengkapnya, misalnya untuk aplikasi ini, menampilkan email dan rangkaian pesan kepada pengguna di halaman yang sama.

Material 3 menentukan tiga tata letak kanonis yang masing-masing memiliki konfigurasi untuk class ukuran jendela yang ringkas, sedang, dan diperluas. Tata letak kanonis Daftar Detail sangat cocok untuk kasus penggunaan ini, dan tersedia di compose sebagai ListDetailPaneScaffold.

  1. Dapatkan komponen ini dengan menambahkan dependensi berikut dan menjalankan sinkronisasi Gradle:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0-beta01"

[libraries]
androidx-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout", version.ref = "material3Adaptive" }
androidx-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation", version.ref = "material3Adaptive" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.layout)
    implementation(libs.androidx.material3.adaptive.navigation)
}
  1. Temukan fungsi composable ReplyAppContent() di ReplyApp.kt, yang saat ini hanya menampilkan panel daftar dengan memanggil ReplyListPane(). Ganti implementasi ini dengan ListDetailPaneScaffold dengan menyisipkan kode berikut. Karena ini adalah API eksperimental, Anda juga akan menambahkan anotasi @OptIn pada fungsi ReplyAppContent():

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            ReplyListPane(replyHomeUIState, onEmailClick)
        },
        detailPane = {
            ReplyDetailPane(replyHomeUIState.emails.first())
        }
    )
}

Kode ini terlebih dahulu membuat navigator menggunakan rememberListDetailPaneNavigator(). Navigator memberikan beberapa kontrol atas panel mana yang ditampilkan dan konten apa yang harus ditampilkan di panel tersebut, yang akan didemonstrasikan nanti.

ListDetailPaneScaffold akan menampilkan dua panel saat class ukuran lebar jendela diperluas. Jika tidak, panel ini akan menampilkan satu panel atau panel lainnya berdasarkan nilai yang diberikan untuk dua parameter: perintah scaffold dan nilai scaffold. Untuk mendapatkan perilaku default, kode ini menggunakan perintah scaffold dan nilai scaffold yang diberikan oleh navigator.

Parameter yang diperlukan lainnya adalah lambda composable untuk panel. ReplyListPane() dan ReplyDetailPane() (terdapat di ReplyListContent.kt) masing-masing digunakan untuk mengisi peran panel daftar dan detail. ReplyDetailPane() mengharapkan argumen email, jadi untuk saat ini kode ini menggunakan email pertama dari daftar email di ReplyHomeUIState.

Jalankan aplikasi dan alihkan tampilan emulator ke perangkat foldable atau tablet (Anda mungkin juga harus mengubah orientasi) untuk melihat tata letak dua panel. Tampilan ini terlihat jauh lebih baik dari sebelumnya!

Sekarang, mari kita bahas beberapa perilaku yang diinginkan dari layar ini. Saat pengguna mengetuk email di panel daftar, email tersebut akan ditampilkan di panel detail bersama dengan semua balasan. Saat ini, aplikasi tidak melacak email mana yang dipilih, dan mengetuk item tidak akan melakukan apa pun. Tempat terbaik untuk menyimpan informasi ini adalah dengan status UI lainnya di ReplyHomeUIState.

  1. Buka ReplyHomeViewModel.kt dan cari class data ReplyHomeUIState. Tambahkan properti untuk email yang dipilih, dengan nilai default null:

ReplyHomeViewModel.kt

data class ReplyHomeUIState(
    val emails : List<Email> = emptyList(),
    val selectedEmail: Email? = null,
    val loading: Boolean = false,
    val error: String? = null
)
  1. Dalam file yang sama, ReplyHomeViewModel memiliki fungsi setSelectedEmail() yang dipanggil saat pengguna mengetuk item daftar. Ubah fungsi ini untuk menyalin status UI dan mencatat email yang dipilih:

ReplyHomeViewModel.kt

fun setSelectedEmail(email: Email) {
    _uiState.update {
        it.copy(selectedEmail = email)
    }
}

Hal yang perlu dipertimbangkan adalah apa yang terjadi sebelum pengguna mengetuk item apa pun dan email yang dipilih adalah null. Apa yang akan ditampilkan di panel detail? Ada beberapa cara untuk menangani kasus ini, seperti menampilkan item pertama dalam daftar secara default.

  1. Dalam file yang sama, ubah fungsi observeEmails(). Saat daftar email dimuat, jika status UI sebelumnya tidak memiliki email yang dipilih, setel ke item pertama:

ReplyHomeViewModel.kt

private fun observeEmails() {
    viewModelScope.launch {
        emailsRepository.getAllEmails()
            .catch { ex ->
                _uiState.value = ReplyHomeUIState(error = ex.message)
            }
            .collect { emails ->
                val currentSelection = _uiState.value.selectedEmail
                _uiState.value = ReplyHomeUIState(
                    emails = emails,
                    selectedEmail = currentSelection ?: emails.first()
                )
            }
    }
}
  1. Kembali ke ReplyApp.kt dan gunakan email yang dipilih, jika tersedia, untuk mengisi konten panel detail:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    detailPane = {
        if (replyHomeUIState.selectedEmail != null) {
            ReplyDetailPane(replyHomeUIState.selectedEmail)
        }
    }
)

Jalankan lagi aplikasi dan ubah emulator ke ukuran tablet, dan perhatikan bahwa mengetuk item daftar akan memperbarui konten panel detail.

Hal ini akan berfungsi dengan baik jika kedua panel terlihat, tetapi jika jendela hanya memiliki ruang untuk menampilkan satu panel, sepertinya tidak ada yang terjadi saat Anda mengetuk item. Coba alihkan tampilan emulator ke ponsel, atau perangkat foldable dalam mode potret, dan perhatikan bahwa hanya panel daftar yang terlihat bahkan setelah mengetuk item. Hal ini karena meskipun email yang dipilih telah diupdate, ListDetailPaneScaffold akan tetap mempertahankan fokus pada panel daftar dalam konfigurasi ini.

  1. Untuk memperbaikinya, sisipkan kode berikut saat lambda diteruskan ke ReplyListPane:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    listPane = {
        ReplyListPane(
            replyHomeUIState = replyHomeUIState,
            onEmailClick = { email ->
                onEmailClick(email)
                navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
            }
        )
    },
    // ...
)

Lambda ini menggunakan navigator yang dibuat sebelumnya untuk menambahkan perilaku tambahan saat item diklik. Fungsi ini akan memanggil lambda asli yang diteruskan ke fungsi ini, lalu juga memanggil navigator.navigateTo() yang menentukan panel mana yang akan ditampilkan. Setiap panel dalam scaffold memiliki peran yang terkait dengannya, dan ListDetailPaneScaffoldRole.Detail untuk panel detail. Pada jendela yang lebih kecil, tindakan ini akan memberikan tampilan bahwa aplikasi telah menavigasi ke depan.

Aplikasi juga perlu menangani apa yang terjadi saat pengguna menekan tombol kembali dari panel detail, dan perilaku ini akan berbeda bergantung pada apakah ada satu atau dua panel yang terlihat.

  1. Dukung navigasi kembali dengan menambahkan kode berikut.

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                ReplyListPane(
                    replyHomeUIState = replyHomeUIState,
                    onEmailClick = { email ->
                        onEmailClick(email)
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
                    }
                )
            }
        },
        detailPane = {
            AnimatedPane {
                if (replyHomeUIState.selectedEmail != null) {
                    ReplyDetailPane(replyHomeUIState.selectedEmail)
                }
            }
        }
    )
}

Navigator mengetahui status lengkap ListDetailPaneScaffold, apakah navigasi kembali dapat dilakukan, dan apa yang harus dilakukan dalam semua skenario ini. Kode ini membuat BackHandler yang diaktifkan setiap kali navigator dapat menavigasi kembali, dan di dalam lambda memanggil navigateBack(). Selain itu, untuk membuat transisi antarpanel lebih lancar, setiap panel digabungkan dalam composable AnimatedPane().

Jalankan kembali aplikasi pada emulator yang dapat diubah ukurannya untuk semua jenis perangkat dan perhatikan bahwa setiap kali konfigurasi layar berubah, atau Anda membuka perangkat foldable, navigasi dan konten layar akan berubah secara dinamis sebagai respons terhadap perubahan status perangkat. Selain itu, coba ketuk email di panel daftar dan lihat perilaku tata letak di berbagai layar, dengan menampilkan kedua panel secara berdampingan atau menganimasikan di antara keduanya dengan lancar.

Menampilkan perubahan kemampuan adaptasi untuk berbagai ukuran perangkat.

Selamat, Anda telah berhasil membuat aplikasi yang dapat disesuaikan untuk semua jenis status dan ukuran perangkat. Coba dan jalankan aplikasi ini di perangkat foldable, tablet, atau perangkat seluler lainnya.

6. Selamat

Selamat! Anda telah berhasil menyelesaikan codelab ini dan mempelajari cara membuat aplikasi menjadi adaptif dengan Jetpack Compose.

Anda telah mempelajari cara memeriksa ukuran dan status lipat perangkat, serta mengupdate UI, navigasi, dan fungsi lainnya dari aplikasi Anda. Anda juga telah mempelajari bagaimana kemampuan adaptasi meningkatkan keterjangkauan dan meningkatkan pengalaman pengguna.

Apa langkah selanjutnya?

Lihat codelab lainnya di pembelajaran Compose.

Aplikasi contoh

  • Contoh compose adalah kumpulan banyak aplikasi yang menggabungkan praktik terbaik yang dijelaskan dalam codelab.

Dokumen referensi