用颤振嵌套路由 [英] Nesting routes with flutter

查看:20
本文介绍了用颤振嵌套路由的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为以下问题找出良好的架构解决方案:我有以下 一级 路线,也可以称为 布局:

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 来动画路线之间的过渡?现在我需要弄清楚如何在登机和仪表板上安装类似 nested secondary router 的东西.

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.

编辑我用下面概述的方法取得了一些进展,基本上我将确定我的 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.

ma​​in.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 方法中(eg 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);
  };
}

这样,您的 FooBar 组件将不会与您的路由系统紧密耦合;但仍然有嵌套路线.它比将您的路线分发到各处更具可读性.而且您可以轻松添加一个新的.

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.

这篇关于用颤振嵌套路由的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