【问题标题】:Navigator 2.0 back button loop flutter webNavigator 2.0后退按钮循环flutter web
【发布时间】:2022-07-30 16:34:05
【问题描述】:

我刚刚实现了 Flutter Navigator 2.0,到目前为止看起来还不错。我发现了一个问题,即如果您在 404 页面上并按下后退按钮,则 URL 会更改为导致错误的 URL,并再次显示 404。基本上,我有一个 404 循环。

这是我的参考代码:

///页面路由文件

class PageRouterDelegate extends RouterDelegate<PageRoutePath>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<PageRoutePath> {
  bool isServices = false;
  bool isLanding = true;
  bool isBrochure = false;
  bool isContact = false;
  bool isCareers = false;
  bool isProductsPage = false;
  bool isProductPage = false;
  int? productId;
  bool isGallery = false;
  bool isUnknown = false;
  bool show404 = false;
  bool isPrivacyPolicy = false;

  @override
  GlobalKey<NavigatorState>? get navigatorKey => GlobalKey<NavigatorState>();

  @override
  PageRoutePath get currentConfiguration {
    if (isServices) return PageRoutePath.services();
    if (isBrochure) return PageRoutePath.brochure();
    if (isContact) return PageRoutePath.contact();
    if (isCareers) return PageRoutePath.careers();
    if (isProductsPage) return PageRoutePath.productsPage();
    if (isProductPage && productId != null) {
      return PageRoutePath.productPage(productId!);
    }
    if (isGallery) return PageRoutePath.gallery();
    if (isPrivacyPolicy) return PageRoutePath.privacyPolicy();
    if (show404) return PageRoutePath.unknown();
    return PageRoutePath.landing();
  }

  void tap(int id) {
    isProductPage = true;
    productId = id;
    notifyListeners();
  }

  @override
  Widget build(BuildContext context) {
    return Navigator(
      key: navigatorKey,
      pages: [
        MaterialPage(
            key: const ValueKey('home-page'),
            child: ResponsiveLanding(
              onTap: tap,
            )),
        if (show404)
          const MaterialPage(
              key: ValueKey('404'), child: UnknownPage()),
        if (isServices)
          const MaterialPage(
              key: ValueKey('service'), child: ResponsiveService()),
        if (isBrochure)
          const MaterialPage(
              key: ValueKey('brochure'), child: ResponsiveBrochure()),
        if (isContact)
          MaterialPage(
              key: const ValueKey('contact-us'),
              child: ResponsiveContactUs(
                countryController: TextEditingController(),
                companyNameController: TextEditingController(),
                commentsController: TextEditingController(),
                addressController: TextEditingController(),
                emailController: TextEditingController(),
            nameController: TextEditingController(),
            phoneController: TextEditingController(),
          )),
        if (isCareers)
          MaterialPage(
              key: const ValueKey('careers'),
              child: ResponsiveCareers(
            phoneController: TextEditingController(),
            nameController: TextEditingController(),
            emailController: TextEditingController(),
          )),
        if (isProductsPage) const MaterialPage(key: ValueKey('products-page'),child: ResponsiveProducts()),
        if (isProductPage && productId != null)
          MaterialPage(
              key: const ValueKey('product-page'),
              child: ResponsiveProduct(
            data: product_data[productId!],
          )),
        if (isGallery) const MaterialPage(key: ValueKey('gallery'),child: ResponsiveGallery()),
        if (isPrivacyPolicy) const MaterialPage(key: ValueKey('privacy-policy'),child: PrivacyPolicy()),
      ],
      onPopPage: (route, result)
      {
        if (!route.didPop(result)) {
          isUnknown = false;
          show404 = false;
          notifyListeners();
          return false;
        }
        final checkRoute = route.settings as MaterialPage;
        if(checkRoute.key == const ValueKey('404')){

          productId = null;
          show404 = false;
          notifyListeners();

          return true;
        }
        print('not-checked');
        return true;
      },
    );
  }



