1. Sebelum memulai
Hingga saat ini, aplikasi yang telah Anda kerjakan terdiri dari satu layar. Namun, banyak aplikasi yang Anda gunakan mungkin memiliki beberapa layar yang dapat dinavigasi. Misalnya, aplikasi Setelan memiliki banyak halaman konten yang tersebar di berbagai layar.
|
|
|
Dalam pengembangan Android modern, aplikasi multilayar dibuat menggunakan komponen Jetpack Navigation. Komponen Navigation Compose memungkinkan Anda membangun aplikasi multilayar di Compose dengan mudah menggunakan pendekatan deklaratif, seperti membangun antarmuka pengguna. Codelab ini memperkenalkan dasar-dasar komponen Navigation Compose, cara membuat AppBar responsif, dan cara mengirim data dari aplikasi Anda ke aplikasi lain menggunakan intent, sekaligus menunjukkan praktik terbaik dalam aplikasi yang makin kompleks.
Prasyarat
- Pemahaman tentang bahasa Kotlin, termasuk jenis fungsi, lambda, dan fungsi cakupan
- Pemahaman tentang tata letak
RowdanColumndasar di Compose
Yang akan Anda pelajari
- Membuat composable
NavHostuntuk menentukan rute dan layar di aplikasi Anda. - Beralih antarlayar menggunakan
NavHostController. - Memanipulasi data sebelumnya untuk beralih ke layar sebelumnya.
- Menggunakan intent untuk berbagi data dengan aplikasi lain.
- Menyesuaikan AppBar, termasuk judul dan tombol kembali.
Yang akan Anda build
- Anda akan mengimplementasikan navigasi di aplikasi multilayar.
Yang Anda butuhkan
- Versi terbaru Android Studio
- Koneksi internet untuk mendownload kode awal
2. Mendownload kode awal
Untuk memulai, download kode awal:
Atau, Anda dapat membuat clone repositori GitHub untuk kode tersebut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-cupcake.git $ cd basic-android-kotlin-compose-training-cupcake $ git checkout starter
Jika Anda ingin melihat kode awal untuk codelab ini, lihat kode tersebut di GitHub.
3. Panduan aplikasi
Aplikasi Cupcake sedikit berbeda dari aplikasi yang telah Anda tangani sejauh ini. Aplikasi ini tidak menampilkan semua konten di satu layar, melainkan di empat layar terpisah. Selain itu, pengguna dapat menjelajahi setiap layar sambil memesan cupcake. Jika menjalankan aplikasi ini, Anda tidak akan melihat apa pun dan tidak akan dapat beralih antar-layar karena komponen navigasi belum ditambahkan ke kode aplikasi. Namun, Anda masih dapat memeriksa pratinjau composable untuk setiap layar dan mencocokkannya dengan layar akhir aplikasi di bawah.
Layar untuk memulai pesanan
Layar pertama menampilkan tiga tombol kepada pengguna yang sesuai dengan jumlah cupcake yang dapat dipesan.
|
|
Dalam kode, layar ini diwakili oleh composable StartOrderScreen di StartOrderScreen.kt.
Layar terdiri dari satu kolom, dengan gambar dan teks, beserta tiga tombol khusus untuk memesan cupcake dalam jumlah berbeda. Tombol kustom diimplementasikan oleh composable SelectQuantityButton, yang juga ada di StartOrderScreen.kt.
Layar untuk memilih rasa
Setelah memilih jumlah, aplikasi akan meminta pengguna untuk memilih rasa cupcake. Aplikasi menggunakan tombol pilihan untuk menampilkan berbagai opsi. Pengguna dapat memilih satu rasa dari beberapa pilihan kemungkinan rasa.
|
|
Daftar kemungkinan rasa disimpan sebagai daftar ID resource string di data.DataSource.kt.
Layar untuk memilih tanggal pengambilan
Setelah memilih rasa, aplikasi menampilkan serangkaian tombol pilihan lain kepada pengguna untuk memilih tanggal pengambilan. Opsi pengambilan berasal dari daftar yang ditampilkan oleh fungsi pickupOptions() di OrderViewModel.
|
|
Layar Pilih Rasa dan layar Pilih Tanggal Pengambilan diwakili oleh composable yang sama, SelectOptionScreen di SelectOptionScreen.kt. Mengapa menggunakan composable yang sama? Tata letak layar ini sama persis. Satu-satunya perbedaan adalah data, tetapi Anda dapat menggunakan composable yang sama untuk menampilkan layar untuk memilih rasa dan tanggal pengambilan.
Layar Ringkasan Pesanan
Setelah memilih tanggal pengambilan, aplikasi akan menampilkan layar Ringkasan Pesanan tempat pengguna dapat meninjau dan menyelesaikan pesanan.
|
|
Layar ini diimplementasikan oleh composable OrderSummaryScreen di SummaryScreen.kt.
Tata letak terdiri dari Column yang berisi semua informasi pesanan pengguna, composable Text untuk subtotal, dan tombol untuk mengirimkan pesanan ke aplikasi lain atau membatalkan pesanan dan kembali ke layar pertama.
Jika pengguna memilih untuk mengirim pesanan ke aplikasi lain, aplikasi Cupcake akan menampilkan Android ShareSheet yang menunjukkan opsi berbagi yang berbeda.

