【问题标题】:Flutter Navigator 2.0 Routing via the Navigator()Flutter Navigator 2.0 通过 Navigator() 路由
【发布时间】:2021-03-16 10:44:54
【问题描述】:

我目前正在尝试使用新的 Flutter web beta 构建一个 web 应用程序。 问题是能够拥有历史记录,处理浏览器中的前进和后退按钮,并能够处理用户输入到需要新 Navigator 2.0 API 的 URL(至少根据我的理解)。

目前只有少数可用资源,我正在尝试基于这些资源构建我的导航器。 我使用的资源:

我设法让“后退”和“前进”按钮以及历史记录正常工作。但是,我正在努力处理页面切换(在 Navigator() 中)。在 John 的示例中,他管理 Navigator Widget 的“page:”数组中的不同站点(在 routeDeleger 中)。这对我来说似乎很奇怪,但我这样尝试过,但它并没有真正起作用(代码进一步向下)

在“页面:[]”中,我首先尝试使用触发布尔值(如 show404),但这不是很干净,所以我的下一个尝试是按如下方式获取当前页面(在数组中):

if(_currentPage.name == 'pages[0]') pageBuilds[0]

这有点工作,因为然后我可以输入段 /matchit/0 并加载正确的页面,但是由于某种原因,“/”路由不再起作用,我收到了错误

Navigator.onGenerateRoute was null, but the route named "/" was referenced 

然后我尝试使用“ongenerateRoute”,但这只会引发一堆错误。我有点新,所以也许我在那里做错了什么。但在我看来,这不是正确的方法。这就是我目前所困的地方。我不知道下一步该尝试什么,希望你们中的一些人可以帮助我。

我的 Route-Deleger 如下所示(我也会在代码中包含我的 cmets,也许它对你们中的一些人有所帮助,他们也希望了解 Navigator 2.0,了解发生了什么):

/**
 * The RouteDelegate defines application specific behavious of how the router
 * learns about changes in the application state and how it responds to them. 
 * It listens to the RouteInformation Parser and the app state and builds the Navigator with
 * the current list of pages (immutable object used to set navigator's history stack).
 */

//ChangeNotifier for the listeners as they are handled there
//The popRoute is handled by 'PopNavigatorRouterDelegateMixin'
class RoutesDelegater extends RouterDelegate<RoutePath>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<RoutePath> {
  //This is the state of the navigator widget (in build method)
  GlobalKey<NavigatorState> get navigatorKey => GlobalKey<NavigatorState>();
  //RoutesDelegater()  : navigatorKey = GlobalKey<NavigatorState>();
  MyPage _currentPage;
  bool show404 = false; //checks if we show the 404 page

  List<MyPage> pages = [
    MyPage('ProjektListe'),
    MyPage('StudiListe'),
    MyPage('PRView'),
  ];

  List<Page> pageBuilds = [
    MaterialPage(key: ValueKey('Unknown'), child: UnknownScreen()),
    MaterialPage(key: ValueKey('Homepage'), child: MyFirstHomepage()),
    MaterialPage(key: ValueKey('OtherScreen'), child: OtherScreen()),
  ];

  //currentConfiguration detects changes in the route information
  //It helps complete the browser history and (IMPORTANT) makes the browser back and forward buttons work

  RoutePath get currentConfiguration {
    if (show404) {
      return RoutePath.unknown();
    }
    if (_currentPage == null) return RoutePath.home();
    //if not 404 or homepage it is some other page
    return RoutePath.details(pages.indexOf(_currentPage));
  }

  @override
  Widget build(BuildContext context) {
    return Navigator(
        key: navigatorKey,
        pages: //List.of(pageBuilds),
            [
          //pageBuilds[1],
          if (show404)
            pageBuilds[0]
          else if (_currentPage != null)
            pageBuilds[1]
        ],
        onPopPage: (route, result) {
          if (!route.didPop(result)) {
            return false;
          }
          _currentPage = null;
          show404 = false;
          //we are using Changenotifier
          notifyListeners();
          return true;
        });
  }

  void _handleTapped(MyPage page) {
    _currentPage = page;
    notifyListeners();
  }

  @override
  Future<void> setNewRoutePath(RoutePath path) async {
    //Handle the unknown path
    if (path.isUnknown) {
      show404 = true;
      _currentPage = null;
      return;
    }

    if (path.isDetailPage) {
      //Check if Path id is valid
      if (path.id.isNegative || path.id > pages.length - 1) {
        show404 = true;
        return;
      }
      _currentPage = pages[path.id];
    } else {
      //homepage will be shown
      _currentPage = null;
    }

    show404 = false;
  }
}

我的 RoutingInformationParser 如下所示:

/*
* The RouteInformationParser takes the RouteInformation from RouteInformationProvider and
* parses it into a user-defined data type.
*/