  @override
  // ignore: avoid_renaming_method_parameters
  Future<void> setNewRoutePath(PageRoutePath path) async {
    if (path.isUnknown) {
      productId = null;
      show404 = true;
      return null;
    }

    if (path.isServices) {
      isServices = true;
    }

    if (path.isContact) {
      isContact = true;
    }

    if (path.isCareers) {
      isCareers = true;
    }

    if (path.isProductsPage) {
      isProductsPage = true;
    }

    if (path.isProductPage && path.productId != null) {
      if (path.productId! < 0 || path.productId! > product_data.length - 1) {
        productId = null;
        show404 = true;
        return null;
      }
      isProductPage = true;
      productId = path.productId;
    }

    if (path.isGallery) {
      isGallery = true;
    }

    if (path.isPrivacyPolicy) {
      isPrivacyPolicy = true;
    }

    isUnknown = false;
    show404 = false;
  }
}

class PageRouteInformationParser extends RouteInformationParser<PageRoutePath> {
  @override
  Future<PageRoutePath> parseRouteInformation(
      RouteInformation routeInformation) async {
    final uri = Uri.parse(routeInformation.location!);

    ///handle '/' landing
    if (uri.pathSegments.isEmpty) {
      return PageRoutePath.landing();
    }

    /// handle /services
    if (uri.pathSegments.first == 'services' && uri.pathSegments.length == 1) {
      return PageRoutePath.services();
    }

    /// handle /brochure
    if (uri.pathSegments.first == 'brochure' && uri.pathSegments.length == 1) {
      return PageRoutePath.brochure();
    }

    /// handle /contact-us
    if (uri.pathSegments.first == 'contact-us' &&
        uri.pathSegments.length == 1) {
      return PageRoutePath.contact();
    }

    /// handle /careers
    if (uri.pathSegments.first == 'careers' && uri.pathSegments.length == 1) {
      return PageRoutePath.careers();
    }

    /// handle /products
    if (uri.pathSegments.first == 'products' && uri.pathSegments.length == 1) {
      return PageRoutePath.productsPage();
    }

    /// handle /product/int
    if (uri.pathSegments.first == 'product' &&
        int.tryParse(uri.pathSegments.elementAt(1)) != null &&
        uri.pathSegments.length == 2) {
      return PageRoutePath.productPage(
          int.tryParse(uri.pathSegments.elementAt(1))!);
    }

    /// handle /gallery
    if (uri.pathSegments.first == 'gallery' && uri.pathSegments.length == 1) {
      return PageRoutePath.gallery();
    }

    /// handle /privacy-policy
    if (uri.pathSegments.first == 'privacy-policy' &&
        uri.pathSegments.length == 1) {
      return PageRoutePath.privacyPolicy();
    }

    return PageRoutePath.unknown();
  }

  @override
  RouteInformation restoreRouteInformation(PageRoutePath configuration) {
    if (configuration.isUnknown) {
      return const RouteInformation(location: '/404');
    }
    else if (configuration.isServices) {
      return const RouteInformation(location: '/services');
    }
    else if (configuration.isBrochure) {
      return const RouteInformation(location: '/brochure');
    }
    else if (configuration.isContact) {
      return const RouteInformation(location: '/contact-us');
    }
    else if (configuration.isCareers) {
      return const RouteInformation(location: '/careers');
    }
    else if (configuration.isProductPage) {
      return RouteInformation(location: '/product/${configuration.productId}');
    }
    else if (configuration.isProductsPage) {
      return const RouteInformation(location: '/products');
    }
    else if (configuration.isGallery) {
      return const RouteInformation(location: '/gallery');
    }
    else if (configuration.isPrivacyPolicy) {
      return const RouteInformation(location: '/privacy-policy');
    }
    return const RouteInformation(location: '/home');
  }
}

///routePath 类

class PageRoutePath {
  final int? productId;
  final bool isLanding;
  final bool isServices;
  final bool isBrochure;
  final bool isContact;
  final bool isCareers;
  final bool isProductPage;
  final bool isProductsPage;
  final bool isGallery;
  final bool isUnknown;
  final bool isPrivacyPolicy;

  PageRoutePath.landing()
      : isLanding = true,
        isServices = false,
        isBrochure = false,
        isContact = false,
        isCareers = false,
        isProductPage = false,
        isProductsPage = false,
        isGallery = false,
        isUnknown = false,
        isPrivacyPolicy = false,
        productId = null;

  PageRoutePath.services()
      : isLanding = false,
        isServices = true,
        isBrochure = false,
        isContact = false,
        isCareers = false,
        isProductPage = false,
        isProductsPage = false,
        isGallery = false,
        isUnknown = false,
        isPrivacyPolicy = false,
        productId = null;

