You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAUI Shell导航:如何统一回退到指定Products页面?

问题:MAUI Shell 统一导航回指定页面的原生实现需求

我需要实现用户完成产品移动操作后,统一返回至Products页面(用户大概率会继续从同一位置移动产品)。目前存在两种导航场景:

场景1

当前路由:

//SourceAisle/SourceLocation/Products/TargetAisle/TargetLocation

场景2

当前路由:

//SourceAisle/SourceLocation/Products/FavoriteLocation

两种场景下,操作完成后都需要返回至:

//SourceAisle/SourceLocation/Products

目前可以在场景1用Shell.Current.GoToAsync("../.."),场景2用Shell.Current.GoToAsync(".."),但希望用统一逻辑直接回退到Products页面,不想推送新页面,只需要弹出所有页面直到目标页面显示。

官方文档提到可以用/route或///route进行反向搜索,但不确定具体用法。之前在旧Xamarin项目中写过扩展方法实现类似功能,但更倾向于用Shell原生方案。


备注:旧Xamarin扩展方法实现

// await Shell.Current.Navigation.PopToAsync<ProductsPage>();

internal static class NavigationExtensions
{
    private static readonly SemaphoreSlim _semaphore = new (1, 1);

    public static async Task PopToAsync<T>(this INavigation nav, Page nextPage = null, bool animated = true) where T : Page
    {
        await _semaphore.WaitAsync();

        try
        {
            bool exists = nav.NavigationStack
                .Where((p, i) => i <= nav.NavigationStack.Count - 2)
                .Any(p => p is T);

            if (!exists)
                throw new ArgumentException($"The specified page {typeof(T).Name} does not exist in the stack");

            for (var index = nav.NavigationStack.Count - 2; index > 0; index--)
            {
                if (nav.NavigationStack[index] is T)
                    break;

                nav.RemovePage(nav.NavigationStack[index]);
            }

            if (nextPage is not null)
                nav.InsertPageBefore(nextPage, nav.NavigationStack[nav.NavigationStack.Count - 1]);

            await nav.PopAsync(animated);
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

补充信息:AppShell配置

AppShell.xaml

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="MyApp.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:views="clr-namespace:MyApp.Views">

    <FlyoutItem Route="SourceAisle" Title="Stock Movement">
        <ShellContent ContentTemplate="{DataTemplate views:AislesPage}" />
    </FlyoutItem>
</Shell>

AppShell.xaml.cs

public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();

        Routing.RegisterRoute("SourceAisle", typeof(AislesPage));
        Routing.RegisterRoute("SourceLocation", typeof(LocationsPage));
        Routing.RegisterRoute("Products", typeof(ProductsPage));
        Routing.RegisterRoute("TargetAisle", typeof(AislesPage));
        Routing.RegisterRoute("TargetLocation", typeof(LocationsPage));
        Routing.RegisterRoute("FavoriteLocation", typeof(FavoritesPage));
    }
}

注:aisle和location页面同时用于源位置和目标位置,通过状态区分场景,此部分不在本次问题范围内。当前导航调用:从LocationsViewModel中调用await Shell.Current.GoToAsync("Products"),从ProductsViewModel中调用await Shell.Current.GoToAsync("TargetAisle")等,认为是相对路由,但不确定是否需要调整。


解决方案

可以通过MAUI Shell的原生路由语法实现统一回退需求,推荐以下两种方案:

方案1:使用///Products反向匹配路由

Shell的///route语法会从当前导航栈的最顶层开始,反向查找第一个匹配指定路由名的页面,找到后自动弹出该页面之上的所有页面,直接回到目标页面,完全符合你“不推送新页面、仅回退到已存在页面”的需求。

两种场景下统一调用即可:

await Shell.Current.GoToAsync("///Products");

此方案灵活性强,即便后续路由层级调整,只要Products页面的路由名不变,就能正常匹配。

方案2:使用绝对路由指定完整路径

如果Products页面的父路由结构固定(SourceAisle/SourceLocation/Products),也可以直接使用绝对路由跳转:

await Shell.Current.GoToAsync("//SourceAisle/SourceLocation/Products");

但这种方案依赖固定的路由层级,后续结构调整时需要同步修改代码,适配性不如方案1。

额外说明

你当前使用的相对路由调用(如GoToAsync("Products"))是正确的,无需调整,不会影响反向匹配的逻辑。只要Products页面的路由注册(Routing.RegisterRoute("Products", typeof(ProductsPage)))保持正确,///Products就能准确匹配到栈中的目标页面。


内容的提问来源于stack exchange,提问作者Arthur Rey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:40:34