Flutter Navigator 2.0 通过 Navigator() 路由
Flutter Navigator 2.0 Routing via the Navigator()
我目前正在尝试使用新的 Flutter 网络测试版构建网络应用程序。
问题是能够拥有历史记录,处理浏览器中的前进和后退按钮,并能够处理用户输入 URL 新的 Navigator 2.0 API 是必需的(至少从什么开始我明白了)。
目前只有少数资源可用,我正在尝试基于这些资源构建我的 Navigator。
我使用的资源:
- (我的主要来源)'Learning Flutter’s new navigation and routing system' 作者:John Ryan
- A similar project(作者 orestesgaolin)但带有页面管理器且更复杂
- publicly shared document by flutter关于新导航
我设法让“后退”和“前进”按钮以及历史记录正常工作。但是,我正在努力处理页面切换(在 Navigator() 中)。在 John 的示例中,他管理 Navigator Widget(在 routeDelegater 中)的 'page: ' 数组中的不同站点。这对我来说似乎很奇怪,但我像这样尝试过但它并没有真正起作用(代码进一步向下)。
在 'page: []' 中,我首先尝试使用触发的布尔值(如 show404),但这不是很干净,所以我的下一次尝试是按如下方式获取当前页面(在数组中):
if(_currentPage.name == 'pages[0]') pageBuilds[0]
这有点奏效,因为我随后能够输入段 /matchit/0 并加载了正确的页面,但是由于某种原因,“/”路由不再起作用,我得到了错误
Navigator.onGenerateRoute was null, but the route named "/" was referenced
然后我尝试使用 'ongenerateRoute' 但这只是引发了一堆错误。我有点新,所以也许我在那里做错了什么。但在我看来这不是正确的方法。这就是我目前所处的位置。我不知道下一步该怎么做,希望你们中的一些人能帮助我。
我的 Route-Delegater 如下所示(我也会在代码中包含我的注释,也许它可以帮助一些同样希望了解 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 和 Delegater:
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);
}
}
提前致谢!
我实际上设法通过在 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()),
];
我目前正在尝试使用新的 Flutter 网络测试版构建网络应用程序。 问题是能够拥有历史记录,处理浏览器中的前进和后退按钮,并能够处理用户输入 URL 新的 Navigator 2.0 API 是必需的(至少从什么开始我明白了)。
目前只有少数资源可用,我正在尝试基于这些资源构建我的 Navigator。 我使用的资源:
- (我的主要来源)'Learning Flutter’s new navigation and routing system' 作者:John Ryan
- A similar project(作者 orestesgaolin)但带有页面管理器且更复杂
- publicly shared document by flutter关于新导航
我设法让“后退”和“前进”按钮以及历史记录正常工作。但是,我正在努力处理页面切换(在 Navigator() 中)。在 John 的示例中,他管理 Navigator Widget(在 routeDelegater 中)的 'page: ' 数组中的不同站点。这对我来说似乎很奇怪,但我像这样尝试过但它并没有真正起作用(代码进一步向下)。
在 'page: []' 中,我首先尝试使用触发的布尔值(如 show404),但这不是很干净,所以我的下一次尝试是按如下方式获取当前页面(在数组中):
if(_currentPage.name == 'pages[0]') pageBuilds[0]
这有点奏效,因为我随后能够输入段 /matchit/0 并加载了正确的页面,但是由于某种原因,“/”路由不再起作用,我得到了错误
Navigator.onGenerateRoute was null, but the route named "/" was referenced
然后我尝试使用 'ongenerateRoute' 但这只是引发了一堆错误。我有点新,所以也许我在那里做错了什么。但在我看来这不是正确的方法。这就是我目前所处的位置。我不知道下一步该怎么做,希望你们中的一些人能帮助我。
我的 Route-Delegater 如下所示(我也会在代码中包含我的注释,也许它可以帮助一些同样希望了解 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 和 Delegater:
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);
}
}
提前致谢!
我实际上设法通过在 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()),
];