Search code examples
c#mauiviewmodel

How to Reinitialize Singleton Pages and ViewModels on User Logout and Login in a MAUI Shell App?


Problem Description

I am developing a MAUI app that utilizes Shell tabs alongside a login flow. Each tab’s page and corresponding ViewModel are registered as singletons. Upon the initial load, the pages use the OnAppearing method to call an initialization method from the ViewModel, which fetches data from a local database (using LiteDb) and loads it into an observable collection for the user to view.

Issue Encountered

The application allows the current user to log out, with the following flow:

try
{
    SecureStorage.Default.RemoveAll(); // Clears the session of the logged-in user
    await _repositories.ClearAsync(); // Clears all data created by the current user

    await Shell.Current.GoToAsync($"//{nameof(SignInUpPage)}");
}
catch (Exception)
{
    Toast.Show("Something went wrong");
}

After navigating back to the SignInUpPage and logging in again, the data from the previous session is still visible on the main tab pages. This is due to the pages and ViewModels being registered as singletons.

Question

What is the best approach to ensure that these pages and ViewModels are destroyed or reinitialized upon a new login? The goal is to have fresh instances that reload data appropriately when a user logs in after logging out.

Page setup

In the MainShellPage.xaml I have the following:

<ShellContent
        Title="Loading"
        ContentTemplate="{DataTemplate pages:LoadingPage}"
        Route="LoadingPage" />

    <ShellContent ContentTemplate="{DataTemplate account:SignInUpPage}" Route="SignInUpPage" />

    <TabBar Route="MainShellPage">
        <Tab Title="{Binding Dashboard.Title}" Icon="{Binding Dashboard.Icon}">
            <ShellContent ContentTemplate="{DataTemplate dashboard:DashboardMainPage}" />
        </Tab> Another tabs .. </TabBar>

When LoadingPage starts in its own viewmodel it decides if the user is authenticated or not.

If user is authenticated it goes to:

 await Shell.Current.GoToAsync($"//{nameof(MainShellPage)}")

Otherwise

await Shell.Current.GoToAsync($"//{nameof(SignInUpage)}")

The reason of the Loading page:

  • decide user is authenticated
  • if default data init was not done yet it fetches data from web api and save into local db
  • if user was working on something and the app was closed here it checks if there is cached data and load to the app to continue on

Solution

  • I swiched the all tabs pages and viewmodels to transients, and when logout happnes I do the following to reinit MainPage.

    try
    {
        SecureStorage.Default.RemoveAll(); // Clears the session of the logged-in user
        await _repositories.ClearAsync(); // Clears all data created by the current user 
        App.Current.MainPage = new MainShellPage(); // Reinit main page -> when login again all pages and vms will be reinit
        await Shell.Current.GoToAsync($"//{nameof(SignInUpPage)}");
    }
    catch (Exception)
    {
        Toast.Show("Something went wrong");
    }