class MyRoutesInformationParser extends RouteInformationParser<RoutePath> {
  @override
  Future<RoutePath> parseRouteInformation(
      RouteInformation routeInformation) async {
    //routeInformation is an object we get from the uri
    final uri = Uri.parse(routeInformation.location);
    // Handle '/' (Home Path)
    //Path segments are the segments seperated by /, if we don't have any we are on Home
    if (uri.pathSegments.length == 0) {
      return RoutePath.home();
    }

    //We have 2, as we have matchit/...
    if (uri.pathSegments.length == 2) {
      //If there is no 'matchit' in the first path segment the path is unknown
      if (uri.pathSegments.first != 'matchit') return RoutePath.unknown();
      //If we now have the correct first segment we can now handle the rest of the segment
      final remaining = uri.pathSegments.elementAt(1);
      final id = int.tryParse(remaining);
      //if it fails we return the unknown path
      if (id == null) return RoutePath.unknown();
      return RoutePath.details(id);
    }

    //Handling the unknown Path, e.g. user just typed anything in uri
    return RoutePath.unknown();
  }

  //THIS IS IMPORTANT: Here we restore the web history
  @override
  RouteInformation restoreRouteInformation(RoutePath path) {
    //Here we set the routeInformation which is used above, e.g. /404 for unknown page
    if (path.isUnknown) {
      return RouteInformation(location: '/404');
    }
    if (path.isHomePage) {
      return RouteInformation(location: '/');
    }
    //Any other page is handled here via /matchit/... <= the id of the path
    if (path.isDetailPage) {
      return RouteInformation(location: '/matchit/${path.id}');
    }
    //If none of the paths are hit
    return null;
  }
}

然后我们还有路由信息的数据类型:

class RoutePath{
  final int id;
  final bool isUnknown;

  RoutePath.home()
      : id = null,
        isUnknown = false;

  //Details means here that it is any other side than Home or unknown
  RoutePath.details(this.id) : isUnknown = false;

  RoutePath.unknown()
      : id = null,
        isUnknown = true;

  //check if is on HomePage or other page, then either == null or != null
  //not needed for isInknown, as when unknown then = true as set above
  bool get isHomePage => id == null;
  bool get isDetailPage => id != null;
}

最后是我的 Homepage(),InformationParser 和 Deleger 被初始化:

class Homepage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _HomepageState();
}

class _HomepageState extends State<Homepage> {
  //initialize the RouteDelegater and Information Parser to be unsed further down

  RoutesDelegater _routesDelegater = RoutesDelegater();
  MyRoutesInformationParser _myRoutesInformationParser =
      MyRoutesInformationParser();

/*
 * Relevant routing information for this build method:
 * We need to use the MaterialApp.router else we can't use routerDelegate and routeInformationParser.
 * Then we define the delegate and Information Parser (they are initiated above)
 */

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
        title: 'MatchIT',
        routerDelegate: _routesDelegater,
        routeInformationParser: _myRoutesInformationParser,
        theme: ThemeData(primarySwatch: Colors.blue),
        debugShowCheckedModeBanner: false);
  }
}

提前谢谢你!

【问题讨论】:

    标签: flutter flutter-web navigator flutter-navigation flutter-routes


    【解决方案1】:

    我实际上设法通过在 pages[] 下的 RouteDelegator 中添加另一个在我的 Navigator() 中调用的函数来解决它:

     @override
      Widget build(BuildContext context) {
        return Navigator(
            key: navigatorKey,
            pages: [
              pageBuilds[1],
              if (show404)
                pageBuilds[0]
              else if (_currentPage != null)
                _getMyPage(_currentPage)
            ],
            onPopPage: (route, result) {
              if (!route.didPop(result)) {
                return false;
              }
              _currentPage = null;
              show404 = false;
              //we are using Changenotifier
              notifyListeners();
              return true;
            });
      }
    
      /*
       * This is where every other site than homepage and 404 is handled
       * to add another site to this application add the name to [List<MyPage> pages]
       * and then add the MaterialPage here
       */
    
      MaterialPage _getMyPage(MyPage currentPage) {
        if (currentPage.name == 'Login')
          return MaterialPage(key: ValueKey('LoginScreen'), child: OtherScreen());
        else
          return MaterialPage(
              key: ValueKey('ProfileScreen'), child: ResultScreen());
      }
    

    我还更改了上面列表中的名称(只是为了让你们理解代码):

    List<MyPage> pages = [
        MyPage('Login'),
        MyPage('Profile'),
      ];
    
      List<Page> pageBuilds = [
        MaterialPage(key: ValueKey('Unknown'), child: UnknownScreen()),
        MaterialPage(key: ValueKey('Homepage'), child: MyFirstHomepage()),
        MaterialPage(key: ValueKey('LoginScreen'), child: OtherScreen()),
        MaterialPage(key: ValueKey('ProfileScreen'), child: ResultScreen()),
      ];
    

    【讨论】:

      猜你喜欢
      • 2021-05-28
      • 2021-06-22
      • 2021-08-22
      • 2019-01-25
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 2021-04-09
      • 1970-01-01
      相关资源
      最近更新 更多