如何使用视图模型构造函数将数据保存在可组合函数中



我有一个用于主屏幕的可组合函数,每当我导航到新的可组合函数并返回此函数时,都会重新创建所有内容。我是否必须将视图模型移动到更高的位置? 主屏幕

@Composable
fun HomeScreen(viewModel: NFTViewModel = viewModel()) {
val feedState by viewModel.nftResponse.observeAsState()

if (feedState?.status == Result.Status.SUCCESS) {
val nfts = feedState?.data?.content
LazyColumn {
itemsIndexed(nfts!!) { index, item ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(15.dp)
.clickable { },
elevation = 8.dp
) {
Column(
modifier = Modifier.padding(15.dp)
) {
Image(
painter = rememberAsyncImagePainter(
item.firebaseUri
),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.height(250.dp)
)
Text(
buildAnnotatedString {
append("Contract Address: ")
withStyle(
style = SpanStyle(fontWeight = FontWeight.W900, color = Color(0xFF4552B8))
) {
append(item.contractAddress)
}
}
)
Text(
buildAnnotatedString {
append("Chain: ")
withStyle(style = SpanStyle(fontWeight = FontWeight.W900)) {
append(item.chain.displayName)
}
append("")
}
)
Text(
buildAnnotatedString {
append("Price: ")
withStyle(style = SpanStyle(fontWeight = FontWeight.W900)) {
append(item.price)
}
append("")
}
)
}
}
}
}
} else {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.white))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Feed",
fontWeight = FontWeight.Bold,
color = Color.DarkGray,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
}

导航

@Composable
fun Navigation(navController: NavHostController) {
NavHost(navController, startDestination = NavigationItem.Home.route) {
composable(NavigationItem.Home.route) {
val vm: NFTViewModel = viewModel()
HomeScreen(vm)
}
composable(NavigationItem.Add.route) {
AddScreen()
}
composable(NavigationItem.Wallets.route) {
WalletsScreen()
}
composable(NavigationItem.Popular.route) {
PopularScreen()
}
composable(NavigationItem.Profile.route) {
ProfileScreen()
}
}
}

似乎我需要将视图模型保存为状态或其他东西?我似乎错过了什么。基本上我不想每次调用可组合函数时都触发 getFeed(),我应该将数据保存在视图模型中的 compose 函数中的什么位置?

现在编辑从视图模型中的初始化调用getFeed

class NFTViewModel : ViewModel() {
private val nftRepository = NFTRepository()
private var successResult: Result<NftResponse>? = null
private var _nftResponse = MutableLiveData<Result<NftResponse>>()
val nftResponse: LiveData<Result<NftResponse>>
get() = _nftResponse
init {
successResult?.let {
_nftResponse.postValue(successResult!!)
} ?: kotlin.run {
getFeed() //this is always called still successResult is always null
}
}
private fun getFeed() {
viewModelScope.launch {
_nftResponse.postValue(Result(Result.Status.LOADING, null, null))
val data = nftRepository.getFeed()
if (data.status == Result.Status.SUCCESS) {
successResult = data
_nftResponse.postValue(Result(Result.Status.SUCCESS, data.data, data.message))
} else {
_nftResponse.postValue(Result(Result.Status.ERROR, null, data.message))
}
}
}
override fun onCleared() {
Timber.tag(Constants.TIMBER).d("onCleared")
super.onCleared()
}
}

但是,似乎仍在创建新的视图模型。

似乎我需要将视图模型保存为状态或其他东西?

你不必这样做。ViewModel已作为其所有者范围的一部分保留。如果您正确检索ViewModel,则将返回相同的ViewModel实例。

我似乎错过了什么。

每次导航可组合重构(被调用)时,您都在初始化NFTViewModel的新实例,而不是从其ViewModelStoreOwner中检索NFTViewModel

您应该通过调用viewModel()来检索ViewModel,或者如果您使用的是 Hilt 和@HiltViewModel则改为调用hiltViewModel()

无剑柄

val vm: NFTViewModel = viewModel()

返回现有ViewModel或在给定所有者(通常是片段或活动)中创建新所有者,默认为LocalViewModelStoreOwner提供的所有者。 创建的ViewModel与给定的viewModelStoreOwner相关联,只要所有者还活着(例如,如果它是一个活动,直到它完成或进程被终止),就会被保留。

如果使用 Hilt (即您的ViewModel具有@HiltViewModel注释)

val vm: NFTViewModel = hiltViewModel()

返回现有的@HiltViewModel批注ViewModel或创建一个作用域为NavController后退堆栈上存在的当前导航图的新。 如果当前不存在导航图,则将使用当前范围,通常是片段或活动。

以上将保留您的视图模型状态,但是如果您的可组合对象退出合成,然后在以后重新进入它,您仍然会在可组合物中重置状态,每次导航到不同的屏幕(到不同的"屏幕"可组合物,如果它只是一个对话框,那么以前的可组合对象不会离开合成, 因为它仍将显示在后台)。

由于这部分代码

@Composable
fun HomeScreen(viewModel: NFTViewModel) {
val feedState by viewModel.nftResponse.observeAsState()
// this resets to false when HomeScreen leaves and later
// re-enters the composition
val fetched = remember { mutableStateOf(false) }
if (!fetched.value) {
fetched.value = true
viewModel.getFeed()
}

当您导航到(和返回)HomeScreen时,fetched将始终为 false,因此将调用getFeed()。 如果在导航回HomeScreen时不想调用getFeed(),则必须将fetched值存储在其他位置(可能NFTViewModel中),并且仅在希望再次调用该getFeed()时才将其重置为false

最新更新