Status saat ini dari aplikasi disimpan di data.OrderUiState.kt. Class data OrderUiState berisi properti untuk menyimpan pilihan pengguna dari setiap layar.
Layar aplikasi akan ditampilkan dalam composable CupcakeApp. Namun, dalam project permulaan, aplikasi hanya menampilkan layar pertama. Saat ini, Anda tidak dapat membuka semua layar aplikasi, tetapi jangan khawatir, untuk itulah Anda ada di sini. Anda akan mempelajari cara menentukan rute navigasi, menyiapkan composable NavHost untuk melakukan navigasi antarlayar—yang juga dikenal sebagai tujuan—melakukan intent untuk berintegrasi dengan komponen UI sistem seperti layar berbagi, dan membuat AppBar merespons perubahan navigasi.
Composable yang dapat digunakan kembali
Jika sesuai, aplikasi contoh dalam kursus ini dirancang untuk menerapkan praktik terbaik. Begitu pula dengan aplikasi Cupcake. Dalam paket ui.components, Anda akan melihat file bernama CommonUi.kt yang berisi composable FormattedPriceLabel. Beberapa layar di aplikasi menggunakan composable ini untuk memformat harga pesanan secara konsisten. Daripada membuat duplikat composable Text yang sama dengan format dan pengubah yang sama, Anda dapat menentukan FormattedPriceLabel satu kali, lalu menggunakannya kembali sebanyak yang diperlukan untuk layar lainnya.
Layar rasa dan tanggal pengambilan menggunakan composable SelectOptionScreen, yang juga dapat digunakan kembali. Composable ini mengambil parameter bernama options dari jenis List<String> yang mewakili opsi untuk ditampilkan. Opsi muncul di Row, yang terdiri dari composable RadioButton dan composable Text yang berisi setiap string. Column mengelilingi seluruh tata letak dan juga berisi composable Text untuk menampilkan harga berformat, tombol Cancel, dan tombol Next.
4. Menentukan rute dan membuat NavHostController
Bagian dari Komponen Navigasi
Komponen Navigasi memiliki tiga bagian utama:
- NavController: Bertanggung jawab untuk menavigasi di antara tujuan—yaitu layar di aplikasi Anda.
- NavGraph: Memetakan tujuan composable untuk dinavigasi.
- NavHost: Composable yang bertindak sebagai container untuk menampilkan tujuan NavGraph saat ini.
Dalam codelab ini, Anda akan berfokus pada NavController dan NavHost. Dalam NavHost, Anda akan menentukan tujuan untuk NavGraph aplikasi Cupcake.
Menentukan rute untuk tujuan di aplikasi Anda
Salah satu konsep dasar navigasi di aplikasi Compose adalah rute. Rute adalah string yang sesuai dengan tujuan. Ide ini mirip dengan konsep URL. Sama seperti URL berbeda yang dipetakan ke halaman yang berbeda di situs, rute adalah string yang dipetakan ke tujuan dan berfungsi sebagai ID uniknya. Tujuan biasanya berupa Composable tunggal atau grup Composable yang sesuai dengan apa yang dilihat pengguna. Aplikasi Cupcake memerlukan tujuan untuk layar mulai pesanan, layar rasa, layar tanggal pengambilan, dan layar ringkasan pesanan.
Aplikasi memiliki jumlah layar yang terbatas, sehingga rute juga terbatas. Anda dapat menentukan rute aplikasi menggunakan class enum. Class Enum di Kotlin memiliki properti nama yang menampilkan string dengan nama properti.
Anda akan mulai dengan menentukan empat rute aplikasi Cupcake.
Start: Pilih jumlah cupcake dari salah satu dari tiga tombol.Flavor: Pilih rasa dari daftar pilihan.Pickup: Pilih tanggal pengambilan dari daftar pilihan.Summary: Tinjau pilihan, lalu kirim atau batalkan pesanan.
Tambahkan class enum untuk menentukan rute.
- Di
CupcakeScreen.kt, di atas composableCupcakeAppBar, tambahkan class enum bernamaCupcakeScreen.
enum class CupcakeScreen() {
}
- Tambahkan empat kasus ke class enum:
Start,Flavor,Pickup, danSummary.
enum class CupcakeScreen() {
Start,
Flavor,
Pickup,
Summary
}
Menambahkan NavHost ke aplikasi Anda
NavHost adalah Composable yang menampilkan tujuan composable lainnya, berdasarkan rute tertentu. Misalnya, jika rutenya adalah Flavor, NavHost akan menampilkan layar untuk memilih rasa cupcake. Jika rutenya adalah Summary, aplikasi akan menampilkan layar ringkasan.
Sintaksis untuk NavHost sama seperti Composable lainnya.

Ada dua parameter penting.
navController: Instance dari classNavHostController. Anda dapat menggunakan objek ini untuk berpindah antarlayar, misalnya, dengan memanggil metodenavigate()untuk menuju ke tujuan lain. Anda dapat memperolehNavHostControllerdengan memanggilrememberNavController()dari fungsi composable.startDestination: Rute string yang menentukan tujuan yang ditampilkan secara default saat aplikasi pertama kali menampilkanNavHost. Untuk aplikasi Cupcake, seharusnya ini adalah ruteStart.
Seperti composable lainnya, NavHost juga menggunakan parameter modifier.
Anda akan menambahkan NavHost ke composable CupcakeApp di CupcakeScreen.kt. Pertama, Anda memerlukan referensi untuk pengontrol navigasi. Anda dapat menggunakan pengontrol navigasi di NavHost yang Anda tambahkan sekarang dan AppBar yang akan Anda tambahkan pada langkah berikutnya. Oleh karena itu, Anda harus mendeklarasikan variabel dalam composable CupcakeApp().
- Buka
CupcakeScreen.kt. - Dalam
Scaffold, di bawah variabeluiState, tambahkan composableNavHost.
import androidx.navigation.compose.NavHost
Scaffold(
...
) { innerPadding ->
val uiState by viewModel.uiState.collectAsState()
NavHost()
}
- Teruskan variabel
navControlleruntuk parameternavControllerdanCupcakeScreen.Start.nameuntuk parameterstartDestination. Teruskan pengubah yang diteruskan keCupcakeApp()untuk parameter pengubah. Teruskan lambda akhir kosong untuk parameter akhir.
import androidx.compose.foundation.layout.padding
NavHost(
navController = navController,
startDestination = CupcakeScreen.Start.name,
modifier = Modifier.padding(innerPadding)
) {
}
Menangani rute di NavHost Anda
Seperti composable lainnya, NavHost menggunakan jenis fungsi untuk kontennya.

