今天要將 training 資料搬到 viewModel。
ViewModel
能以比較長的生命週期持有 State
,讓螢幕選轉等會造成 config 變化導致資料重置的情況。
原本在 composable
用了 rememberSaverble
儲存 List 來度過螢幕旋轉,但這麼做有兩個缺點,一是Saverble是把值儲存在 Bundle 官方不建議儲存複雜且大量的數據,二是如果要存不是基礎型態的資料需要自定義 Saver,這麼做也很麻煩。
所以把 List 搬到 ViewModel
來讓 Composalbe 來取資料會是個好作法。
那麼 ViewModel 中的 List 要怎麼擁有狀態,又怎麼讓 Composable 能夠觀察 List 的改變呢?
toMutableStateList
,能將普通的 List 轉為帶有狀態可變更內容的 List ,當 List 新增或刪除 item 時,用到這個 list 資料的 composable 就會重組。
class TrainingViewModel : ViewModel() {
fun getTraining(): List<TrainingItem> {
//... list of training
}
private val _trainingList = getTraining().toMutableStateList()
val trainingList: List<TrainingItem>
get() = _trainingList
fun addTraining(trainingItem: TrainingItem) {
_trainingList.add(trainingItem)
}
fun updateTraining(trainingItem: TrainingItem) {
_trainingList[trainingItem.index] = trainingItem
}
fun deleteTraining(trainingIndex: Int) {
_trainingList.removeAt(trainingIndex)
}
}
getTraining()
:這裡整理成 function,未來資料可以從 DB 取得。toMutableStateList()
:這個 function 可以把 List 轉成有狀態的 List。addTraining
:新增trainingItem
到 list 的最後面updateTraining
:更新 trainingItem 的資料deleteTraining
:根據 trainingItem 的 index 移除 trainingItemfun TrainingScreen(
initDate: LocalDate = LocalDate.now(),
trainingVM: TrainingViewModel = viewModel()
) {
val trainingList = trainingVM.trainingList
}
接下來可以用 viewModel
裡的 funtion 來更新 Training
List 。
為了方便操作,我先在 onClick lambda 裡使用上
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TrainingScreen(
//...
trainingVM: TrainingViewModel = viewModel()
) {
val trainingList = trainingVM.trainingList
//...
Scaffold(
topBar = {//...},
floatingActionButton = {
FloatingActionButton(
onClick = {
trainingVM.addTraining(
trainingList.last().copy(index = trainingList.last().index + 1)
)
}
) {
//...
}
}
) { padding ->
LazyColumn(
//...
) {
itemsIndexed(trainingList) { i,item->
TrainingItem(item, onClick = { trainingVM.deleteTraining(i) })
}
}
}
}
FloatingActionButton()
中 onClick = {}
使用 trainingVM.addTraining()
,為了先確認這個方法是否可行,我直接複製 training List 最後一個 training item 並且將 index +1 以確保 index 在 list 中是唯一值。items
改為 itemsIndexed
,lambda 要加上 index, item →
變數名稱,把 it
給為 item
。deleteTraining
移除的方式是看 list 的 index
trainingVM.deleteTraining(it.index)
今日運動
休息