iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0

前言


今天要將 training 資料搬到 viewModel。

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 移除 trainingItem
fun TrainingScreen(
    initDate: LocalDate = LocalDate.now(),
    trainingVM: TrainingViewModel = viewModel()
) {
		val trainingList = trainingVM.trainingList
}

OnClick 操作 ViewModel List


接下來可以用 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
  • 在 LazyColumn 中點擊 item 時,移除 list 內容 trainingVM.deleteTraining(it.index)

最終成果


image

今日運動
休息


上一篇
Day28 實作 DateTimePicker
下一篇
Day 30 List 的 Item 參數也能有狀態?
系列文
今年一定減成功!Jetpack Compose 做出重訓紀錄APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言