Dalam fungsi konten NavHost, Anda memanggil fungsi composable(). Fungsi composable() memerlukan dua parameter.
route: String yang sesuai dengan nama rute. Ini dapat berupa string unik apa pun. Anda akan menggunakan properti nama konstanta enumCupcakeScreen.content: Di sini Anda dapat memanggil composable yang ingin ditampilkan untuk rute yang diberikan.
Anda akan memanggil fungsi composable() satu kali untuk masing-masing dari keempat rute.
- Panggil fungsi
composable(), dengan meneruskanCupcakeScreen.Start.nameuntukroute.
import androidx.navigation.compose.composable
NavHost(
navController = navController,
startDestination = CupcakeScreen.Start.name,
modifier = Modifier.padding(innerPadding)
) {
composable(route = CupcakeScreen.Start.name) {
}
}
- Dalam lambda terakhir, panggil composable
StartOrderScreen, dengan meneruskanquantityOptionsuntuk propertiquantityOptions. Untuk kartumodifierdiModifier.fillMaxSize().padding(dimensionResource(R.dimen.padding_medium))
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.res.dimensionResource
import com.example.cupcake.ui.StartOrderScreen
import com.example.cupcake.data.DataSource
NavHost(
navController = navController,
startDestination = CupcakeScreen.Start.name,
modifier = Modifier.padding(innerPadding)
) {
composable(route = CupcakeScreen.Start.name) {
StartOrderScreen(
quantityOptions = DataSource.quantityOptions,
modifier = Modifier
.fillMaxSize()
.padding(dimensionResource(R.dimen.padding_medium))
)
}
}
- Di bawah panggilan pertama ke
composable(), panggilcomposable()lagi, dengan meneruskanCupcakeScreen.Flavor.nameuntukroute.
composable(route = CupcakeScreen.Flavor.name) {
}
- Dalam lambda terakhir, dapatkan referensi ke
LocalContext.current, lalu simpan dalam variabel bernamacontext.Contextadalah class abstrak yang implementasinya disediakan oleh sistem Android. Class ini memungkinkan akses ke resource dan class khusus aplikasi, serta up-call untuk operasi tingkat aplikasi seperti aktivitas peluncuran, dll. Anda dapat menggunakan variabel ini untuk mendapatkan string dari daftar ID resource dalam model tampilan untuk menampilkan daftar rasa.
import androidx.compose.ui.platform.LocalContext
composable(route = CupcakeScreen.Flavor.name) {
val context = LocalContext.current
}
- Panggil composable
SelectOptionScreen.
composable(route = CupcakeScreen.Flavor.name) {
val context = LocalContext.current
SelectOptionScreen(
)
}
- Layar rasa perlu menampilkan dan mengupdate subtotal saat pengguna memilih rasa. Teruskan
uiState.priceuntuk parametersubtotal.
composable(route = CupcakeScreen.Flavor.name) {
val context = LocalContext.current
SelectOptionScreen(
subtotal = uiState.price
)
}
- Layar rasa mendapatkan daftar rasa dari resource string aplikasi. Ubah daftar ID resource menjadi daftar string menggunakan fungsi
map()dan memanggilcontext.resources.getString(id)untuk setiap rasa.
import com.example.cupcake.ui.SelectOptionScreen
composable(route = CupcakeScreen.Flavor.name) {
val context = LocalContext.current
SelectOptionScreen(
subtotal = uiState.price,
options = DataSource.flavors.map { id -> context.resources.getString(id) }
)
}
- Untuk parameter
onSelectionChanged, teruskan ekspresi lambda yang memanggilsetFlavor()pada model tampilan, dengan meneruskanit(argumen yang diteruskan keonSelectionChanged()). Untuk parametermodifier, teruskanModifier.fillMaxHeight().
import androidx.compose.foundation.layout.fillMaxHeight
import com.example.cupcake.data.DataSource.flavors
composable(route = CupcakeScreen.Flavor.name) {
val context = LocalContext.current
SelectOptionScreen(
subtotal = uiState.price,
options = DataSource.flavors.map { id -> context.resources.getString(id) },
onSelectionChanged = { viewModel.setFlavor(it) },
modifier = Modifier.fillMaxHeight()
)
}
Layar tanggal pengambilan mirip dengan layar rasa. Satu-satunya perbedaan adalah data yang diteruskan ke composable SelectOptionScreen.
- Panggil lagi fungsi
composable()dengan meneruskanCupcakeScreen.Pickup.nameuntuk parameterroute.
composable(route = CupcakeScreen.Pickup.name) {
}
- Di lambda terakhir, panggil composable
SelectOptionScreendan teruskanuiState.priceuntuksubtotal, seperti sebelumnya. TeruskanuiState.pickupOptionsuntuk parameteroptionsdan ekspresi lambda yang memanggilsetDate()diviewModeluntuk parameteronSelectionChanged. Untuk parametermodifier, teruskanModifier.fillMaxHeight().
SelectOptionScreen(
subtotal = uiState.price,
options = uiState.pickupOptions,
onSelectionChanged = { viewModel.setDate(it) },
modifier = Modifier.fillMaxHeight()
)
- Panggil
composable()sekali lagi, dengan meneruskanCupcakeScreen.Summary.nameuntukroute.
composable(route = CupcakeScreen.Summary.name) {
}
- Di lambda terakhir, panggil composable
OrderSummaryScreen(), dengan meneruskan variabeluiStateuntuk parameterorderUiState. Untuk parametermodifier, teruskanModifier.fillMaxHeight().
import com.example.cupcake.ui.OrderSummaryScreen
composable(route = CupcakeScreen.Summary.name) {
OrderSummaryScreen(
orderUiState = uiState,
modifier = Modifier.fillMaxHeight()
)
}
Seperti itulah cara menyiapkan NavHost. Di bagian berikutnya, Anda akan membuat aplikasi mengubah rute dan menavigasi antarlayar saat pengguna mengetuk setiap tombol.
5. Menavigasi antar-rute
Setelah Anda menentukan rute dan memetakannya ke composable di NavHost, kini saatnya menavigasi antarlayar. NavHostController, yang merupakan properti navController dari panggilan rememberNavController(), bertanggung jawab untuk navigasi di antara rute. Namun, perhatikan bahwa properti ini ditentukan dalam composable CupcakeApp. Anda memerlukan cara untuk mengaksesnya dari berbagai layar di aplikasi Anda.
Mudah, kan? Cukup teruskan navController sebagai parameter ke setiap composable.
Meskipun pendekatan ini berhasil, ini bukan cara yang ideal untuk merancang aplikasi. Manfaat menggunakan NavHost untuk menangani navigasi aplikasi Anda adalah logika navigasi disimpan terpisah dari masing-masing UI. Opsi ini menghindari beberapa kelemahan utama dalam meneruskan navController sebagai parameter.
- Logika navigasi disimpan di satu tempat, yang bisa membuat kode Anda lebih mudah dikelola dan mencegah bug dengan tidak sengaja memberikan kontrol navigasi bebas pada setiap layar di aplikasi.
- Di aplikasi yang perlu berfungsi pada berbagai faktor bentuk (seperti ponsel mode potret, ponsel foldable, atau tablet layar besar), tombol mungkin atau mungkin tidak memicu navigasi, bergantung pada tata letak aplikasi. Masing-masing layar harus bersifat mandiri dan tidak perlu mengetahui layar lain di aplikasi.
Sebagai gantinya, pendekatan kita adalah meneruskan jenis fungsi ke setiap composable untuk apa yang akan terjadi saat pengguna mengklik tombol. Dengan demikian, composable dan setiap composable turunannya memutuskan kapan harus memanggil fungsi. Namun, logika navigasi tidak ditampilkan ke setiap layar di aplikasi Anda. Semua perilaku navigasi ditangani di NavHost.
Menambahkan pengendali tombol ke StartOrderScreen
Anda akan memulai dengan menambahkan parameter jenis fungsi yang dipanggil saat salah satu tombol kuantitas ditekan di layar pertama. Fungsi ini diteruskan ke dalam composable StartOrderScreen dan bertanggung jawab untuk memperbarui model tampilan dan membuka layar berikutnya.
- Buka
StartOrderScreen.kt. - Di bawah parameter
quantityOptions, dan sebelum parameter pengubah, tambahkan parameter bernamaonNextButtonClickeddari jenis() -> Unit.
@Composable
fun StartOrderScreen(
quantityOptions: List<Pair<Int, Int>>,
onNextButtonClicked: () -> Unit,
modifier: Modifier = Modifier
){
...
}
- Setelah composable
StartOrderScreenmendapatkan nilai untukonNextButtonClicked, cariStartOrderPreviewlalu teruskan isi lambda kosong ke parameteronNextButtonClicked.
@Preview
@Composable
fun StartOrderPreview() {
CupcakeTheme {
StartOrderScreen(
quantityOptions = DataSource.quantityOptions,
onNextButtonClicked = {},
modifier = Modifier
.fillMaxSize()
.padding(dimensionResource(R.dimen.padding_medium))
)
}
}
Setiap tombol sesuai dengan jumlah cupcake yang berbeda. Anda akan memerlukan informasi ini agar fungsi yang diteruskan untuk onNextButtonClicked dapat mengupdate model tampilan.
- Ubah jenis parameter
onNextButtonClickeduntuk mengambil parameterInt.
onNextButtonClicked: (Int) -> Unit,
Agar Int diteruskan saat memanggil onNextButtonClicked(), lihat jenis parameter quantityOptions.
Jenisnya adalah List<Pair<Int, Int>> atau daftar Pair<Int, Int>. Anda mungkin tidak mengenal jenis Pair, tetapi sama seperti namanya, ini adalah sepasang nilai. Pair menggunakan dua parameter jenis generik. Dalam hal ini, keduanya adalah jenis Int.

