Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Kotlin untuk Jetpack Compose

Jetpack Compose dibuat berdasarkan Kotlin. Dalam beberapa kasus, Kotlin memberikan idiom khusus yang mempermudah penulisan kode Compose yang baik. Jika Anda memikirkan bahasa pemrograman lain dan secara natural menerjemahkan bahasa tersebut ke Kotlin, Anda mungkin akan melewatkan beberapa keunggulan Compose, dan mungkin akan merasa sulit memahami kode Kotlin yang ditulis secara idiomatik. Mengenal gaya Kotlin lebih jauh dapat membantu Anda menghindari kegagalan tersebut.

Argumen default

Saat menulis fungsi Kotlin, Anda dapat menentukan nilai default untuk argumen fungsi, yang digunakan jika pemanggil tidak secara eksplisit meneruskan nilai tersebut. Fitur ini mengurangi kebutuhan akan kelebihan fungsi.

Misalnya, Anda ingin menulis fungsi yang menggambar persegi. Fungsi tersebut mungkin memiliki satu parameter yang diperlukan, side, yang menentukan panjang setiap sisi. Fungsi itu mungkin memiliki beberapa parameter opsional, seperti thickness, edgeColor, fill, dan sebagainya; jika pemanggil tidak menentukannya, fungsi akan menggunakan nilai default. Dalam bahasa lain, Anda mungkin ingin menulis beberapa fungsi:

// We don't need to do this in Kotlin!
void drawSquare(int side) {...}
void drawSquare(int side, int thickness) {...}
void drawSquare(int side, int thickness, Color edgeColor ) {...}
// …

Di Kotlin, Anda dapat menulis fungsi tunggal dan menentukan nilai default untuk argumen:

fun drawSquare(
    side: Int, thickness: Int = 2,
    edgeColor: Color = Color.Black,
    fill: Color = Color.White
) { ... }

Selain mencegah Anda menulis beberapa fungsi secara berlebihan, fitur ini menjadikan kode Anda lebih mudah dibaca. Jika pemanggil tidak menentukan nilai untuk sebuah argumen, itu menunjukkan bahwa fungsi akan menggunakan nilai default. Selain itu, parameter yang dinamai mempermudah untuk melihat apa yang terjadi. Jika Anda melihat kode dan melihat panggilan fungsi seperti ini, Anda mungkin tidak tahu apa arti parameter itu tanpa memeriksa kode drawSquare():

drawSquare(30, 5, Color.Red);

Sebaliknya, kode itu terdokumentasi sendiri:

drawSquare(side = 30, thickness = 5, edgeColor = Color.Red)

Sebagian besar library Compose menggunakan argumen default, dan ada baiknya untuk melakukan hal yang sama pada fungsi yang dapat dikomposisi, yang Anda tulis. Praktik ini membuat komposisi Anda dapat disesuaikan, tetapi tetap membuat perilaku default mudah dipanggil. Jadi, misalnya, Anda dapat membuat elemen teks sederhana seperti ini:

Text(text = "Hello, Android!")

Kode tersebut memiliki efek yang sama seperti kode berikut yang jauh lebih panjang, dengan lebih banyak parameter Text yang ditetapkan secara eksplisit:

Text(text = "Hello, Android!",
    color = Color.Unset,
    fontSize = TextUnit.Inherit,
    letterSpacing = TextUnit.Inherit,
    Overflow = TextOverflow.Clip)

Tidak hanya cuplikan kode pertama yang jauh lebih sederhana dan lebih mudah dibaca, cuplikan ini juga mendokumentasikan dirinya sendiri. Dengan hanya menentukan parameter text, Anda mendokumentasikan bahwa untuk semua parameter lainnya, Anda ingin menggunakan nilai default. Sebaliknya, cuplikan kedua menyiratkan bahwa Anda ingin menetapkan nilai secara eksplisit untuk parameter lainnya, meskipun nilai yang Anda tetapkan kebetulan adalah nilai default untuk fungsi tersebut.

Fungsi dan ekspresi lambda yang lebih tinggi

Kotlin mendukung fungsi yang lebih tinggi, yang menerima fungsi lain sebagai parameter. Compose dibuat berdasarkan pendekatan ini. Misalnya, fungsi Button yang dapat dikomposisi menyediakan parameter lambda onClick. Nilai parameter tersebut adalah fungsi, yang dipanggil oleh tombol saat pengguna mengkliknya:

Button( // …
    onClick = myClickFunction)

Fungsi yang lebih tinggi berpasangan secara alami dengan ekspresi lambda, ekspresi yang mengevaluasi sebuah fungsi. Jika Anda hanya memerlukan fungsi tersebut sekali, Anda tidak perlu menentukannya di tempat lain untuk meneruskannya ke fungsi yang lebih tinggi. Sebagai gantinya, Anda dapat menentukan fungsi secara langsung dengan ekspresi lambda. Contoh sebelumnya mengasumsikan bahwa myClickFunction() ditentukan di tempat lain. Tetapi, jika Anda hanya menggunakan fungsi tersebut di sini, akan lebih mudah untuk menentukan fungsi itu sebagai bagian dari ekspresi lambda:

Button( // ...
    onClick = {
       // do something
       // do something else
    }
)

Trailing lambda

Kotlin menawarkan sintaksis khusus untuk memanggil fungsi yang lebih tinggi dengan parameter terakhir adalah lambda. Jika Anda ingin meneruskan ekspresi lambda sebagai parameter tersebut, Anda dapat menggunakan sintaksis trailing lambda. Alih-alih menempatkan ekspresi lambda dalam tanda kurung, Anda harus menempatkannya setelahnya. Ini adalah situasi umum di Compose, jadi Anda harus mengetahui tampilan kodenya.

