Tugas 10: Membuat Aplikasi Unscramble
Blog ini akan memberikan penjelasan komprehensif dari seluruh kode aplikasi "Unscramble". Kita akan melihat bagaimana setiap file—mulai dari data, state, hingga logika di ViewModel—saling terhubung untuk menciptakan sebuah game yang fungsional dan terstruktur dengan baik dalam arsitektur Android modern.
Alur Kerja Aplikasi: Dari UI ke Logika dan Kembali Lagi
Secara garis besar, aplikasi ini bekerja dengan alur sebagai berikut:
- Inisialisasi:
MainActivitymemuatGameScreen(UI).GameScreenkemudian mendapatkan instance dariGameViewModel. - Pengamatan State:
GameScreen"mengamati" atau "mendengarkan" perubahan padauiStatedariGameViewModel. - Tampilan UI: Berdasarkan data dari
uiState(misalnya, kata yang diacak, skor),GameScreenmenampilkan elemen-elemen yang sesuai. - Interaksi Pengguna: Pengguna mengetik tebakan di
TextFieldatau menekan tombol "Submit". - Pemicu Aksi: Aksi pengguna tersebut memanggil fungsi yang sesuai di
GameViewModel(misalnya,checkUserGuess()). - Proses Logika:
GameViewModelmenjalankan logika permainan (memeriksa jawaban, memperbarui skor, dll). - Pembaruan State:
GameViewModelmemperbarui_uiStateinternalnya dengan data baru. - Recomposition: Karena
GameScreenmengamatiuiState, perubahan ini secara otomatis memicu penggambaran ulang (recomposition) UI untuk menampilkan data terbaru.
1. Sumber Data dan Aturan Permainan (`WordsData.kt`)
const val MAX_NO_OF_WORDS = 10
const val SCORE_INCREASE = 20
val allWords: Set<String> =
setOf(
"animal",
"auto",
"anecdote",
// ...and many more words
)
File ini adalah fondasi dari permainan. Ia mendefinisikan aturan dasar seperti jumlah maksimum kata per permainan (MAX_NO_OF_WORDS) dan penambahan skor (SCORE_INCREASE). Variabel allWords yang bertipe Set adalah bank kata yang akan digunakan. Penggunaan Set efisien untuk memastikan tidak ada kata duplikat.
2. Cetak Biru State Tampilan (`GameUiState.kt`)
data class GameUiState(
val currentScrambledWord: String = "",
val currentWordCount: Int = 1,
val score: Int = 0,
val isGuessedWordWrong: Boolean = false,
val isGameOver: Boolean = false
)
Ini adalah sebuah data class yang berfungsi sebagai "cetak biru" atau "blueprint" untuk semua data yang perlu diketahui oleh UI pada satu waktu. Dengan menyatukan semua state relevan dalam satu objek, kita menciptakan "sumber kebenaran tunggal" (single source of truth). UI hanya perlu menerima objek ini untuk menampilkan seluruh keadaan permainan.
3. Otak Permainan (`GameViewModel.kt`)
Ini adalah komponen paling penting yang berisi seluruh logika dan state management.
a. Manajemen State dengan StateFlow
private val _uiState = MutableStateFlow(GameUiState())
val uiState: StateFlow<GameUiState> = _uiState.asStateFlow()
ViewModel menggunakan MutableStateFlow untuk menyimpan state game saat ini. _uiState bersifat private, artinya hanya ViewModel sendiri yang bisa mengubahnya. Kemudian, ia diekspos sebagai StateFlow yang bersifat read-only ke UI. Pola ini memastikan bahwa UI tidak dapat secara tidak sengaja mengubah state, menjaga alur data tetap teratur.
b. Logika Permainan (Memeriksa dan Memperbarui)
fun checkUserGuess() {
if (userGuess.equals(currentWord, ignoreCase = true)) {
val updatedScore = _uiState.value.score.plus(SCORE_INCREASE)
updateGameState(updatedScore)
} else {
_uiState.update { currentState ->
currentState.copy(isGuessedWordWrong = true)
}
}
updateUserGuess("") // Reset tebakan setelah diperiksa
}
private fun updateGameState(updatedScore: Int) {
if (usedWords.size == MAX_NO_OF_WORDS) {
_uiState.update { currentState ->
currentState.copy(isGameOver = true, score = updatedScore)
}
} else {
_uiState.update { currentState ->
currentState.copy(
isGuessedWordWrong = false,
currentScrambledWord = pickRandomWordAndShuffle(),
currentWordCount = currentState.currentWordCount.inc(),
score = updatedScore
)
}
}
}
Fungsi-fungsi publik seperti checkUserGuess() dan skipWord() adalah antarmuka yang akan dipanggil oleh UI. Mereka kemudian memanggil fungsi privat updateGameState() yang mengatur alur permainan, apakah lanjut ke ronde berikutnya atau mengakhiri permainan. Semua logika kompleks ini terisolasi di dalam ViewModel.
4. Menghubungkan ke UI (`GameScreen.kt` - Sebuah Inferensi)
Catatan: Kode UI (GameScreen.kt) tidak disertakan dalam prompt Anda, namun berdasarkan ViewModel yang ada, kita dapat menyimpulkan (inferensi) bagaimana kode tersebut akan terlihat dan bekerja.
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun GameScreen(
gameViewModel: GameViewModel = viewModel() // 1. Mendapatkan instance ViewModel
) {
// 2. Mengumpulkan state dari ViewModel menjadi State Compose
val gameUiState by gameViewModel.uiState.collectAsState()
Column(...) {
// 3. Menampilkan UI berdasarkan state
Text(text = gameUiState.currentScrambledWord, style = MaterialTheme.typography.headlineMedium)
Text(text = "Skor: ${gameUiState.score}")
Text(text = "Kata ke-${gameUiState.currentWordCount}")
OutlinedTextField(
value = gameViewModel.userGuess,
onValueChange = { gameViewModel.updateUserGuess(it) }, // 4. Mengirim event ke ViewModel
label = { Text("Masukkan tebakan Anda") },
isError = gameUiState.isGuessedWordWrong // Mengubah tampilan jika jawaban salah
)
Button(onClick = { gameViewModel.checkUserGuess() }) { // 5. Memanggil aksi di ViewModel
Text("Submit")
}
// 6. UI Kondisional untuk Game Over
if (gameUiState.isGameOver) {
FinalScoreDialog(
score = gameUiState.score,
onPlayAgain = { gameViewModel.resetGame() }
)
}
}
}
Berikut adalah penjelasan dari kode inferensi di atas:
- Mendapatkan ViewModel: Composable menggunakan `viewModel()` untuk mendapatkan instance dari
GameViewModelyang terikat pada siklus hidup yang benar. - Mengumpulkan State:
collectAsState()adalah fungsi ekstensi yang mengubahStateFlowdari ViewModel menjadiStateyang dapat dibaca oleh Compose. Setiap kaliStateFlowmengeluarkan nilai baru, composable ini akan di-recompose. - Menampilkan UI: Elemen-elemen seperti
Textmembaca data langsung dari objekgameUiStateuntuk menampilkan kata acak, skor, dll. - Mengirim Event: Saat pengguna mengetik,
onValueChangepadaOutlinedTextFieldmemanggil `gameViewModel.updateUserGuess(it)`. Ini adalah contoh "event yang mengalir ke atas". - Memanggil Aksi: Saat tombol "Submit" ditekan,
onClickmemanggil `gameViewModel.checkUserGuess()`, mendelegasikan logika pemeriksaan ke ViewModel. - UI Kondisional: Blok `if (gameUiState.isGameOver)` menunjukkan bagaimana UI dapat secara dinamis menampilkan komponen yang berbeda (seperti dialog skor akhir) berdasarkan sebuah flag boolean di dalam state.
Dokumentasi & Referensi
- Github
- https://developer.android.com/codelabs/basic-android-kotlin-compose-viewmodel-and-state
- https://kuliahppb.blogspot.com/2024/05/viewmodel-and-state-in-compose.html

Komentar
Posting Komentar