Setiap item dalam pasangan diakses oleh properti pertama atau properti kedua. Untuk parameter quantityOptions composable StartOrderScreen, Int pertama adalah ID resource untuk string yang ditampilkan di setiap tombol. Int kedua adalah jumlah cupcake sebenarnya.
Kita akan meneruskan properti kedua dari pasangan yang dipilih saat memanggil fungsi onNextButtonClicked().
- Temukan ekspresi lambda kosong untuk parameter
onClickdariSelectQuantityButton.
quantityOptions.forEach { item ->
SelectQuantityButton(
labelResourceId = item.first,
onClick = {}
)
}
- Dalam ekspresi lambda, panggil
onNextButtonClickeddengan meneruskanitem.second, yaitu jumlah cupcake.
quantityOptions.forEach { item ->
SelectQuantityButton(
labelResourceId = item.first,
onClick = { onNextButtonClicked(item.second) }
)
}
Menambahkan pengendali tombol ke SelectOptionScreen
- Di bawah parameter
onSelectionChangedcomposableSelectOptionScreendiSelectOptionScreen.kt, tambahkan parameter bernamaonCancelButtonClickeddari jenis() -> Unitdengan nilai default{}.
@Composable
fun SelectOptionScreen(
subtotal: String,
options: List<String>,
onSelectionChanged: (String) -> Unit = {},
onCancelButtonClicked: () -> Unit = {},
modifier: Modifier = Modifier
)
- Di bawah parameter
onCancelButtonClicked, tambahkan parameter lain dari jenis() -> UnitbernamaonNextButtonClickeddengan nilai default{}.
@Composable
fun SelectOptionScreen(
subtotal: String,
options: List<String>,
onSelectionChanged: (String) -> Unit = {},
onCancelButtonClicked: () -> Unit = {},
onNextButtonClicked: () -> Unit = {},
modifier: Modifier = Modifier
)
- Teruskan
onCancelButtonClickeduntuk parameteronClicktombol batal.
OutlinedButton(
modifier = Modifier.weight(1f),
onClick = onCancelButtonClicked
) {
Text(stringResource(R.string.cancel))
}
- Teruskan
onNextButtonClickeduntuk parameteronClicktombol berikutnya.
Button(
modifier = Modifier.weight(1f),
enabled = selectedValue.isNotEmpty(),
onClick = onNextButtonClicked
) {
Text(stringResource(R.string.next))
}
Menambahkan pengendali tombol ke SummaryScreen
Terakhir, tambahkan fungsi pengendali tombol untuk tombol Cancel dan Send pada layar ringkasan.
- Pada composable
OrderSummaryScreendiSummaryScreen.kt, tambahkan parameter bernamaonCancelButtonClickeddari jenis() -> Unit.
@Composable
fun OrderSummaryScreen(
orderUiState: OrderUiState,
onCancelButtonClicked: () -> Unit,
modifier: Modifier = Modifier
){
...
}
- Tambahkan parameter lain dari jenis
(String, String) -> Unitlalu beri namaonSendButtonClicked.
@Composable
fun OrderSummaryScreen(
orderUiState: OrderUiState,
onCancelButtonClicked: () -> Unit,
onSendButtonClicked: (String, String) -> Unit,
modifier: Modifier = Modifier
){
...
}
- Sekarang composable
OrderSummaryScreenmendapatkan nilai untukonSendButtonClickeddanonCancelButtonClicked. CariOrderSummaryPreview, teruskan isi lambda kosong dengan dua parameterStringkeonSendButtonClickeddan isi lambda kosong ke parameteronCancelButtonClicked.
@Preview
@Composable
fun OrderSummaryPreview() {
CupcakeTheme {
OrderSummaryScreen(
orderUiState = OrderUiState(0, "Test", "Test", "$300.00"),
onSendButtonClicked = { subject: String, summary: String -> },
onCancelButtonClicked = {},
modifier = Modifier.fillMaxHeight()
)
}
}
- Teruskan
onSendButtonClickeduntuk parameteronClickdari tombol Send. TeruskannewOrderdanorderSummary, dua variabel yang ditentukan sebelumnya diOrderSummaryScreen. String ini terdiri dari data aktual yang dapat dibagikan pengguna kepada aplikasi lain.
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { onSendButtonClicked(newOrder, orderSummary) }
) {
Text(stringResource(R.string.send))
}
- Teruskan
onCancelButtonClickeduntuk parameteronClickpada tombol Cancel.
OutlinedButton(
modifier = Modifier.fillMaxWidth(),
onClick = onCancelButtonClicked
) {
Text(stringResource(R.string.cancel))
}
Membuka rute lain
Untuk membuka rute lain, cukup panggil metode navigate() pada instance NavHostController Anda.