Misalnya, parameter terakhir untuk semua tata letak, seperti fungsi Column() yang dapat dikomposisi, adalah children, fungsi yang membuat elemen UI turunan. Misalnya Anda ingin membuat kolom yang berisi tiga elemen teks, dan Anda perlu menerapkan beberapa pemformatan. Kode ini akan berfungsi, namun sangat rumit:

Column(
    modifier = Modifier.padding(16.dp), children = {
        Text("Some text")
        Text("Some more text")
        Text("Last text")
    }
)

Karena parameter children adalah parameter terakhir dalam tanda tangan fungsi, dan kita meneruskan nilainya sebagai ekspresi lambda, kita dapat mengeluarkannya dari tanda kurung:

Column(modifier = Modifier.padding(16.dp)) {
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

Kedua contoh tersebut memiliki arti yang sama persis. Kurung kurawal menentukan ekspresi lambda yang diteruskan ke parameter content.

Sebenarnya, jika satu-satunya parameter yang Anda teruskan adalah trailing lambda—yaitu, jika parameter terakhir adalah lambda, dan Anda tidak meneruskan parameter lain—Anda bisa menghapus tanda kurung sekaligus. Jadi, misalnya Anda tidak perlu meneruskan pengubah ke Column. Anda dapat menulis kode seperti ini:

Column {
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

Sintaksis ini sangat umum di Compose, terutama untuk elemen tata letak seperti Column. Parameter terakhir adalah ekspresi lambda yang menentukan turunan elemen, dan turunan tersebut ditentukan dalam tanda kurung kurawal setelah panggilan fungsi.

Cakupan dan penerima

Beberapa metode dan properti hanya tersedia dalam cakupan tertentu. Cakupan terbatas memungkinkan Anda menawarkan fungsi di tempat yang memerlukannya dan menghindari penggunaan fungsi tersebut secara tidak sengaja jika tidak sesuai.

Pertimbangkan contoh yang digunakan di Compose. Saat Anda memanggil komposisi tata letak Row, lambda konten Anda akan dipanggil secara otomatis dalam RowScope. Ini memungkinkan Row mengekspos fungsi yang hanya valid dalam Row. Contoh di bawah menunjukkan cara Row mengekspos nilai spesifik baris untuk pengubah gravity:

Row {
    Text(
        text = "Hello world",
        // This Text is inside a RowScope so it has access to
        // Alignment.CenterVertically but not to
        // Alignment.CenterHorizontally, which would be available
        // in a ColumnScope.
        modifier = Modifier.gravity(Alignment.CenterVertically)
    )
}

Beberapa API menerima lambda yang dipanggil dalam cakupan penerima. lambda tersebut memiliki akses ke properti dan fungsi yang ditentukan di tempat lain, berdasarkan deklarasi parameter:

Box(
    modifier = Modifier.drawBehind {
        // This method accepts a lambda of type DrawScope.() -> Unit
        // therefore in this lambda we can access properties and functions
        // available from DrawScope, such as the `drawRectangle` function.
        drawRectangle(...)
    }
)

Untuk informasi selengkapnya, lihat literal fungsi dengan penerima dalam dokumentasi Kotlin.

Properti yang didelegasikan

Kotlin mendukung properti yang didelegasikan. Properti ini dipanggil seolah-olah merupakan kolom, tetapi nilainya ditentukan secara dinamis dengan mengevaluasi ekspresi. Anda dapat mengenali properti ini dari penggunaan sintaksis by:

class delegatingClass {
    var name: String by nameGetterFunction()
}

Kode lain dapat mengakses properti dengan kode seperti ini:

myDC = delegatingClass()
println("The name property is: " + myDC.name)

Saat println() dijalankan, nameGetterFunction() dipanggil untuk menampilkan nilai string.

Properti yang didelegasikan ini sangat berguna ketika Anda bekerja dengan properti yang didukung status:

var showDialog by remember { mutableStateOf(false) }

// Updating the var automatically triggers a state change
showDialog = true

Destrukturisasi class data

Jika Anda menentukan class data, Anda dapat dengan mudah mengakses data dengan deklarasi destrukturisasi. Misalnya, anggaplah Anda menentukan class Person:

data class Person(val name: String, val age: Int)

Jika Anda memiliki objek dari jenis tersebut, Anda dapat mengakses nilainya dengan kode seperti ini:

val mary = Person(name = "Mary", age = 35)

// ...

val (name, age) = mary

Anda akan sering melihat kode semacam itu di fungsi Compose:

ConstraintLayout {

    val (image, title, subtitle) = createRefs()

    // The `createRefs` function returns a data object;
    // the first three components are extracted into the
    // image, title, and subtitle variables.

    // ...

}

Class data menyediakan banyak fungsi berguna lainnya. Misalnya, saat Anda menentukan class data, compiler akan secara otomatis menentukan fungsi yang berguna seperti equals() dan copy(). Anda dapat menemukan informasi selengkapnya di dokumentasi class data.

Objek tunggal

Kotlin memudahkan untuk mendeklarasikan class tunggal yang selalu memiliki satu instance saja. Objek tunggal ini dideklarasikan dengan object kata kunci. Compose sering menggunakan objek tersebut. Misalnya, MaterialTheme didefinisikan sebagai objek tunggal; properti MaterialTheme.colors, shapes, dan typography semuanya berisi nilai untuk tema saat ini.