Pola arsitektur Aliran Data Searah (UDF) berfungsi lancar dengan Compose. Jika aplikasi menggunakan jenis pola arsitektur lain, seperti Presenter View Model (MVP), sebaiknya migrasikan bagian UI tersebut ke UDF sebelum atau saat mengadopsi Compose.
ViewModel dalam Compose
Jika menggunakan library Architecture Components
ViewModel, Anda dapat mengakses
ViewModel
dari composable mana pun dengan
memanggil fungsi
viewModel()
,
seperti yang dijelaskan dalam Dokumentasi integrasi Compose dengan
library umum.
Saat menggunakan Compose, berhati-hatilah saat menggunakan jenis ViewModel
yang sama dalam berbagai composable karena elemen ViewModel
mengikuti cakupan siklus proses View. Cakupan
akan berupa aktivitas host, fragmen, atau grafik navigasi jika
library Navigasi digunakan.
Misalnya, jika composable dapat dihosting dalam aktivitas, viewModel()
selalu
menampilkan instance yang sama yang hanya akan dihapus saat aktivitas telah selesai.
Pada contoh berikut, pengguna yang sama ("user1") akan disapa dua kali karena
instance GreetingViewModel
yang sama digunakan kembali di semua composable dalam
aktivitas host. Instance ViewModel
pertama yang telah dibuat digunakan kembali di
composable lain.
class GreetingActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { Column { GreetingScreen("user1") GreetingScreen("user2") } } } } } @Composable fun GreetingScreen( userId: String, viewModel: GreetingViewModel = viewModel( factory = GreetingViewModelFactory(userId) ) ) { val messageUser by viewModel.message.observeAsState("") Text(messageUser) } class GreetingViewModel(private val userId: String) : ViewModel() { private val _message = MutableLiveData("Hi $userId") val message: LiveData<String> = _message } class GreetingViewModelFactory(private val userId: String) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { return GreetingViewModel(userId) as T } }
Karena grafik navigasi juga mencakup elemen ViewModel
, composable yang merupakan
tujuan dalam grafik navigasi memiliki instance ViewModel
yang berbeda.
Dalam hal ini, ViewModel
dimasukkan ke siklus proses tujuan dan
akan dihapus saat tujuan dihapus dari backstack. Pada contoh berikut,
saat pengguna membuka
layar Profil, instance baru GreetingViewModel
akan dibuat.
@Composable fun MyApp() { NavHost(rememberNavController(), startDestination = "profile/{userId}") { /* ... */ composable("profile/{userId}") { backStackEntry -> GreetingScreen(backStackEntry.arguments?.getString("userId") ?: "") } } }
Sumber status kebenaran
Saat Anda mengadopsi Compose di satu bagian UI, Compose dan kode sistem View mungkin perlu membagikan data. Jika memungkinkan, sebaiknya Anda merangkum status bersama tersebut di class lain yang mengikuti praktik terbaik UDF dan digunakan oleh kedua platform tersebut, misalnya, di ViewModel yang mengekspos aliran data dari data bersama untuk mengurangi update data.
Namun, hal ini tidak selalu mungkin jika data yang akan dibagikan dapat diubah atau terikat erat dengan elemen UI. Dalam hal ini, satu sistem harus menjadi sumber kebenaran, dan sistem tersebut perlu membagikan setiap update data ke sistem lain. Sebagai aturan umum, sumber kebenaran harus dimiliki oleh elemen mana pun yang lebih dekat dengan root hierarki UI.
Compose sebagai sumber kebenaran
Gunakan fungsi
SideEffect
untuk memublikasikan status Compose ke kode non-compose. Dalam hal ini,
sumber kebenaran disimpan dalam properti yang dapat disusun yang mengirim update status.
Contohnya, library analisis Anda dapat digunakan untuk mengelompokkan populasi
pengguna dengan melampirkan metadata khusus (properti pengguna pada contoh ini)
ke semua peristiwa analisis berikutnya. Untuk memberitahukan jenis pengguna dari
pengguna saat ini ke library analisis Anda, gunakan SideEffect
untuk memperbarui nilainya.
@Composable
fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics {
val analytics: FirebaseAnalytics = remember {
/* ... */
}
// On every successful composition, update FirebaseAnalytics with
// the userType from the current User, ensuring that future analytics
// events have this metadata attached
SideEffect {
analytics.setUserProperty("userType", user.userType)
}
return analytics
}
Untuk mengetahui informasi selengkapnya, lihat dokumentasi efek samping.
Sistem View sebagai sumber kebenaran
Jika sistem View memiliki status dan membagikannya dengan Compose, sebaiknya
gabungkan status dalam objek mutableStateOf
agar Compose aman dari thread. Jika Anda menggunakan pendekatan ini, fungsi composable akan disederhanakan karena
tidak lagi memiliki sumber kebenaran, tetapi sistem View harus mengupdate status
yang dapat diubah dan View yang menggunakan status tersebut.
Dalam contoh berikut, CustomViewGroup
berisi TextView
dan
ComposeView
dengan komponen TextField
di dalamnya. TextView
harus menampilkan
konten yang diketik pengguna dalam TextField
.
class CustomViewGroup @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : LinearLayout(context, attrs, defStyle) { // Source of truth in the View system as mutableStateOf // to make it thread-safe for Compose private var text by mutableStateOf("") private val textView: TextView init { orientation = VERTICAL textView = TextView(context) val composeView = ComposeView(context).apply { setContent { MaterialTheme { TextField(value = text, onValueChange = { updateState(it) }) } } } addView(textView) addView(composeView) } // Update both the source of truth and the TextView private fun updateState(newValue: String) { text = newValue textView.text = newValue } }