Activity 启动器(文件选择器)在单个事件中加载多次 - Jetpack compose

Activity Launcher(File picker) is loading multiple times in single event - Jetpack compose

我在 Jetpack Compose 中的 Horizo​​ntalPager 中使用文件选择器。当点击按钮加载相应的屏幕时,启动器被触发 2 次。

代码片段

var openFileManager by remember {
        mutableStateOf(false)
}

if (openFileManager) {
     launcher.launch("*/*")
}

Button(text = "Upload",
        onClick = {
            openFileManager = true
    })

已编辑:首先,Ian 的观点是正确的,为什么不直接在 onClick 中启动它?我还假设您可能想用 true false 值做更多事情。如果你只想启动,那么这些都是无用的。

当您点击并使 openFileManager 为真时,屏幕可以绘制多次,因此仅使用条件不会阻止它多次调用。

您可以用 LaunchedEffectopenFileManager 作为键来包装您的代码。 LaunchedEffect 块将 运行 仅当您的 openFileManager 更改时。

if (openFileManager) {
    LaunchedEffect(openFileManager) {
         launcher.launch("*/*")
    }
}

你永远不应该在 @Composable 中存储如此重要的状态。如此重要的业务逻辑应该存储在更强大的容器中,例如 ViewModel.

ViewModel{
  var launch by mutableStateOf (false)
  private set
 
  fun updateLaunchValue(newValue: Boolean){
    launch = newValue
  }
}

将这些从主 activity

传递给 Composable
MyComposable(
  launchValue = viewModel.launch
  updateLaunchValue = viewModel::updateLaunchValue
)

根据需要在可组合项中创建参数

@Comoosable
fun Uploader(launchValue: Boolean, onUpdateLaunchValue: (Boolean) -> Unit){
  LaunchedEffect (launchValue){
    if (launchValue)
      launcher.launch(...)
  }
  
  Button { // This is onClick
    onUpdateLaunchValue(true) // makes the value true in the vm, updating state
  }
}

如果您认为它过于复杂,那您就错了范式。这是在 Compose 或任何声明性范例中处理状态的推荐和正确的方式,真的 afaik。这保持代码干净,同时完全分离 UI 和数据层,允许 UI 和状态之间的受控交互以实现应用程序的完美行为。