问题描述
我正在尝试为以下问题找出好的架构解决方案:我有以下第一级路线,也可以称为布局:
I am trying to figure out good architectural solution for following problem: I have following First level routes that can also be referred to as layouts:
/onboarding/* -> Shows onboarding layout
/dashboard/* -> Shows dashboard layout
/overlay/* -> shows slide up overlay layout
/modal/* -> shows modal layout
用户根据他/她的身份验证状态、操作等被路由到每一个.我正确地得到了这个阶段.
User is routed to each of these depending on his/her auth state, actions etc.. I got this stage correctly.
当我想使用可以称为页面的二级路由时出现问题,例如
Issues arise when I want to use Secondary level routes that can be referred to as pages, for example
/onboarding/signin -> Shows onboarding layout, that displays signin route
/onboarding/plan -> Shows onboarding layout, that displays plan options
/modal/plan-info -> Shows modal layout, over previous page (/onboarding/plan) and displays plan-information page.
如何以一种可以有效路由到它们显示的布局和页面的方式来最好地定义/组织这些?请注意,每当我在一个布局内路由页面时,布局都不会改变,但我想根据路由对在其中更改的内容(页面)进行动画处理.
How can I best define / organise these in a way where I can efficiently route to layouts and pages they display? Note, that whenever I route pages inside one layout, layout is not changing, but I want to animate content (pages) that are changing inside of it based on route.
到目前为止,我实现了以下
Thus far I achieved following
import "package:flutter/widgets.dart";
import "package:skimitar/layouts/Onboarding.dart";
import "package:skimitar/layouts/Dashboard.dart";
Route generate(RouteSettings settings) {
Route page;
switch (settings.name) {
case "/onboarding":
page = new PageRouteBuilder(pageBuilder: (BuildContext context,
Animation<double> animation, Animation<double> secondaryAnimation) {
return new Onboarding();
});
break;
case "/dashboard":
page = new PageRouteBuilder(pageBuilder: (BuildContext context,
Animation<double> animation, Animation<double> secondaryAnimation) {
return new Dashboard();
});
break;
}
return page;
}
/* Main */
void main() {
runApp(new WidgetsApp(
onGenerateRoute: generate, color: const Color(0xFFFFFFFFF)));
}
这将路由到登机和仪表板布局(现在只是简单的容器包装文本).我也相信我可以使用 PageRouteBuilder
后者来为路由之间的过渡设置动画?现在我需要弄清楚如何在登机和仪表板内部添加嵌套辅助路由器之类的东西.
This routes to on boarding and dashboard layouts (right now just simple Containers wrapping text). I also believe that I can use PageRouteBuilder
latter on to animate transitions between routes? Now I need to figure out how to have something like nested secondary router inside on boarding and dashboard.
下面是我想要实现的目标的某种视觉表示,我需要能够成功地路由蓝色和红色位.在这个例子中,只要我们在 /dashboard
下,蓝色位(布局)就不会改变,但是当我们从 /dashboard/home
导航到 /仪表板/统计
红色位(页面)应该淡出并淡入新内容.如果我们从 /dashboard/home
导航到 /onboarding/home
,红色位(布局)应该随着它当前活动的页面消失并显示新的布局用于入职,故事还在继续.
Below is somewhat of a visual representation of what I want to achieve, I need to be able to successfully route blue and red bits. In this example as long as we are under /dashboard
blue bit (layout) doesn't change, but as we navigate from say /dashboard/home
to /dashboard/stats
the red bit (page) should fade out and fade in with new content. If we navigate away from /dashboard/home
to say /onboarding/home
, the red bit (layout) should fade away, along with its currently active page and show new layout for onboarding and the story continues.
EDIT 我使用下面概述的方法取得了一些进展,基本上我将确定我的 runApp
内的布局并声明新的 WidgetsApp
和每个布局内的路线.它似乎工作,但有一个问题,当我点击注册"时,我被重定向到正确的页面,但我也可以看到它下面的旧页面.
EDIT I made a bit of the progress with approach outlined below, essentially I will determine layout inside my runApp
and will declare new WidgetsApp
and routes inside each of the layouts. It seems to work, but there is an issue, When I click "SignUp" I am redirected to correct page, but I can also see old page below it.
main.dart
import "package:flutter/widgets.dart";
import "package:myProject/containers/layouts/Onboarding.dart";
/* Main */
void main() {
runApp(new Onboarding());
}
Onboarding.dart
import "package:flutter/widgets.dart";
import "package:myProject/containers/pages/SignIn.dart";
import "package:myProject/containers/pages/SignUp.dart";
import "package:myProject/services/helpers.dart";
/* Onboarding router */
Route onboardingRouter(RouteSettings settings) {
Route page;
switch (settings.name) {
case "/":
page = buildOnboardingRoute(new SignIn());
break;
case "/sign-up":
page = buildOnboardingRoute(new SignUp());
break;
default:
page = buildOnboardingRoute(new SignIn());
}
return page;
}
class Onboarding extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
color: const Color(0xFF000000),
image: new DecorationImage(
image: new AssetImage("assets/images/background-fire.jpg"),
fit: BoxFit.cover)),
child: new WidgetsApp(
onGenerateRoute: onboardingRouter, color: const Color(0xFF000000)),
);
}
}
SignUp.dart
import "package:flutter/widgets.dart";
class SignUp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Center(
child: new Text("Sign Up",
style: new TextStyle(color: const Color(0xFFFFFFFF))));
}
}
helpers.dart
import "package:flutter/widgets.dart";
Route buildOnboardingRoute(Widget page) {
return new PageRouteBuilder(
opaque: true,
pageBuilder: (BuildContext context, _, __) {
return page;
});
}
推荐答案
虽然技术上可以嵌套导航器",但这里不推荐这样做(因为它会破坏 Hero 动画)
While it's technically possible to nest "Navigator", this is unrecommended here (as it breaks Hero animation)
你可以使用onGenerateRoute
来构建嵌套的'routes',在路由'/dashboard/profile'的情况下,构建一个Tree WidgetApp >仪表板个人资料
.我认为这就是您想要实现的目标.
You can use onGenerateRoute
to build nested 'routes', in the case of a route '/dashboard/profile', build a Tree WidgetApp > Dashboard > Profile
. Which I assume is what you're trying to achieve.
结合高阶函数,你可以有一些东西为你创建onGenerateRoute
.
Combined with a higher order function, you can have something that creates onGenerateRoute
for you.
为了提供代码流的线索:NestedRoute
忽略了布局的确切构建,让它在 builder
方法中(例如em> builder: (child) => new Dashboard(child: child),
).当调用 buildRoute
方法时,我们将为这个页面的实例生成一个 PageRouteBuilder
,但让 _build
管理 的创建小部件
.在 _build
中,我们要么按原样使用 builder
- 要么让它膨胀子路由,通过调用请求的子路由,调用它自己的 _build
.完成后,我们将使用构建的子路由作为构建器的参数.长话短说,您递归地深入到进一步的路径级别以构建路线的最后一个级别,然后让它从递归中升起,并将结果用作外部级别的参数,依此类推.
To provide a clue of the code-flow: the NestedRoute
neglects the exact build of the layout, letting it in the builder
method (e.g. builder: (child) => new Dashboard(child: child),
). When calling the buildRoute
method we will generate a PageRouteBuilder
for the very instance of this page, but letting _build
manage the creation of the Widgets
. In _build
we either use the builder
as is - or let it inflate the subroute, by recalling the requested subroute, calling its own _build
. Once done, we'll be using the built subroute as the argument of our builder. Long story short, you recursively dive into further path levels to build the last level of the route, then let it rise from recursion and use the result as an argument for the outer level and so on.
BuildNestedRoutes
为您完成繁琐的工作并解析 NestedRoutes
的列表以构建必要的 RouteSettings
.
BuildNestedRoutes
does the dirty job for you and parses the lists of NestedRoutes
to build the necessary RouteSettings
.
所以,从下面的例子
示例:
@override
Widget build(BuildContext context) {
return new MaterialApp(
initialRoute: '/foo/bar',
home: const FooBar(),
onGenerateRoute: buildNestedRoutes(
[
new NestedRoute(
name: 'foo',
builder: (child) => new Center(child: child),
subRoutes: [
new NestedRoute(
name: 'bar',
builder: (_) => const Text('bar'),
),
new NestedRoute(
name: 'baz',
builder: (_) => const Text('baz'),
)
],
),
],
),
);
}
在这里您只是定义了嵌套路由(名称 + 相关组件).而 NestedRoute
类 + buildNestedRoutes
方法是这样定义的:
Here you simply defined your nested routes (name + associated component).And NestedRoute
class + buildNestedRoutes
method are defined this way :
typedef Widget NestedRouteBuilder(Widget child);
@immutable
class NestedRoute {
final String name;
final List<NestedRoute> subRoutes;
final NestedRouteBuilder builder;
const NestedRoute({@required this.name, this.subRoutes, @required this.builder});
Route buildRoute(List<String> paths, int index) {
return new PageRouteBuilder<dynamic>(
pageBuilder: (_, __, ___) => _build(paths, index),
);
}
Widget _build(List<String> paths, int index) {
if (index > paths.length) {
return builder(null);
}
final route = subRoutes?.firstWhere((route) => route.name == paths[index], orElse: () => null);
return builder(route?._build(paths, index + 1));
}
}
RouteFactory buildNestedRoutes(List<NestedRoute> routes) {
return (RouteSettings settings) {
final paths = settings.name.split('/');
if (paths.length <= 1) {
return null;
}
final rootRoute = routes.firstWhere((route) => route.name == paths[1]);
return rootRoute.buildRoute(paths, 2);
};
}
这样,您的 Foo
和 Bar
组件将不会与您的路由系统紧密耦合;但仍然有嵌套路由.它比将您的路线分发到所有地方更具可读性.您可以轻松添加一个新的.
This way, your Foo
and Bar
components will not be tightly coupled with your routing system ; but still have nested routes.It's more readable then having your routes dispatched all over the place. And you'll easily add a new one.
这篇关于使用颤振嵌套路由的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!