【问题标题】:Multiple ModalBottomSheet in Compose not updated when state changes状态更改时,Compose 中的多个 ModalBottomSheet 未更新
【发布时间】:2022-08-03 05:53:32
【问题描述】:

我在 ModalBottomSheetLayout 中实现了 2 个底部工作表,两个底部工作表都有一个可用复选框选中的项目列表。 屏幕的状态由ViewModel管理,当调用选择更改时,一个函数,将所选文本的新值复制状态。 当底部工作表打开时,选择是正确的,但是当我单击更改选择时,底部工作表没有重新组合并且选择没有改变,但是在主屏幕中,状态变化被正确读取并且值被更新。

这是我的代码:

主屏幕:

import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch

@ExperimentalMaterialApi
@Composable
fun MainScreen(
    viewModel: MainViewModel = androidx.lifecycle.viewmodel.compose.viewModel()
) {

    val screenState = viewModel.screenState

    val scope = rememberCoroutineScope()

    val bottomSheetState = rememberModalBottomSheetState(
        initialValue = ModalBottomSheetValue.Hidden
    )

    var bottomSheetContent: (@Composable () -> Unit)? by remember {
        mutableStateOf(null)
    }

    ModalBottomSheetLayout(
        sheetState = bottomSheetState,
        sheetContent = {
            Box(
                modifier = Modifier.defaultMinSize(minHeight = 1.dp)
            ) {
                bottomSheetContent?.let { it() }
            }
        }
    ) {

        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp)
        ) {

            Text(text = \"First BottomSheet\", style = MaterialTheme.typography.h6)

            Text(
                text = \"Selected: ${screenState.selectedTextFromFirstBottomSheet}\",
                Modifier.padding(16.dp)
            )

            Button(onClick = {
                bottomSheetContent = {
                    FirstBottomSheet(
                        selectedText = screenState.selectedTextFromFirstBottomSheet,
                        onSelected = { text ->
                            viewModel.onEvent(
                                MainScreenEvent.OnFirstBottomSheetSelectedTextChanged(text)
                            )
                        },
                        textList = screenState.firstBottomSheetTextList
                    )
                }

                scope.launch {
                    bottomSheetState.show()
                }
            }, modifier = Modifier.padding(16.dp)) {
                Text(text = \" Open First BottomSheet\")
            }


            Text(text = \"Second BottomSheet\", style = MaterialTheme.typography.h6)

            Text(
                text = \"Selected: ${screenState.selectedTextFromSecondBottomSheet}\",
                Modifier.padding(16.dp)
            )

            Button(
                onClick = {
                    bottomSheetContent = {
                        SecondBottomSheet(
                            selectedText = screenState.selectedTextFromSecondBottomSheet,
                            onSelected = { text ->
                                viewModel.onEvent(
                                    MainScreenEvent.OnSecondBottomSheetSelectedTextChanged(text)
                                )
                            },
                            textList = screenState.secondBottomSheetTextList
                        )
                    }

                    scope.launch {
                        bottomSheetState.show()
                    }
                }, modifier = Modifier
                    .padding(16.dp)
            ) {
                Text(text = \" Open Second BottomSheet\")

            }

        }
    }

}

视图模型:

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel


class MainViewModel : ViewModel(){

    var screenState by mutableStateOf(MainScreenState())

    fun onEvent(event: MainScreenEvent){
        when(event){
            is MainScreenEvent.OnFirstBottomSheetSelectedTextChanged -> {
                screenState = screenState.copy(
                    selectedTextFromFirstBottomSheet = event.text
                )
            }

            is MainScreenEvent.OnSecondBottomSheetSelectedTextChanged -> {
                screenState = screenState.copy(
                    selectedTextFromSecondBottomSheet = event.text
                )
            }
        }
    }
}

屏幕状态

data class MainScreenState(
    val selectedTextFromFirstBottomSheet: String = \"First Text b1\",
    val selectedTextFromSecondBottomSheet: String = \"Third Text b2\",
    val firstBottomSheetTextList: List<String> = listOf(
        \"First Text b1\",
        \"Second Text b1\",
        \"Third Text b1\",
        \"Fourth Text b1\",
        \"Five Text b1\"
    ),
    val secondBottomSheetTextList: List<String> = listOf(
        \"First Text b2\",
        \"Second Text b2\",
        \"Third Text b2\",
        \"Fourth Text b2\",
        \"Five Text b2\"
    )
)

屏幕事件

sealed class MainScreenEvent(){
    data class OnFirstBottomSheetSelectedTextChanged(val text: String): MainScreenEvent()
    data class OnSecondBottomSheetSelectedTextChanged(val text: String): MainScreenEvent()
}

第一张底页

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material.Checkbox
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp

@Composable
fun FirstBottomSheet(
    selectedText: String,
    textList: List<String>,
    onSelected: (text: String) -> Unit
) {

    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {

        textList.forEach { text ->

            Row(modifier = Modifier
                .fillMaxWidth()
                .toggleable(
                    value = selectedText == text,
                    role = Role.Checkbox,
                    onValueChange = { isSelected ->
                        if (isSelected) {
                            onSelected(text)
                        }
                    }
                )
                .padding(16.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {

                Text(text = text, modifier = Modifier.weight(1f))

                Checkbox(checked = selectedText == text, onCheckedChange = null)
            }
        }
    }
}

第二个底页

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material.Checkbox
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp

@Composable
fun SecondBottomSheet(
    selectedText: String,
    textList: List<String>,
    onSelected: (text: String) -> Unit
) {


    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {

        textList.forEach { text ->

            Row(modifier = Modifier
                .fillMaxWidth()
                .toggleable(
                    value = selectedText == text,
                    role = Role.Checkbox,
                    onValueChange = { isSelected ->
                        if (isSelected) {
                            onSelected(text)
                        }

                    }

                )
                .padding(16.dp),
                verticalAlignment = Alignment.CenterVertically) {

                Text(text = text, modifier = Modifier.weight(1f))

                Checkbox(checked = selectedText == text, onCheckedChange = null)
            }
        }
    }
}

谢谢你的帮助!

    标签: android android-jetpack-compose


    【解决方案1】:

    我复制并粘贴了您的代码。我所做的唯一更改是:

    1. MainScreen 中删除此行
      val screenState = viewModel.screenState
      
      1. 直接访问状态。
      Text(
          text = "Selected: ${viewModel.screenState.selectedTextFromFirstBottomSheet}",
          Modifier.padding(16.dp)
      )
      
      Button(onClick = {
          bottomSheetContent = {
              FirstBottomSheet(
                  selectedText = viewModel.screenState.selectedTextFromFirstBottomSheet,
                  onSelected = { text ->
                      viewModel.onEvent(
                          MainScreenEvent.OnFirstBottomSheetSelectedTextChanged(text)
                      )
                  },
                  textList = viewModel.screenState.firstBottomSheetTextList
              )
          }
      
          scope.launch {
              bottomSheetState.show()
          }
      }, modifier = Modifier.padding(16.dp)) {
          Text(text = " Open First BottomSheet")
      }
      

      繁荣!有效 :)

      我的理解是:您正在创建一个包含状态值的变量,但您没有监听状态更改,因此 Compose 不知道状态已更改,因此不会发生重组。状态声明中的 by 关键字是一个属性委托,它设置/获取状态的当前值,但不注册可组合以对这些更改做出反应。

      您可以使用另一种解决方案来观察状态而无需重复viewModel.screenState

      1. 使用derivedStateOf
        val screenState by remember {
            derivedStateOf {
                viewModel.screenState
            }
        }
        
        1. 更改screenState 声明。
        // Using "=" instead of "by"
        var screenState = mutableStateOf(MainScreenState())
        

        然后使用screenState.value 设置/获取状态值。 在屏幕中,使用如下:

        val screenState = viewModel.screenState
        

    【讨论】:

    • 您好,感谢您的帮助,它成功了!你能给我解释一下为什么吗?
    • 我用更多解释编辑了我的答案
    【解决方案2】:

    我的应用程序中有完全相同的用例,但在我更新了一些 compose 库之前一切正常。

    【讨论】:

      猜你喜欢
      • 2019-02-24
      • 2022-11-01
      • 2018-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-29
      相关资源
      最近更新 更多