Share via

rethinking user flow

Eduardo Gomez 4,316 Reputation points
2024-03-02T15:31:31.4966667+00:00

I am coding my user flow but this time I am going to dynamically create the menu.

So, in my shell, I have two pages.

  • Startup Page
  • Login Page

The startup page is responsible for checking for the token and redirect the user.

public class StartupPageViewModel : BaseViewModel {
    readonly IAppService _appService;
    public StartupPageViewModel(IAppService appService) {
        _appService = appService;
        HandleNavigation(_appService);
    }
    private async void HandleNavigation(IAppService appService) {
        var currentUserEmail = await SecureStorage.GetAsync("CurrentUser");
        if(string.IsNullOrEmpty(currentUserEmail)) {
            await appService.NavigateTo($"//{nameof(LoginPage)}");
        } else {
            await appService.NavigateTo($"//{nameof(WelcomePage)}");
        }
    }
}



<Shell
    x:Class="DemyAI.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:badge="clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core"
    xmlns:helpers="clr-namespace:DemyAI.Helpers"
    xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    xmlns:pages="clr-namespace:DemyAI.Views"
    xmlns:sfavatar="clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core"
    xmlns:vm="clr-namespace:DemyAI.ViewModels"
    Title="DemyAI"
    x:DataType="vm:AppShellViewModel"
    FlyoutBehavior="{OnIdiom Desktop=Locked,
                             Phone=Flyout}">
    <Shell.FlyoutHeader>
        <Grid>
            <BoxView
                BackgroundColor="#522cd4"
                HeightRequest="142" />
            <VerticalStackLayout>
                <Image
                    HeightRequest="80"
                    Source="user.png"
                    WidthRequest="80" />
                <Label
                    HorizontalTextAlignment="Center"
                    Text="{Binding User.DemyId}"
                    TextColor="White" />
                <Label
                    HorizontalTextAlignment="Center"
                    Text="{Binding User.Email}"
                    TextColor="White" />
                <Label
                    HorizontalTextAlignment="Center"
                    Text="{Binding User.Role}"
                    TextColor="White" />
            </VerticalStackLayout>
        </Grid>
    </Shell.FlyoutHeader>
    <Shell.FlyoutFooterTemplate>
        <DataTemplate>
            <Button
                Margin="20"
                Command="{Binding SignOutCommand}"
                CornerRadius="8"
                Text="Log out" />
        </DataTemplate>
    </Shell.FlyoutFooterTemplate>
    <ShellContent
        ContentTemplate="{DataTemplate pages:StartupPage}"
        Route="StartupPage"
        Shell.FlyoutBehavior="Disabled" />
    <ShellContent
        ContentTemplate="{DataTemplate pages:LoginPage}"
        Route="loginPage"
        Shell.FlyoutBehavior="Disabled" />
</Shell>


I put all the navigation in an interface

public interface IAppService {
    
    Task NavigateTo(string pageName, bool isAnimated, Dictionary<string, object> obj);
    Task NavigateTo(string pageName);
    Task DisplayToast(string message, ToastDuration toastDuration, double fontSize);
    Task DisplayAlert(string title, string message, string cancelMessage);
    Task NavigateBack();
}


public class AppService : IAppService {
    public async Task DisplayAlert(string title, string message, string cancelMessage) {
        await Shell.Current.DisplayAlert(title, message, cancelMessage);
    }
    public async Task DisplayToast(string message, ToastDuration toastDuration, double fontSize) {
        var toast = Toast.Make(message, toastDuration, fontSize);
        await toast.Show();
    }
    public async Task NavigateTo(string pageName, bool isAnimated, Dictionary<string, object> obj) {
        await Shell.Current.GoToAsync(pageName, isAnimated, obj);
    }
    public async Task NavigateTo(string pageName) {
        await Shell.Current.GoToAsync(pageName, true);
    }
    public async Task NavigateBack() {
        await Shell.Current.GoToAsync("..");
    }
}


But a am getting this error.

System.Exception: 'Global routes currently cannot be the only page on the stack, so absolute routing to global routes is not supported. For now, just navigate to: LoginPage'


also, I don't want my flyout the be visible in those Login page.

I am basically trying to replicate this.

https://www.youtube.com/watch?v=lSmRAV5IIBs

Developer technologies | .NET | .NET Multi-platform App UI

Answer accepted by question author

Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 50,166 Reputation points Microsoft External Staff
2024-03-05T05:33:40.0833333+00:00

Hello,

For login, you could do this by separating the two pages from the shell as separate ContentPages.

For example, when the program starts, you can enter StarUpPage to verify the user's login status. If the user is logged in, replace the page with a shell, or if not, go to the login page.

You can implement it with the following code:

App.Current.MainPage = StartupPage();

App.Current.MainPage = LoginPage();

App.Current.MainPage = AppShell();

The benefits of using this approach are twofold.

  • Compared with using AppShell to determine whether it is hidden, this method is more simple and easy to operate.
  • In this way, the shell is completely isolated, and the user is prevented from using some hacking to manipulate the hidden Shell when logging in.

Best Regards,

Alec Liu.


