Eksplorasi Aplikasi Digital Banking Clipboard Copy pada Saving Card (Jetpack Compose)
Bagian 0: Digital Bank Application Exploration
Hello Folks! Pada artikel kali ini saya akan memulai kembali membagikan eksplorasi UI dengan Jetpack Compose. Jetpack Compose sendiri merupakan sebuah UI toolkit yang dapat digunakan untuk menyederhanakan dan mempercepat UI development process. Dengan toolkit ini kita dapat membangun dan menata UI secara deklaratif secara function based sebagai komponen yang disebut composable. Selain itu kita dapat sepenuhnya menulis kode dengan Kotlin dan memanfaatkan compiler Kotlin untuk menghasilkan aplikasi high perfomance apps. Jika teman — teman ingin belom mengetahui apa itu jetpack compose bisa cek artikel series berikut.
Study case ekplorasi aplikasi yang saya pilih adalah digital banking app. Saat ini banyak sekali opsi digital banking yang bisa kita gunakan, namun saya memilih Blu Digital, karena hampir satu tahun terakhir aplikasi ini rumayan sering saya gunakan. Selama saya menggunakan aplikasi ini terdapat beberapa komponen yang cukup menarik jika kita coba buat dengan menggunakan komponen jetpack compose.
Sekedar informasi, aplikasi ini saya buat hanya sebagai bahan belajar dan explorasi berbagai macam komponen dalam jetpack compose. Untuk penggunaan warna dan berbagai komponen saya mencoba untuk merubah beberapa komponen seperti warna, icon, xlt, padding, dst. Selanjutnya pada pembahasan ini kali akan berfokus pada pembuatan komponen card dan action copy pada halaman home yang memiliki ekpektasi tampilan card dan toast message seperti dibawah ini.
Sebagai langkah awal, buatlah sebuah proyek dengan template Jetpack Compose pada android studio. Selanjutnya untuk membuat komponen dengan pendekatan deklaratif seperti jetpack compose, akan lebih mudah jika dimulai dengan fokus pada komponen terkecil. Sebagai contoh untuk membuat komponen saving card diatas, kita bisa breakdown setiap komponennya menjadi lebih kecil seperti dibawah ini.
Seperti pada ilustrasi diatas Komponen AccountNameNumberTextCopy terdiri dari dua komponen, salah satunya yaitu AccountNameNumberText. Buatlah sebuah file baru dengan nama CustomText.kt, lalu tambahkan sebuah composable function dengan nama AccountNameNumberText untuk membuat komponen yang menampilkan nama dan nomor akun dari saving card, dengan code dibawah ini.
@Composable
fun AccountNameNumberText(
textName: String,
textNumber: String,
modifier: Modifier = Modifier
) {
Row(modifier = Modifier.padding(top = 15.dp, start = 20.dp)) {
Text(
text = textName, style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF212821),
)
)
Text(
text = " - ", style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF212821),
)
)
Text(
text = textNumber, style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Light,
color = Color(0xFF212821),
)
)
}
}
- Text merupakan komponen yang digunakan untuk menampilkan dua informasi berupa nama dan nomor akun, sedang kan simbol (-) ditampilkan dengan Text yang digunakan sebagai separator antara nama dan nomor akun.
- Row merupakan komponen yang digunakan untuk menyusun komponen text secara horizontal.
- TextStyle merupakan komponen yang digunakan untuk menambahkan style pada Text, seperti fontWeight = FontWeight.Bold untuk membuat text menjadi bold, fontSize untuk mengatur ukuran font.
Lalu komponen berikutnya adalah CopyButton, untuk membuat komponen ini tambahkan sebuah file baru dengan nama CustomButton.kt, dan download icon copy pada link berikut. Buatlah sebuah composable function dengan nama CopyButton didalamnya dengan kode seperti dibawah ini.
@Composable
fun CopyButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
IconButton(
onClick = onClick,
modifier
.size(40.dp)
.padding(end = 10.dp, top = 10.dp)
) {
Icon(
painter = painterResource(R.drawable.icon_copy),
contentDescription = "Copy",
modifier.size(30.dp)
)
}
}
- IconButton merupakan komponen untuk membuat sebuah button dengan icon.
- Icon merupakan komponen yang akan menampilkan ikon image didalam button, untuk resource ikon dapat dimasukkan dengan menggunakan painter = painterResource(R.drawable.icon_copy)
- onClick: () -> Unit digunakan untuk mentrigger action click tambahkan sebuah paramater dengan type unit.
Sesuai dengan ilustrasi sebelumnya gabungkan komponen AccountNameNumberText dan CopyButton kedalam satu composable function dengan nama AccountNameNumberTextCopy. Susun dengan menggunakan Row lalu tambahkan Arrangement.SpaceBetween untuk membuat komponen pertama ke posisi start dan komponen ke dua ke posisi end.
@Composable
fun AccountNameNumberTextCopy(
textName: String,
textNumber: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
AccountNameNumberText(textName = textName, textNumber = textNumber, modifier = Modifier)
CopyButton(onClick = onClick)
}
}
Komponen berikutnya adalah text balance, komponen ini terdiri dari 2 komponen yaitu text dan custom button. Sehingga untuk membuat komponen ini, bisa dimulai dengan menambahkan sebuah sebuah composable function didalam file CustomButton.kt dengan nama EyeIconButton dengan code dibawah ini.
@Composable
fun EyeIconButton(onClick: () -> Unit, modifier: Modifier = Modifier, isVisible: Boolean) {
IconButton(
onClick = onClick,
modifier
.clip(CircleShape)
.background(color = Color(0XFFECEDF1))
) {
Icon(
painter = painterResource(if (isVisible) R.drawable.icon_unhide else R.drawable.icon_hide),
contentDescription = "Eye",
modifier = Modifier.size(30.dp)
)
}
}
Pada code diatas komponen IconButton kembali digunakan, untuk mebuat sebuah button hide/unhide dengan dua icon yang berbeda download icon pada link berikut.
- clip(CircleShape) merupakah sebuah atribut untuk memberikan sebuah bentuh button lingkaran.
- isVisible merupakan sebuah parameter dengan tipe data boolean. Ikon pada button ini akan ditampilkan sesuai dengan value dari parameter ini.
Langkah berikutnya buatlah BalanceText komponen seperti pada ilustrasi sebelumnya. Untuk membuat komponen ini tambahkan sebuah composable function pada file CustomText.kt. Pada composable function ini gabungkan komponen Text dan EyeIconButton dan pisahkan dengan spacer untuk memberi jarak antar kedua komponen, untuk lebih lengkapnya perhatikan kode dibawah ini.
@Composable
fun BalanceText(modifier: Modifier = Modifier, balance: String, isVisible: Boolean) {
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
Text(
text = if (isVisible) "RP $balance" else "RP ********", style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF212821)
)
)
Spacer(modifier = modifier.width(5.dp))
EyeIconButton(onClick = {}, modifier = Modifier, isVisible = isVisible)
}
}
Kode diatas akan menampilkan text balance sesuai dengan value dari isVisible parameter. Value true akan menampilkan value balance parameter dan false akan menampilkan string “********”.
Selanjutnya buatlah komponen ketiga yaitu berupa button Pindah dana dan Qris. Kedua button hanya memiliki perbedaan pada label, sehingga buatlah sebuah common button. Untuk membuat common button tambahkan sebuah composable componen dengan nama CommonButton pada file CustomButton.kt dengan kode seperti dibawah ini.
@Composable
fun CommonButton(onClick: () -> Unit, modifier: Modifier = Modifier, label: String) {
TextButton(
onClick = onClick,
modifier
.clip(RoundedCornerShape(20.dp))
.background(color = Color(0XFFECEDF1))
) {
Text(
text = label, style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF212821)
)
)
}
}
Kode diatas akan menampilkan sebuah rectangle button dengan rounded corner, untuk membuat komponen ini reuseable maka text dapat diassign dengan value berupa label yang dikeluarkan sebagai parameter. Selain itu hal ini juga berlaku pada onClick parameter. Selanjutnya pada komponen text button sendiri untuk membuatnya memiliki rounded corner gunakan atribut clip(RoundedCornerShape(20.dp)).
Buatlah Pindah dana dan Qris button dengan menggunakan Common button dalam sebuah Row yang nantinya harus ditulis didalam composable function dari saving card, seperti kode dibawah ini.
Row(modifier = Modifier.padding(start = 20.dp, top = 20.dp)) {
CommonButton(onClick = {}, modifier = Modifier, label = "Pindah Dana")
Spacer(Modifier.width(10.dp))
CommonButton(onClick = {}, modifier = Modifier, label = "Qris")
}
Kode diatas akan menampilkan Common Button secara horizontal dan side-by-side. Untuk memberikan jarak antar kedua button Spacer ditambahkan dengan ukuran lebar (10.dp). Selain itu common button parameter label merupakan text yang dibutuhkan untuk menampilkan button text, sehingga parameter ini di assign dengan value “Pindah dana” dan “Qris” sesuai dengan kebutuhan.
Setelah membuat komponen UI terkecil, selanjutnya bisa ke komponen yang lebih besar berupa Saving Card yang terdiri dari 3 blok komponen yang sudah dibuat sebelumnya. Buatlah sebuah CustomCard.kt file dan tambahkan composable function dengan nama SavingCard. Susun komponen didalam SavingCard function dengan menggunakan Column, seperti kode dibawah ini.
@Composable
fun SavingCard(modifier: Modifier, onClick: () -> Unit, textNumber: String, balance: String, textName: String) {
Card(
modifier = modifier.size(width = 320.dp, height = 200.dp),
colors = CardDefaults.cardColors(containerColor = Color(0xFFF3F4F6))
) {
Column(verticalArrangement = Arrangement.SpaceBetween) {
AccountNameNumberTextCopy(
modifier = Modifier,
textName = textName,
textNumber = textNumber,
onClick = onClick
)
BalanceText(
balance = balance,
modifier = Modifier.padding(start = 20.dp, top = 20.dp),
isVisible = false
)
Row(modifier = Modifier.padding(start = 20.dp, top = 20.dp)) {
CommonButton(onClick = {}, modifier = Modifier, label = "Pindah Dana")
Spacer(Modifier.width(10.dp))
CommonButton(onClick = {}, modifier = Modifier, label = "Qris")
}
}
}
}
Pada kode diatas terdapat beberapa hal yang perlu diperhatikan:
- Column digunakan untuk menyusun komponen secara vertikan dan untuk memberikan space antar komponen digunakan properti Arrangement.SpaceBetween.
- Parameter onClick: () -> Unit untuk nantinya diassign dengan copy account number, textNumber: String nantinya di assign dengan value dari account number, balance: String nantinya di assign dengan value dari amount balance, dan textName: String nantinya di assign dengan value dari saving name. Sedangkan hide/unhide balance akan dibahas pada artikel berikutnya sehingga pada code diatas isVisible memiliki value false.
Saving card ini memiliki fitur untuk menyalin account number. Untuk membuat fitur ini diperlukan sebuah extension function seperti code dibawah ini.
fun Context.setClipboard(label: String, text: String) {
(getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager)?.setPrimaryClip(
ClipData.newPlainText(
label,
text
)
)
}
Pada kode diatas terdapat setClipboard yang merupakan extension function untuk Context Class di Android. Sehingga memungkinkan untuk melakukan penyalinan teks ke clipboard dengan mudah. Hal utama yang perlu diperhatikan dalam kode tersebut adalah ClipboardManager. ClipboardManager memprovide pembuatan objek ClipData yang berisi teks dan label, dan dijadikan sebagai klip utama di clipboard.
Saat text disalin pada clipboard aplikasi perlu menampilkan message bahwa text berhasil disalin. Message ini akan di tampilkan dengan menggunakan custom toast. Buatlah sebuah file baru dengan nama CustomToast.kt dan tambahkan sebuah composable function dengan nama CustomOnTopToast, seperti pada kode dibawah ini.
@Composable
fun CustomOnTopToast(
message: String,
modifier: Modifier = Modifier,
backgroundColor: Color = Color(0xFF399918),
duration: Long = 3000L,
onDismiss: () -> Unit
) {
var isVisible by remember { mutableStateOf(true) }
LaunchedEffect(key1 = message) {
delay(duration)
isVisible = false
onDismiss()
}
AnimatedVisibility(
visible = isVisible,
exit = fadeOut(animationSpec = tween(500))
) {
Box(
modifier = modifier
.fillMaxWidth()
.background(backgroundColor)
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Text(
text = message,
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
textAlign = TextAlign.Center
)
)
}
}
}
Pada kode diatas akan menampilkan message bahwa text berhasil disalin kepada pengguna. Terdapat beberapa hal yang perlu diperhatikan :
- duration merupakan value yang digunakan untuk menentukan berapa lama message akan di tampilkan dan secara default diset dengan value 3000 milidetik atau 3 detik. Setelah durasi tersebut, pesan akan menghilang secara otomatis.
- Box() merupakan komponen yang akan menampilkan Pesan ditampilkan di dalam komponen Box. Sedangkan property .fillMaxWidth(), akan membuat Box akan memiliki lebar match_parent. Selanjutnya sesuai dengan desain di atas, untuk membuat message ini berada di atas page maka Box tidak perlu ditempatkan secara eksplisit pada posisi apapun. Maka secara default message ini nantinya akan muncul di bagian atas layar, menimpa konten lain.
- isVisible merupakan sebuah variable state yang digunakan untuk men-tracking apakah toast terlihat atau tidak.
- LaunchedEffect() akan me-launch coroutine untuk
delay(duration) dan menampilkan message sesuai dengan value durasi yang ditentukan. - AnimatedVisibility(visible = isVisible, exit = fadeOut(animationSpec = tween(500))) merupakan sebuah animasi (fade-out) yang akan membuat message muncul dan menghilang dengan animasi memudar.
- onDismiss: () -> Unit merupakan sebuah Callback ketika message menghilang.
Langkah terakhir pada bagian ini adalah menampilkan SavingCard pada halaman utama, untuk sementara panggil didalam MainActivity. Panggil SavingCard komponen pada MainActivity seperti kode dibawah ini.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
GreenBankApplicationTheme {
val textNumber = "9876543210"
val textName = "greenSaving"
val balance = "1.000.000"
var isShowMessage by remember { mutableStateOf(false) }
var message by remember { mutableStateOf("") }
SavingCard(
modifier = Modifier.padding(horizontal = 30.dp, vertical = 60.dp),
textNumber = textNumber,
textName = textName,
balance = balance,
onClick = {
this.apply {
setClipboard("Account Number", textNumber)
isShowMessage = true
message = "Berhasil di-Copy!"
}
})
if (isShowMessage) {
CustomOnTopToast(
message = message,
onDismiss = { isShowMessage = false }
)
}
}
}
}
}
Pada kode diatas terdapat beberapa hal yang perlu diperhatikan:
- textNumber merupakan sebuah variabel dengan value nomor rekening.
- textName merupakan sebuah variabel dengan value nama rekening.
- balance merupakan sebuah variabel dengan value saldo rekening.
- isShowMessage merupakan variabel state yang digunakan untuk mengontrol apakah CustomOnTopToast ditampilkan atau tidak.
- message merupakan variabel state yang digunakan untuk menyimpan pesan yang akan ditampilkan pada CustomOnTopToast.
- this.apply{} digunakan untuk memanggil fungsi setClipboard dan mengubah state isShowMessage dan message pada MainActivity.
- setClipboard() digunakan untuk menyalin nomor rekening ke clipboard.
- if (isShowMessage) {} merupakan blok kondisi yang menampilkan akan menampilkanCustomOnTopToast hanya jika isShowMessage bernilai true.
Selanjutnya compile SavingCard, dan akan muncul tampilan seperti dibawah ini.
Jika teman — teman merasa artikel ini bermanfaat, jangan lupa follow akun ini dan clap untuk artikel berikutnya :) cheers.