我应用背后的逻辑是:
我输入一本书,我想搜索并按搜索。我从ViewModel中调用该函数,该函数从服务器中获取书籍列表并导航到BookListsCreen.
BookListsCreen
BookDetailsscreen:显示有关此书之前获取的详细信息。
书bookdetails中,然后回到bookList
。这意味着我应该在中进行searchBooks()
。
对于booklist而言,同一件事。当我回到
QueryScreen时,我应该在书booklistuistate.中设置
searchBook()
。
最好的方法是什么?假设我有20个屏幕,每个屏幕上都有一个旗帜,以跟踪我是否要回去。
如果您可以向我解释两种方法,我将感激不已,谢谢!!
这些是我的州课:
val book: Book = null
这是我在屏幕之间执行导航的方式:
val books: List<Book> = emptyList()
这是我的ViewModel类:
data class QueryUiState(
val query: String = "",
val isFetchingBooks: Boolean = false,
val errorMessage: String? = null
)
data class BookListUiState(
val books: List<Book> = emptyList(),
val isFetchingBook: Boolean = false,
val errorMessage: String? = null,
val selectedBook: Book? = null
)
data class BookDetailsUiState(
val book: Book? = null
)
我在这里看到两种可能的方法。 aptiona
您可以尝试使用
object BookshelfDestinations {
const val QUERY_ROUTE = "query"
const val BOOK_LIST_ROUTE = "bookList"
const val BOOK_DETAILS_ROUTE = "bookDetails"
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BookshelfApp(
appContainer: AppContainer,
navController: NavHostController = rememberNavController(),
modifier: Modifier = Modifier
) {
Scaffold { innerPadding ->
BookshelfNavHost(
appContainer = appContainer,
navController = navController,
modifier = modifier.padding(innerPadding)
)
}
}
@Composable
fun BookshelfNavHost(
appContainer: AppContainer,
navController: NavHostController,
modifier: Modifier = Modifier
) {
val viewModel: AppViewModel =
viewModel(factory = AppViewModel.AppViewModelFactory(appContainer.bookshelfRepository))
NavHost(
navController = navController,
startDestination = BookshelfDestinations.QUERY_ROUTE,
modifier = modifier
) {
composable(route = BookshelfDestinations.QUERY_ROUTE) {
QueryScreen(
viewModel = viewModel,
onSearch = {
navController.navigate(BookshelfDestinations.BOOK_LIST_ROUTE)
}
)
}
composable(route = BookshelfDestinations.BOOK_LIST_ROUTE) {
BookListScreen(
viewModel = viewModel,
onBookClicked = {
navController.navigate(BookshelfDestinations.BOOK_DETAILS_ROUTE)
}
)
}
composable(route = BookshelfDestinations.BOOK_DETAILS_ROUTE) {
BookDetailsScreen(
viewModel = viewModel
)
}
}
}
class AppViewModel(private val repository: BookshelfRepository) : ViewModel() {
private val _queryUiState = MutableStateFlow(QueryUiState())
val queryUiState: StateFlow<QueryUiState> = _queryUiState.asStateFlow()
private val _bookListUiState = MutableStateFlow(BookListUiState())
val bookListUiState: StateFlow<BookListUiState> = _bookListUiState.asStateFlow()
private val _bookDetailsUiState = MutableStateFlow(BookDetailsUiState())
val bookDetailsUiState: StateFlow<BookDetailsUiState> = _bookDetailsUiState.asStateFlow()
init {
Log.d("AppViewModel", "App view model initialized!")
}
fun onQueryChanged(query: String) {
_queryUiState.value = _queryUiState.value.copy(query = query)
}
fun onBookSelected(book: Book) {
_bookListUiState.value = _bookListUiState.value.copy(selectedBook = book)
searchBook()
}
fun searchBooks() {
viewModelScope.launch {
_queryUiState.value = _queryUiState.value.copy(isFetchingBooks = true)
try {
val result = repository.getBooks(_queryUiState.value.query)
when(result) {
is BookshelfResult.Success -> {
_bookListUiState.value = _bookListUiState.value.copy(books = result.data)
}
is BookshelfResult.Error -> {
_queryUiState.value = _queryUiState.value.copy(errorMessage = result.exception.message)
}
}
} catch (e: Exception) {
_queryUiState.value = _queryUiState.value.copy(errorMessage = e.message)
} finally {
_queryUiState.value = _queryUiState.value.copy(isFetchingBooks = false)
}
}
}
fun searchBook() {
viewModelScope.launch {
_bookListUiState.value = _bookListUiState.value.copy(isFetchingBook = true)
try {
val result = repository.getBook(_bookListUiState.value.selectedBook!!.id)
when(result) {
is BookshelfResult.Success -> {
_bookDetailsUiState.value = _bookDetailsUiState.value.copy(book = result.data)
}
is BookshelfResult.Error -> {
_bookListUiState.value = _bookListUiState.value.copy(errorMessage = result.exception.message)
}
}
} catch (e: Exception) {
_bookListUiState.value = _bookListUiState.value.copy(errorMessage = e.message)
} finally {
_bookListUiState.value = _bookListUiState.value.copy(isFetchingBook = false)
}
}
}
class AppViewModelFactory(private val appRepository: BookshelfRepository) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(AppViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return AppViewModel(appRepository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
}
BackHandler
BookDetailsScreen(
viewModel: AppViewModel,
onBack: () -> Unit
) {
`BackHandler` {
viewModel.resetBook()
onBack()
}
}
我认为更干净的是为您的ViewModel选择适当的Scope。当前,您的ViewModel不在NAVHOST之外,因此范围为活动。如果您有三个较小的ViewModels,则可以将它们调整到Navgraph中的目的地,然后每当您离开此目的地时将它们重置。 您可以尝试这样的代码:
NavHost
功能是以下依赖性中包含的便利函数:
composable(route = BookshelfDestinations.BOOK_DETAILS_ROUTE) {
BookDetailsScreen(
viewModel = viewModel,
onBack = {
navController.popBackStack()
}
)
}
它会自动生成一个ViewModel实例,而无需手动工厂类别。,然后,您可以使用“官方文档”中所述的导航参数将所需的数据(例如书ID)从一个目的地传递到下一个目的地。还可以查看
nevigation document。