Metode navigasi mengambil satu parameter: String yang sesuai dengan rute yang ditentukan di NavHost. Jika rute cocok dengan salah satu panggilan ke composable() di NavHost, aplikasi akan membuka layar tersebut.
Anda akan meneruskan fungsi yang memanggil navigate() saat pengguna menekan tombol pada layar Start, Flavor, dan Pickup.
- Di
CupcakeScreen.kt, temukan panggilan kecomposable()untuk layar mulai. Untuk parameteronNextButtonClicked, teruskan ekspresi lambda.
StartOrderScreen(
quantityOptions = DataSource.quantityOptions,
onNextButtonClicked = {
}
)
Ingat properti Int yang diteruskan ke fungsi ini untuk jumlah cupcake? Sebelum membuka layar berikutnya, Anda harus mengupdate model tampilan agar aplikasi menampilkan subtotal yang benar.
- Panggil
setQuantitypadaviewModel, yang meneruskanit.
onNextButtonClicked = {
viewModel.setQuantity(it)
}
- Panggil
navigate()padanavController, yang meneruskanCupcakeScreen.Flavor.nameuntukroute.
onNextButtonClicked = {
viewModel.setQuantity(it)
navController.navigate(CupcakeScreen.Flavor.name)
}
- Untuk parameter
onNextButtonClickeddi layar rasa, cukup teruskan lambda yang memanggilnavigate(), dengan meneruskanCupcakeScreen.Pickup.nameuntukroute.
composable(route = CupcakeScreen.Flavor.name) {
val context = LocalContext.current
SelectOptionScreen(
subtotal = uiState.price,
onNextButtonClicked = { navController.navigate(CupcakeScreen.Pickup.name) },
options = DataSource.flavors.map { id -> context.resources.getString(id) },
onSelectionChanged = { viewModel.setFlavor(it) },
modifier = Modifier.fillMaxHeight()
)
}
- Teruskan lambda kosong untuk
onCancelButtonClickedyang akan Anda implementasikan berikutnya.
SelectOptionScreen(
subtotal = uiState.price,
onNextButtonClicked = { navController.navigate(CupcakeScreen.Pickup.name) },
onCancelButtonClicked = {},
options = DataSource.flavors.map { id -> context.resources.getString(id) },
onSelectionChanged = { viewModel.setFlavor(it) },
modifier = Modifier.fillMaxHeight()
)
- Untuk parameter
onNextButtonClickeddi layar pengambilan, teruskan lambda yang memanggilnavigate(), dengan meneruskanCupcakeScreen.Summary.nameuntukroute.
composable(route = CupcakeScreen.Pickup.name) {
SelectOptionScreen(
subtotal = uiState.price,
onNextButtonClicked = { navController.navigate(CupcakeScreen.Summary.name) },
options = uiState.pickupOptions,
onSelectionChanged = { viewModel.setDate(it) },
modifier = Modifier.fillMaxHeight()
)
}
- Sekali lagi, teruskan lambda kosong untuk
onCancelButtonClicked().
SelectOptionScreen(
subtotal = uiState.price,
onNextButtonClicked = { navController.navigate(CupcakeScreen.Summary.name) },
onCancelButtonClicked = {},
options = uiState.pickupOptions,
onSelectionChanged = { viewModel.setDate(it) },
modifier = Modifier.fillMaxHeight()
)
- Untuk
OrderSummaryScreen, teruskan lambda kosong untukonCancelButtonClickeddanonSendButtonClicked. Tambahkan parameter untuksubjectdansummaryyang diteruskan keonSendButtonClicked, yang akan segera Anda implementasikan.
composable(route = CupcakeScreen.Summary.name) {
OrderSummaryScreen(
orderUiState = uiState,
onCancelButtonClicked = {},
onSendButtonClicked = { subject: String, summary: String ->
},
modifier = Modifier.fillMaxHeight()
)
}
Sekarang Anda dapat membuka setiap layar aplikasi. Perhatikan bahwa dengan memanggil navigate(), layar tidak hanya berubah, tetapi juga ditempatkan di atas data sebelumnya. Selain itu, saat menekan tombol kembali sistem, Anda dapat kembali ke layar sebelumnya.
Aplikasi menumpuk setiap layar di atas layar sebelumnya, dan tombol kembali (
) dapat menghapusnya. Histori layar dari startDestination di bagian bawah hingga layar teratas yang baru saja ditampilkan dikenal sebagai data sebelumnya.
Membuka layar awal
Tidak seperti tombol kembali sistem, tombol Cancel tidak kembali ke layar sebelumnya. Sebagai gantinya, tombol ini akan menghilangkan—menghapus—semua layar dari data sebelumnya dan kembali ke layar awal.
Anda dapat melakukannya dengan memanggil metode popBackStack().

