5

因此,在 Jetpack Compose 中似乎推荐的做法是从您的可组合项中提升状态,使它们成为无状态、可重用和可测试的,并允许在预览中轻松使用它们。所以而不是有类似的东西

@Composable
fun MyInputField() {
    var text by remember { mutableStateOf("") }
    TextField(value = text, onValueChange = { text = it })
}

你会像这样提升状态

@Composable
fun MyInputField(text: String, onTextChange: (String) -> Unit) {
    TextField(value = text, onValueChange = onTextChange)
}

这很好,但是一些更复杂的用途呢?假设我有一个由可组合对象表示的屏幕,在 View 和 ViewModel 之间有多个交互。这个屏幕被分成多个内部可组合(例如,一个用于标题,一个用于正文,然后又分为几个较小的可组合)

  • 您不能在可组合物中创建 ViewModel(viewModel()至少,您可以手动实例化一个)并在 a 中使用此可组合物Preview(预览不支持像这样创建视图模型)
  • 在内部可组合项中使用 ViewModel 会使它们成为有状态的,不是吗?

因此,我看到的“最干净”的解决方案是仅在最高可组合级别实例化我的视图模型,然后将仅val代表状态的子可组合传递给子组件,并回调 ViewModel 函数。

但这很疯狂,我不会通过单个参数将我的所有 ViewModel 状态和函数传递给所有需要它们的可组合组件。data class例如,将它们分组可能是一个解决方案

data class UiState(
  val textInput: String,
  val numberPicked: Int,
  ……

也许为回调创建另一个?但这仍然是创建一个全新的类,只是为了模仿视图模型已经拥有的东西。

我实际上并没有看到这样做的最佳方法是什么,而且我在任何地方都没有找到任何相关信息

4

2 回答 2

1

管理复杂状态的一个好方法是将所需的复杂行为封装到一个类中并使用记住功能,同时尽可能拥有无状态小部件,并在需要时更改状态的任何属性。

SearchTextField是一个仅使用状态提升的组件,SearchBar具有后退箭头,SearchTextField并且本身也是一个无状态可组合。这两者和 Searchbar 的父级之间的通信仅通过回调函数处理,这使得 SearchTextField 可以重新使用并且易于预览,并在预览中具有默认状态。HomeScreen包含此状态以及您管理更改的位置。

完整的实现发布在这里

@Composable
fun <R, S> rememberSearchState(
    query: TextFieldValue = TextFieldValue(""),
    focused: Boolean = false,
    searching: Boolean = false,
    suggestions: List<S> = emptyList(),
    searchResults: List<R> = emptyList()
): SearchState<R, S> {
    return remember {
        SearchState(
            query = query,
            focused = focused,
            searching = searching,
            suggestions = suggestions,
            searchResults = searchResults
        )
    }
}

请记住保持状态的功能,仅在合成期间对其进行评估。

class SearchState<R, S>(
    query: TextFieldValue,
    focused: Boolean,
    searching: Boolean,
    suggestions: List<S>,
    searchResults: List<R>
) {
    var query by mutableStateOf(query)
    var focused by mutableStateOf(focused)
    var searching by mutableStateOf(searching)
    var suggestions by mutableStateOf(suggestions)
    var searchResults by mutableStateOf(searchResults)

    val searchDisplay: SearchDisplay
        get() = when {
            !focused && query.text.isEmpty() -> SearchDisplay.InitialResults
            focused && query.text.isEmpty() -> SearchDisplay.Suggestions
            searchResults.isEmpty() -> SearchDisplay.NoResults
            else -> SearchDisplay.Results
        }
}

并通过将状态传递给其他可组合或 ViewModel 来更改 UI 任何部分的状态

fun HomeScreen(
    modifier: Modifier = Modifier,
    viewModel: HomeViewModel,
    navigateToTutorial: (String) -> Unit,
    state: SearchState<TutorialSectionModel, SuggestionModel> = rememberSearchState()
) {


    Column(
        modifier = modifier.fillMaxSize()
    ) {
            
        SearchBar(
            query = state.query,
            onQueryChange = { state.query = it },
            onSearchFocusChange = { state.focused = it },
            onClearQuery = { state.query = TextFieldValue("") },
            onBack = { state.query = TextFieldValue("") },
            searching = state.searching,
            focused = state.focused,
            modifier = modifier
        )

        LaunchedEffect(state.query.text) {
            state.searching = true
            delay(100)
            state.searchResults = viewModel.getTutorials(state.query.text)
            state.searching = false
        }

        when (state.searchDisplay) {
            SearchDisplay.InitialResults -> {

            }
            SearchDisplay.NoResults -> {

            }

            SearchDisplay.Suggestions -> {

            }

            SearchDisplay.Results -> {
 
            }
        }
    }
}
于 2021-10-25T06:39:26.043 回答
0

Jetmagic 是一个开源框架,可以准确处理这个问题,同时还解决了 Google 在开发 Compose 时忽略的其他主要问题。关于您的请求,您根本不会将视图模型作为参数传递。Jetmagic 遵循“提升状态”模式,但它会为您管理视图模型并使它们与您的可组合项相关联。它将可组合组件视为资源,其方式类似于旧视图系统处理 xml 布局的方式。您无需直接调用可组合函数,而是要求 Jetmagic 的框架为您提供与设备配置最匹配的可组合“实例”。请记住,在较旧的基于 xml 的系统下,您可以有效地为同一个屏幕设置多种布局(例如,一种用于纵向模式,另一种用于横向模式)。Jetmagic 会为您挑选正确的。当它执行此操作时,它会为您提供一个对象,用于管理可组合的状态及其相关的视图模型。

您可以在屏幕层次结构中的任何位置轻松访问视图模型,而无需将视图模型作为参数向下传递层次结构。这部分是使用 CompositionLocalProvider 完成的。

Jetmagic 旨在处理构成屏幕的顶级可组合项。在可组合层次结构中,您仍然像往常一样调用可组合对象,但在有意义的地方使用状态提升。

最好的办法是下载 Jetmagic 并尝试一下。它有一个很棒的演示,说明了您正在寻找的解决方案:

https://github.com/JohannBlake/Jetmagic

于 2021-10-25T06:24:10.097 回答