  PageRoutePath.brochure()
      : isLanding = false,
        isServices = false,
        isBrochure = true,
        isContact = false,
        isCareers = false,
        isProductPage = false,
        isProductsPage = false,
        isGallery = false,
        isUnknown = false,
        isPrivacyPolicy = false,
        productId = null;

  PageRoutePath.contact()
      : isLanding = false,
        isServices = false,
        isBrochure = false,
        isContact = true,
        isCareers = false,
        isProductPage = false,
        isProductsPage = false,
        isGallery = false,
        isUnknown = false,
        isPrivacyPolicy = false,
        productId = null;

  PageRoutePath.careers()
      : isLanding = false,
        isServices = false,
        isBrochure = false,
        isContact = false,
        isCareers = true,
        isProductPage = false,
        isProductsPage = false,
        isGallery = false,
        isUnknown = false,
        isPrivacyPolicy = false,
        productId = null;

  PageRoutePath.productPage(int id)
        : isLanding = false,
        isServices = false,
        isBrochure = false,
        isContact = false,
        isCareers = false,
        isProductPage = true,
        isProductsPage = false,
        isGallery = false,
        isUnknown = false,
        isPrivacyPolicy = false,
        productId = id;

  PageRoutePath.productsPage()
      : isLanding = false,
        isServices = false,
        isBrochure = false,
        isContact = false,
        isCareers = false,
        isProductPage = false,
        isProductsPage = true,
        isGallery = false,
        isUnknown = false,
        isPrivacyPolicy = false,
        productId = null;

  PageRoutePath.gallery()
      : isLanding = false,
        isServices = false,
        isBrochure = false,
        isContact = false,
        isCareers = false,
        isProductPage = false,
        isProductsPage = false,
        isGallery = true,
        isUnknown = false,
        isPrivacyPolicy = false,
        productId = null;

  PageRoutePath.unknown()
      : isLanding = false,
        isServices = false,
        isBrochure = false,
        isContact = false,
        isCareers = false,
        isProductPage = false,
        isProductsPage = false,
        isGallery = false,
        isUnknown = true,
        isPrivacyPolicy = false,
        productId = null;

  PageRoutePath.privacyPolicy()
      : isLanding = false,
        isServices = false,
        isBrochure = false,
        isContact = false,
        isCareers = false,
        isProductPage = false,
        isProductsPage = false,
        isGallery = false,
        isUnknown = false,
        isPrivacyPolicy = true,
        productId = null;
}

按下后退按钮会返回导致错误的网址并返回 404 页面。

【问题讨论】:

  • 我将问题减少到最小但完整的demo

标签: flutter flutter-web


【解决方案1】:

在 404 中添加一个 willPopScope 并监听返回按钮按下然后重定向到主页。

return WillPopScope(
  onWillPop: _backPressed,
  child: Scaffold(
    body: Scaffold(
      body: Container(), //404 page UI here
    ),
  ),
);


Future<bool> _backPressed() async {
    Navigator.pushNamedAndRemoveUntil(context, "/home", (route) => false);
    return false;
}

【讨论】:

  • 我会试试这个并告诉你。
  • 这里有同样的问题。但不是,应用栏 后退按钮 不起作用,您可以使用 WillPopScope 处理。它是浏览器处理历史堆栈的后退按钮,这很棘手:无效地址被重定向到 404 页面。但无效地址仍在历史堆栈中。如果你按返回,你会再次回到无效地址,然后重定向到用户被困的404页面。
  • 因此,当您导航到未找到的页面时,您必须通过推送替换或推送和删除进行导航,直到
  • RouteInformationParser 正在通过parseRouteInformation(决定uri 是否正确)和restoreRouteInformation(决定找不到页面 uri)。我看不到应该在哪里放置pushReplacementpushAndRemoveUntil。按照 Flutter 团队推荐的 Navigation 2.0 article,您将面临与 404 相同的问题,该问题在此处被忽略。
【解决方案2】:

到目前为止,在我看来,防止 404 循环的唯一方法是:

替换成restoreRouteInformation

if (configuration.isUnknown) {
  return const RouteInformation(location: '/404');
}

if (configuration.isUnknown) {
  return null;
}

但地址栏不再显示 404。

【讨论】:

    猜你喜欢
    • 2012-02-23
    • 2021-03-16
    • 1970-01-01
    • 2021-05-10
    • 2021-06-03
    • 2015-07-19
    • 1970-01-01
    • 2019-07-17
    • 2018-02-08
    相关资源
    最近更新 更多