Metode popBackStack() memerlukan dua parameter.
route: String yang mewakili rute tujuan yang akan dinavigasikan kembali.inclusive: Nilai Boolean yang, jika benar, juga akan menampilkan (menghapus) rute yang ditentukan. Jika salah,popBackStack()akan menghapus semua tujuan di atas—tetapi tidak termasuk—tujuan awal, sehingga membiarkannya sebagai layar teratas yang terlihat oleh pengguna.
Saat pengguna menekan tombol Cancel pada layar mana pun, aplikasi akan mereset status dalam model tampilan dan memanggil popBackStack(). Anda akan menerapkan metode untuk melakukan ini terlebih dahulu, lalu meneruskannya untuk parameter yang sesuai di ketiga layar dengan tombol Cancel.
- Setelah fungsi
CupcakeApp(), tentukan fungsi pribadi yang disebutcancelOrderAndNavigateToStart().
private fun cancelOrderAndNavigateToStart() {
}
- Tambahkan dua parameter:
viewModeldari jenisOrderViewModel, dannavControllerdari jenisNavHostController.
private fun cancelOrderAndNavigateToStart(
viewModel: OrderViewModel,
navController: NavHostController
) {
}
- Di isi fungsi, panggil
resetOrder()padaviewModel.
private fun cancelOrderAndNavigateToStart(
viewModel: OrderViewModel,
navController: NavHostController
) {
viewModel.resetOrder()
}
- Panggil
popBackStack()padanavController, yang meneruskanCupcakeScreen.Start.nameuntukroute, danfalseuntukinclusive.
private fun cancelOrderAndNavigateToStart(
viewModel: OrderViewModel,
navController: NavHostController
) {
viewModel.resetOrder()
navController.popBackStack(CupcakeScreen.Start.name, inclusive = false)
}
- Di composable
CupcakeApp(), teruskancancelOrderAndNavigateToStartuntuk parameteronCancelButtonClickeddari dua composableSelectOptionScreendan composableOrderSummaryScreen.
composable(route = CupcakeScreen.Start.name) {
StartOrderScreen(
quantityOptions = DataSource.quantityOptions,
onNextButtonClicked = {
viewModel.setQuantity(it)
navController.navigate(CupcakeScreen.Flavor.name)
},
modifier = Modifier
.fillMaxSize()
.padding(dimensionResource(R.dimen.padding_medium))
)
}
composable(route = CupcakeScreen.Flavor.name) {
val context = LocalContext.current
SelectOptionScreen(
subtotal = uiState.price,
onNextButtonClicked = { navController.navigate(CupcakeScreen.Pickup.name) },
onCancelButtonClicked = {
cancelOrderAndNavigateToStart(viewModel, navController)
},
options = DataSource.flavors.map { id -> context.resources.getString(id) },
onSelectionChanged = { viewModel.setFlavor(it) },
modifier = Modifier.fillMaxHeight()
)
}
composable(route = CupcakeScreen.Pickup.name) {
SelectOptionScreen(
subtotal = uiState.price,
onNextButtonClicked = { navController.navigate(CupcakeScreen.Summary.name) },
onCancelButtonClicked = {
cancelOrderAndNavigateToStart(viewModel, navController)
},
options = uiState.pickupOptions,
onSelectionChanged = { viewModel.setDate(it) },
modifier = Modifier.fillMaxHeight()
)
}
composable(route = CupcakeScreen.Summary.name) {
OrderSummaryScreen(
orderUiState = uiState,
onCancelButtonClicked = {
cancelOrderAndNavigateToStart(viewModel, navController)
},
onSendButtonClicked = { subject: String, summary: String ->
},
modifier = Modifier.fillMaxHeight()
)
}
- Jalankan aplikasi Anda dan uji apakah menekan tombol Cancel di layar mana pun akan mengarahkan pengguna kembali ke layar pertama.
6. Membuka aplikasi lain
Sejauh ini, Anda telah mempelajari cara membuka layar yang berbeda di aplikasi dan cara kembali ke layar utama. Tinggal satu langkah lagi untuk menerapkan navigasi di aplikasi Cupcake. Pada layar ringkasan pesanan, pengguna dapat mengirimkan pesanannya ke aplikasi lain. Pilihan ini menampilkan ShareSheet—komponen antarmuka pengguna yang menutupi bagian bawah layar—yang menampilkan opsi berbagi.
UI ini bukan bagian dari aplikasi Cupcake, melainkan disediakan oleh sistem operasi Android. UI sistem, seperti layar berbagi, tidak dipanggil oleh navController. Sebagai gantinya, Anda menggunakan Intent.
Intent adalah permintaan bagi sistem untuk melakukan beberapa tindakan, umumnya menghadirkan aktivitas baru. Ada banyak intent yang berbeda, dan sebaiknya Anda merujuk ke dokumentasi untuk mendapatkan daftar yang komprehensif. Namun, kita tertarik dengan yang disebut ACTION_SEND. Anda dapat memberi intent ini beberapa data, seperti string, dan menyajikan tindakan berbagi yang sesuai untuk data tersebut.
Proses dasar untuk menyiapkan intent adalah sebagai berikut:
- Buat objek intent dan tentukan intent tersebut, seperti
ACTION_SEND. - Tentukan jenis data tambahan yang dikirim dengan intent. Untuk teks sederhana, Anda dapat menggunakan
"text/plain", meskipun jenis lain, seperti"image/*"atau"video/*", tersedia. - Teruskan data tambahan ke intent, seperti teks atau gambar untuk dibagikan, dengan memanggil metode
putExtra(). Intent ini akan memerlukan dua tambahan:EXTRA_SUBJECTdanEXTRA_TEXT. - Panggil metode konteks
startActivity(), dengan meneruskan aktivitas yang dibuat dari intent.
Kami akan memandu Anda dalam membuat intent tindakan berbagi, tetapi prosesnya sama untuk jenis intent lainnya. Untuk project selanjutnya, sebaiknya Anda membaca dokumentasi yang diperlukan untuk jenis data tertentu dan menambahkan yang diperlukan.
Selesaikan langkah-langkah berikut untuk membuat intent guna mengirim pesanan cupcake ke aplikasi lain:
- Di CupcakeScreen.kt, di bawah composable
CupcakeApp, buat fungsi pribadi bernamashareOrder().
private fun shareOrder()
- Tambahkan parameter bernama
contextdari jenisContext.
import android.content.Context
private fun shareOrder(context: Context) {
}
- Tambahkan dua parameter
String:subjectdansummary. String ini akan ditampilkan di lembar tindakan berbagi.
private fun shareOrder(context: Context, subject: String, summary: String) {
}
- Dalam isi fungsi, buat Intent bernama
intent, dan teruskanIntent.ACTION_SENDsebagai argumen.
import android.content.Intent
val intent = Intent(Intent.ACTION_SEND)
Karena Anda hanya perlu mengonfigurasi objek Intent ini sekali, Anda dapat membuat beberapa baris kode berikutnya lebih ringkas menggunakan fungsi apply(), yang telah Anda pelajari di codelab sebelumnya.
- Panggil
apply()pada Intent yang baru dibuat dan teruskan ekspresi lambda.
val intent = Intent(Intent.ACTION_SEND).apply {
}
- Di isi lambda, tetapkan jenis ke
"text/plain". Karena Anda melakukan ini dalam fungsi yang diteruskan keapply(), Anda tidak perlu merujuk ke ID objek,intent.
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
}
- Panggil
putExtra(), dengan meneruskan subjek untukEXTRA_SUBJECT.
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_SUBJECT, subject)
}
- Panggil
putExtra(), dengan meneruskan ringkasan untukEXTRA_TEXT.
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_SUBJECT, subject)
putExtra(Intent.EXTRA_TEXT, summary)
}
- Panggil metode
startActivity()konteks.
context.startActivity(
)
- Dalam lambda yang diteruskan ke
startActivity(), buat aktivitas dari Intent dengan memanggil metode classcreateChooser(). Teruskan intent untuk argumen pertama dan resource stringnew_cupcake_order.
context.startActivity(
Intent.createChooser(
intent,
context.getString(R.string.new_cupcake_order)
)
)
- Pada composable
CupcakeApp, dalam panggilan kecomposable()untukCucpakeScreen.Summary.name, dapatkan referensi ke objek konteks sehingga Anda dapat meneruskannya ke fungsishareOrder().
composable(route = CupcakeScreen.Summary.name) {
val context = LocalContext.current
...
}
- Dalam isi lambda
onSendButtonClicked(), panggilshareOrder()dengan meneruskancontext,subject, dansummarysebagai argumen.
onSendButtonClicked = { subject: String, summary: String ->
shareOrder(context, subject = subject, summary = summary)
}
- Jalankan aplikasi Anda dan jelajahi layar.
Saat mengklik Send Order to Another App, Anda akan melihat tindakan berbagi seperti Messaging dan Bluetooth di sheet bawah, beserta subjek dan ringkasan yang Anda berikan sebagai tambahan.

