Issue
ViewModel
does not preserve state during configuration changes, e.g., leaving and returning to the app when switching between background apps.
@HiltViewModel
class MainViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
) : ViewModel()
{
var title by mutableStateOf("")
internal set
var showMenu by mutableStateOf(false)
internal set
var tosVisible by mutableStateOf(false)
internal set
}
The menu:
Currently: It survives the rotation configuration change, the menu remains open if opened by clicking on the three ... dots there. But, changing app, i.e. leaving the app and going into another app. Then returning, does not preserve the state as expected. What am I possibly doing wrong here?
In MainActivity:
val mainViewModel by viewModels<MainViewModel>()
Main(mainViewModel) // Passing it here
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Main(viewModel: MainViewModel = viewModel()) {
val context = LocalContext.current
val navController = rememberNavController()
EDIT: Modified my ViewModel to this, Makes no difference.
@HiltViewModel
class MainViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
) : ViewModel()
{
var title by mutableStateOf("")
internal set
var showMenu by mutableStateOf(savedStateHandle["MenuOpenState"] ?: false)
internal set
var tosVisible by mutableStateOf(savedStateHandle["AboutDialogState"] ?: false)
internal set
fun displayAboutDialog(){
savedStateHandle["AboutDialogState"] = tosVisible;
}
fun openMainMenu(){
savedStateHandle["MenuOpenState"] = showMenu;
}
}
Full code of the MainActivity:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Main(viewModel: MainViewModel) {
val context = LocalContext.current
val navController = rememberNavController()
//val scope = rememberCoroutineScope()
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
decayAnimationSpec,
rememberTopAppBarScrollState()
)
LaunchedEffect(navController){
navController.currentBackStackEntryFlow.collect{backStackEntry ->
Log.d("App", backStackEntry.destination.route.toString())
viewModel.title = getTitleByRoute(context, backStackEntry.destination.route);
}
}
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(
viewModel.title,
//color = Color(0xFF1877F2),
style = MaterialTheme.typography.headlineSmall,
)
},
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = MaterialTheme.colorScheme.background
),
actions = {
IconButton(
onClick = {
viewModel.showMenu = !viewModel.showMenu
}) {
Icon(imageVector = Icons.Outlined.MoreVert, contentDescription = "")
MaterialTheme(shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(20.dp))) {
IconButton(
onClick = { viewModel.showMenu = !viewModel.showMenu }) {
Icon(imageVector = Icons.Outlined.MoreVert, contentDescription = "")
DropdownMenu(
expanded = viewModel.showMenu,
onDismissRequest = { viewModel.showMenu = false },
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.padding(0.dp),
properties = PopupProperties(focusable = true)
) {
DropdownMenuItem(text = { Text("Sign out", fontSize = 16.sp) }, onClick = { viewModel.showMenu = false })
DropdownMenuItem(text = { Text("Settings", fontSize = 16.sp) }, onClick = { viewModel.showMenu = false })
Divider(color = Color.LightGray, thickness = 1.dp)
DropdownMenuItem(text = { Text("About", fontSize = 16.sp) },
onClick = {
viewModel.showMenu = true
viewModel.tosVisible = true
})
}
}
}
}
},
scrollBehavior = scrollBehavior
) },
bottomBar = { BottomAppBar(navHostController = navController) }
) { innerPadding ->
Box(modifier = Modifier.padding(PaddingValues(0.dp, innerPadding.calculateTopPadding(), 0.dp, innerPadding.calculateBottomPadding()))) {
BottomNavigationGraph(navController = navController)
}
}
}
Solution
Solved with this:
@HiltViewModel
class MainViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
) : ViewModel()
{
companion object {
const val UI_MENU_STATE = "ui.menu.state"
}
init {
savedStateHandle.get<Boolean>(UI_MENU_STATE)?.let {
m -> onMenuStateChange(m);
}
}
private var m_title by mutableStateOf("")
private var displayMenu by mutableStateOf( false)
var tosVisible by mutableStateOf( false)
internal set
fun updateTitleState(title: String){
m_title = title;
}
fun onMenuStateChange(open: Boolean){
Log.d("App", open.toString());
displayMenu = open;
savedStateHandle.set<Boolean>(UI_MENU_STATE, displayMenu);
}
fun isMenuOpen(): Boolean {
return displayMenu;
}
fun getTitle(): String { return m_title; }
}
Answered By - Alix Blaine
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.