If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

Was this answer helpful?


1 additional answer

Sort by: Most helpful
  1. Eduardo Gomez 4,316 Reputation points
    2024-03-04T21:31:47.26+00:00

    But I still got error when I navigate to times to some pages look. https://reccloud.com/u/fvnmdgc

    Shell
        <Shell.FlyoutHeader>
            <Grid>
                <BoxView
                    BackgroundColor="#522cd4"
                    HeightRequest="142" />
                <VerticalStackLayout>
                    <Image
                        HeightRequest="80"
                        Source="user.png"
                        WidthRequest="80" />
                    <Label
                        HorizontalTextAlignment="Center"
                        Text="{Binding User.DemyId}"
                        TextColor="White" />
                    <Label
                        HorizontalTextAlignment="Center"
                        Text="{Binding User.Email}"
                        TextColor="White" />
                    <Label
                        HorizontalTextAlignment="Center"
                        Text="{Binding User.RolesAssigned}"
                        TextColor="White" />
                </VerticalStackLayout>
            </Grid>
        </Shell.FlyoutHeader>
        <Shell.FlyoutFooterTemplate>
            <DataTemplate>
                <Button
                    Margin="20"
                    Command="{Binding SignOutCommand}"
                    CornerRadius="8"
                    Text="Log out" />
            </DataTemplate>
        </Shell.FlyoutFooterTemplate>
        <ShellContent
            ContentTemplate="{DataTemplate pages:LoginPage}"
            Shell.FlyoutBehavior="Disabled"
            Route="LoginPage"
            Shell.FlyoutItemIsVisible="False" />
        <FlyoutItem
            FlyoutDisplayOptions="AsMultipleItems"
            Route="WelcomePage">
            <ShellContent
                Title="Welcome"
                ContentTemplate="{DataTemplate pages:WelcomePage}" />
            <ShellContent
                Title="Join meeting"
                ContentTemplate="{DataTemplate pages:JoinMeetingPage}" />
            <ShellContent
                Title="Manage Course"
                ContentTemplate="{DataTemplate pages:ManageCoursePage}" />
            <ShellContent
                Title="New Lecture"
                ContentTemplate="{DataTemplate pages:NewLecturePage}" />
            <ShellContent
                Title="New Test"
                ContentTemplate="{DataTemplate pages:NewTestPage}" />
            <ShellContent
                Title="Schedule lecture"
                ContentTemplate="{DataTemplate pages:ScheduleLecturePage}" />
            <ShellContent
                Title="Schedule test"
                ContentTemplate="{DataTemplate pages:ScheduleTestPage}" />
            <ShellContent
                Title="Assigned courses"
                ContentTemplate="{DataTemplate pages:NotificationsPage}" />
        </FlyoutItem>
    </Shell>
    
         builder.Logging.AddDebug();
    #endif
                builder.Services.AddSingleton<HttpClient>();
                builder.Services.AddSingleton(AudioManager.Current);
                builder.Services.AddSingleton<IAppService, AppService>();
                builder.Services.AddSingleton(typeof(IDataService<>), typeof(DataService<>));
                builder.Services.AddSingleton<IAuthenticationService, AuthenticationService>();
                builder.Services.AddSingleton<IHttpService, HttpService>();
                builder.Services.AddSingleton(Connectivity.Current);
                builder.Services.AddSingleton(firebaseAuthClient);
                builder.Services.AddSingleton<AppShell>();
                builder.Services.AddSingleton<NoInternetPage>();
                builder.Services.AddSingleton<StartupPage>();
                builder.Services.AddSingleton<LoginPage>();
                builder.Services.AddSingleton<WelcomePage>();
                builder.Services.AddSingleton<NewLecturePage>();
                builder.Services.AddSingleton<NewTestPage>();
                builder.Services.AddSingleton<ScheduleLecturePage>();
                builder.Services.AddSingleton<ScheduleTestPage>();
                builder.Services.AddSingleton<NotificationsPage>();
                builder.Services.AddSingleton<JoinMeetingPage>();
                builder.Services.AddSingleton<ManageCoursePage>();
                builder.Services.AddTransient<AppShellViewModel>();
                builder.Services.AddTransient<StartupPageViewModel>();
                builder.Services.AddTransient<LoginPageViewModel>();
                builder.Services.AddTransient<WelcomePageViewModel>();
                builder.Services.AddTransient<NewLecturePageViewModel>();
                builder.Services.AddTransient<NewTestPageViewMode>();
                builder.Services.AddTransient<ScheduleLecturePageViewModel>();
                builder.Services.AddTransient<ScheduleTestPageViewModel>();
                builder.Services.AddTransient<JoinMeetingPageViewModel>();
                builder.Services.AddTransient<ManageCoursePageViewModel>();
                builder.Services.AddTransient<NotificationsPageViewModel>();
    
    
    

    I found a way to share my project without any sensitive or personal data

    https://www.dropbox.com/scl/fi/g81zmnkfuee8q475wru6y/DemyAI-2.zip?rlkey=v1kto1ysnobojirb77lby97pq&dl=0

    Was this answer helpful?

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.