7. Membuat panel aplikasi merespons navigasi
Meskipun aplikasi Anda berfungsi dan dapat menavigasi ke dan dari setiap layar, masih ada sesuatu yang hilang dari screenshot di awal codelab ini. Panel aplikasi tidak merespons navigasi secara otomatis. Judul tidak diupdate saat aplikasi menuju ke rute baru atau menampilkan tombol Atas sebelum judul jika sesuai.
Kode awal menyertakan composable untuk mengelola AppBar yang bernama CupcakeAppBar. Setelah mengimplementasikan navigasi di aplikasi, Anda dapat menggunakan informasi dari data sebelumnya untuk menampilkan judul yang benar dan menampilkan tombol Atas jika sesuai. Composable CupcakeAppBar harus memperhatikan layar saat ini sehingga judul diupdate dengan tepat.
- Di enum
CupcakeScreendi CupcakeScreen.kt, tambahkan parameter jenisIntbernamatitlemenggunakan anotasi@StringRes.
import androidx.annotation.StringRes
enum class CupcakeScreen(@StringRes val title: Int) {
Start,
Flavor,
Pickup,
Summary
}
- Tambahkan nilai resource untuk setiap kasus enum, yang sesuai dengan teks judul untuk setiap layar. Gunakan
app_nameuntuk layarStart,choose_flavoruntuk layarFlavor,choose_pickup_dateuntuk layarPickup, danorder_summaryuntuk layarSummary.
enum class CupcakeScreen(@StringRes val title: Int) {
Start(title = R.string.app_name),
Flavor(title = R.string.choose_flavor),
Pickup(title = R.string.choose_pickup_date),
Summary(title = R.string.order_summary)
}
- Tambahkan parameter bernama
currentScreendari jenisCupcakeScreenke composableCupcakeAppBar.
fun CupcakeAppBar(
currentScreen: CupcakeScreen,
canNavigateBack: Boolean,
navigateUp: () -> Unit = {},
modifier: Modifier = Modifier
)
- Di dalam
CupcakeAppBar, ganti nama aplikasi hard code dengan judul layar saat ini dengan meneruskancurrentScreen.titleke panggilan kestringResource()untuk parameter judulTopAppBar.
TopAppBar(
title = { Text(stringResource(currentScreen.title)) },
modifier = modifier,
navigationIcon = {
if (canNavigateBack) {
IconButton(onClick = navigateUp) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(R.string.back_button)
)
}
}
}
)
Tombol Atas hanya muncul jika ada composable di data sebelumnya. Jika aplikasi tidak memiliki layar di data sebelumnya—StartOrderScreen ditampilkan—tombol Atas tidak akan ditampilkan. Untuk memeriksanya, Anda memerlukan referensi ke data sebelumnya.
- Pada composable
CupcakeApp, di bawah variabelnavController, buat variabel bernamabackStackEntrydan panggil metodecurrentBackStackEntryAsState()darinavControllermenggunakan delegasiby.
import androidx.navigation.compose.currentBackStackEntryAsState
@Composable
fun CupcakeApp(
viewModel: OrderViewModel = viewModel(),
navController: NavHostController = rememberNavController()
){
val backStackEntry by navController.currentBackStackEntryAsState()
...
}
- Konversikan judul layar saat ini menjadi nilai
CupcakeScreen. Di bawah variabelbackStackEntry, buat variabel menggunakanvalbernamacurrentScreenyang sama dengan hasil pemanggilan fungsi classvalueOf()dariCupcakeScreen, lalu teruskan rute tujuanbackStackEntry. Gunakan operator elvis untuk memberikan nilai defaultCupcakeScreen.Start.name.
val currentScreen = CupcakeScreen.valueOf(
backStackEntry?.destination?.route ?: CupcakeScreen.Start.name
)
- Teruskan nilai variabel
currentScreenke dalam parameter bernama sama dari composableCupcakeAppBar.
CupcakeAppBar(
currentScreen = currentScreen,
canNavigateBack = false,
navigateUp = {}
)
Selama ada layar di belakang layar saat ini pada data sebelumnya, tombol Atas akan muncul. Anda dapat menggunakan ekspresi boolean untuk mengidentifikasi apakah tombol Atas akan muncul:
- Untuk parameter
canNavigateBack, teruskan ekspresi boolean yang memeriksa apakah propertipreviousBackStackEntrydarinavControllertidak sama dengan null.
canNavigateBack = navController.previousBackStackEntry != null,
- Untuk benar-benar kembali ke layar sebelumnya, panggil metode
navigateUp()navController.
navigateUp = { navController.navigateUp() }
- Jalankan aplikasi Anda.
Perhatikan bahwa judul AppBar sekarang diupdate untuk mencerminkan layar saat ini. Saat membuka layar selain StartOrderScreen, tombol Atas akan muncul dan membawa Anda kembali ke layar sebelumnya.

8. Mendapatkan kode solusi
Untuk mendownload kode codelab yang sudah selesai, Anda dapat menggunakan perintah git berikut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-cupcake.git $ cd basic-android-kotlin-compose-training-cupcake $ git checkout navigation
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi untuk codelab ini, lihat kode tersebut di GitHub.
9. Ringkasan
Selamat! Anda baru saja melakukan peningkatan dari aplikasi satu layar sederhana ke aplikasi multilayar yang kompleks menggunakan komponen Jetpack Navigation untuk berpindah di antara beberapa layar. Anda menentukan rute, menanganinya di NavHost, dan menggunakan parameter jenis fungsi untuk memisahkan logika navigasi dari setiap layar. Anda juga telah mempelajari cara mengirim data ke aplikasi lain menggunakan intent serta menyesuaikan panel aplikasi sebagai respons terhadap navigasi. Dalam unit mendatang, Anda akan terus menggunakan keterampilan ini saat mengerjakan beberapa aplikasi multilayar lainnya dengan kompleksitas yang semakin meningkat.









