【问题标题】:Jetpack Compose passing LiveData to a Composable lambdaJetpack Compose 将 LiveData 传递给 Composable lambda
【发布时间】:2022-09-25 10:22:36
【问题描述】:

我无法理解为什么我观察到的值作为状态被正确传递到 Composable lambda 中,但不会触发可组合的重构。

这是我的设置。

屏幕

@Composable
fun Screen(
    showBottomSheet: (@Composable () -> Unit) -> Unit,
    viewModel: MyViewModel = hiltViewModel()
) {
    val someValue = viewModel.someValue.observeAsState().value

    MyController(
        onShowBottomSheetClick = {
            showBottomSheet {
                Log.d(\"DEBUG\", \"value updated: $someValue\")
                BottomSheetLayout(
                    someValue = someValue,
                    onUpdateClick = { newValue -> viewModel.setValue(newValue) }
                )
            }
        }
    )
}

我的控制器

@Composable
fun MyController(
    onShowBottomSheetClick: () -> Unit
) {
    Text(
        text = \"show BottomSheet\",
        modifier = Modifier.clickable { onShowBottomSheetClick() }
    )
}

底页布局

@Composable
fun BottomSheetLayout(
    someValue: Int,
    onUpdateClick: (Int) -> Unit
) {
    Text(
        text = \"Some value: $someValue\",
        modifier = Modifier.clickable { onUpdateClick(someValue + 1) }
    )
}

我的视图模型

@HiltViewModel
class MyViewModel @Inject constructor(): ViewModel() {
    private val _someValue: Int? = MutableLiveData(null)
    val someValue: LiveData<Int?> = _someValue

    fun setValue(newValue: Int) {
        _someValue.value = newValue
    }
}

当我运行它并查看输出时,日志不会显示更新的someValue

value updated: 0
value updated: 0
value updated: 0

但是,如果我向 lambda 本身添加一个新的状态值,它就可以正常工作。

// Screen
// ..
    showBottomSheet {
        val currentValue = viewModel.someValue.observeAsState().value // <-- Note the difference
        Log.d(\"DEBUG\", \"value updated: $currentValue\")
        BottomSheetLayout(
            someValue = currentValue,
            onUpdateClick = { newValue -> viewModel.setValue(newValue) }
        )
    }
// ..

当我运行它并查看输出时,日志会显示正确更新的值。

value updated: 0
value updated: 1
value updated: 2

为什么会这样?有什么方法可以将第一个状态值传递给 showBottomSheet 可组合 lambda?

先感谢您。

  • 尝试使用collectAsState 而不是observeAsState。这有什么改变吗?
  • @m.reiter 抱歉,我的意思是 LiveData,而不是 StateFlow,因为我需要初始值为 null D:我编辑了我的问题。

标签: android android-jetpack-compose kotlin-stateflow


【解决方案1】:

Log.d 是一个副作用并且应该与它的处理程序之一一起使用。

read more about Jetpack Compose Side-Effects

使用 LaunchedEffect 或 DisposableEffect

@Composable
fun Screen(
    showBottomSheet: (@Composable () -> Unit) -> Unit,
    viewModel: MyViewModel = hiltViewModel()
) {
    val someValue = viewModel.someValue.observeAsState().value
    
    LaunchedEffect(someValue) {
        Log.d("DEBUG", "value updated: $someValue")
    }

    MyController(
        onShowBottomSheetClick = {
            showBottomSheet {
                BottomSheetLayout(
                    someValue = someValue,
                    onUpdateClick = { newValue -> viewModel.setValue(newValue) }
                )
            }
        }
    )
}

每次您的值更改时都会启动效果

【讨论】:

  • 我不是在问如何正确打印输出日志消息。我期待 BottomSheetLayout Composable 重新组合,因为 someValue 发生了变化,但事实并非如此。
【解决方案2】:

我没有尝试过你的代码,但这看起来像是这个问题中范围重组的效果。在那个问题中,即使没有记住 mutableState 值也会更新,因为外部范围没有被重组。在您的情况下,因为您传递的是值而不是由于未触发重组而未更新的状态。

Why does mutableStateOf without remember work sometimes?

@Composable
fun Screen(
    showBottomSheet: (@Composable () -> Unit) -> Unit,
    viewModel: MyViewModel = hiltViewModel()
) {
    val someValue = viewModel.someValue.observeAsState().value

    MyController(
        onShowBottomSheetClick = {
            showBottomSheet {
                Log.d("DEBUG", "value updated: $someValue")
                BottomSheetLayout(
                    someValue = someValue,
                    onUpdateClick = { newValue -> viewModel.setValue(newValue) }
                )
            }
        }
    )
}

在此代码中,仅重构了 showBottomSheet,并且由于在 MyController 范围内没有重构,因此作为值的 someValue 不会更新。

使用此代码,我假设 showBottomSheet 是可组合的,因为您可以调用 @Composable 函数 Flow.collectAsState 您正在重新组合内部的所有内容,这就是它使用 viewModel.someValue 的最新值重新组合的原因

showBottomSheet {
    val currentValue = viewModel.someValue.observeAsState().value // <-- Note the difference
    Log.d("DEBUG", "value updated: $currentValue")
    BottomSheetLayout(
        someValue = currentValue,
        onUpdateClick = { newValue -> viewModel.setValue(newValue) }
    )
}

【讨论】:

    【解决方案3】:

    使用代理

    这样,每次您的值发生变化时都会引起重组。

    val someValue by viewModel.someValue.observeAsState()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-31
      • 2022-01-21
      • 2023-02-16
      • 1970-01-01
      • 2023-01-05
      • 2021-04-15
      • 1970-01-01
      相关资源
      最近更新 更多