筑巢路线扑动

我试图找出以下问题的良好体系结构解决方案:我有以下第一级路线,也可以称为布局:

/onboarding/* -> Shows onboarding layout
/dashboard/* -> Shows dashboard layout
/overlay/* -> shows slide up overlay layout
/modal/* -> shows modal layout

用户根据他/她的身份验证状态,操作等被路由到每个人。我正确地获得了这个阶段。

例如,当我想要使用可称为页面的辅助级别路由时,会出现问题

/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.

我怎样才能最好地定义/组织这些方式,以便我可以有效地路由到它们显示的布局和页面? 请注意,无论何时在一个布局中路由页面,布局都不会改变,但我想根据路线为其内部正在改变的内容(页面)制作动画。

迄今为止,我取得了如下成就

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后者来在路线之间动画转换? 现在,我需要弄清楚如何在登机和仪表板中使用嵌套的辅助路由器。

下面是我想要实现的视觉表示,我需要能够成功地路由蓝色和红色位。 在这个例子中,只要我们在/dashboard蓝色位(布局)不会改变,但是当我们从say /dashboard/home导航到/dashboard/stats ,红色位(页面)应该淡出并且淡入内容。 如果我们从/dashboard/home导航/dashboard/home说明/onboarding/home ,则红色位(布局)应该消失,并显示当前活动的页面并显示新的布局,故事会继续。

在这里输入图像描述

编辑我使用下面概述的方法取得了一些进展,基本上我将确定我的runApp中的布局,并将在每个布局中声明新的WidgetsApp和路由。 它似乎工作,但有一个问题,当我点击“注册”我被重定向到正确的页面,但我也可以看到它下面的旧页面。

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;
      });
}

你不能嵌套'路由器'( Navigator )。 它在设计上不是可嵌套的。 这是因为Hero和其他一些东西是基于它的。

另一方面,在路由“/ dashboard / profile”的情况下,您可以使用onGenerateRoute构建嵌套的“路由”,构建树WidgetApp > Dashboard > Profile 。 我认为这是你想要达到的目标。

结合高阶函数,您可以为您创建onGenerateRoute

为了提供代码流的线索: NestedRoute忽略了布局的确切构建,让它在builder方法中(例如builder: (child) => new Dashboard(child: child), )。 当调用buildRoute方法时,我们将为该页面的实例生成一个PageRouteBuilder ,但让_build管理Widgets的创建。 在_build我们要么按照_build使用builder - 要么通过调用请求的子路由来调用它自己的_build,让它膨胀子_build 。 完成后,我们将使用构建的子路由作为我们的构建者的参数。 长话短说,你递归地深入进一步的路径层次来构建路线的最后一层,然后让它从递归上升并使用结果作为外层的参数等等。

BuildNestedRoutes为你做脏活,并分析NestedRoutes列表来构建必要的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方法是这样定义的:

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组件将不会与您的路由系统紧密耦合; 但仍有嵌套的路线。 这是更可读,然后让你的路线全方位调度。 你会很容易地添加一个新的。


你试图建立的模式,即使是合理的,似乎也不能用Flutter来表示。

编辑 :你想要达到的行为需要使用onGenerateRoute,但尚未(1月18日)正确记录(文档)。 请参阅@Darky答案来举个例子。 他提出了NestedRouteBuilderNestedRoute实现,填补了空白。

使用MaterialApp中的普通导航器,路线和页面导航(根据doc)有两个主要特征,即拒绝您想要实现的目标(至少直接)。 一方面, Navigator行为如同一个堆栈,因此推动和弹出下一个等等的路线,其他路线是全屏或模态 - 这意味着它们部分地占据屏幕,但是它们阻止与下面的小部件。 更明确的说,你的范例似乎需要与堆栈中不同级别的页面同时进行交互 - 这是不能这样做的。

此外,它感觉像路径范式不仅是一个层次结构 - 一般框架→特定的子页面 - 但首先在导航器中表示堆栈 。 我自己被欺骗了,但是读起来很清楚:

String initialRoute

final

要显示的第一条路线的名称。

默认情况下,它按照dart:ui.Window.defaultRouteName。

如果这个字符串包含任何/字符,那么字符串会被分割到这些字符上,并且从字符串的开头到每个这样的字符的子字符串又被用作推送的路线。

例如,如果使用route / stocks / HOOLI作为初始路由,那么导航器将在启动时推送以下路由:/,/ stocks,/ stocks / HOOLI。 这可以实现深度链接,同时允许应用程序保持可预测的路由历史记录。

如下所示,一种可能的解决方法是利用路径名来实例化子窗口小部件,并保留一个状态变量以知道要显示的内容:

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new ActionPage(title: 'Flutter Demo Home Page'),
      routes: <String, WidgetBuilder>{
        '/action/plus': (BuildContext context) => new ActionPage(sub: 'plus'),
        '/action/minus': (BuildContext context) => new ActionPage(sub: 'minus'),
      },
    );
  }
}

class ActionPage extends StatefulWidget {
  ActionPage({Key key, this.title, this.sub = 'plus'}) : super(key: key);

  final String title, sub;

  int counter;

  final Map<String, dynamic> subroutes = {
    'plus': (BuildContext context, int count, dynamic setCount) =>
        new PlusSubPage(count, setCount),
    'minus': (BuildContext context, int count, dynamic setCount) =>
        new MinusSubPage(count, setCount),
  };

  @override
  ActionPageState createState() => new ActionPageState();
}

class ActionPageState extends State<ActionPage> {
  int _main_counter = 0;

  String subPageState;

  @override
  void initState() {
    super.initState();
    subPageState = widget.sub;
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text('Testing subpages'),
          actions: <Widget>[
            new FlatButton(
                child: new Text('+1'),
                onPressed: () {
                  if (subPageState != 'plus') {
                    setState(() => subPageState = 'plus');
                    setState(() => null);
                  }
                }),
            new FlatButton(
                child: new Text('-1'),
                onPressed: () {
                  if (subPageState != 'minus') {
                    setState(() => subPageState = 'minus');
                    setState(() => null);
                  }
                }),
          ],
        ),
        body: widget.subroutes[subPageState](context, _main_counter, (count) {
          _main_counter = count;
        }));
  }
}

class PlusSubPage extends StatefulWidget {
  PlusSubPage(this.counter, this.setCount);
  final setCount;
  final int counter;
  @override
  _PlusSubPageState createState() => new _PlusSubPageState();
}

class _PlusSubPageState extends State<PlusSubPage> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    _counter = widget.counter;
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
      widget.setCount(_counter);
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Center(
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new IconButton(
            icon: const Icon(Icons.add),
            onPressed: _incrementCounter,
          ),
          new Text(
            'You have pushed the button this many times:',
          ),
          new Text(
            '$_counter',
            style: Theme.of(context).textTheme.display1,
          ),
        ],
      ),
    );
  }
}

class MinusSubPage extends StatefulWidget {
  MinusSubPage(this.counter, this.setCount);
  final setCount;
  final int counter;
  @override
  _MinusSubPageState createState() => new _MinusSubPageState();
}

class _MinusSubPageState extends State<MinusSubPage> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    _counter = widget.counter;
  }

  void _decrementCounter() {
    setState(() {
      _counter--;
      widget.setCount(_counter);
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Center(
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new IconButton(
            icon: const Icon(Icons.remove),
            onPressed: _decrementCounter,
          ),
          new Text(
            'You have pushed the button this many times:',
          ),
          new Text(
            '$_counter',
            style: Theme.of(context).textTheme.display1,
          ),
        ],
      ),
    );
  }
}

但是,这在较低级别上没有堆栈内存 。 如果你要处理的子路径部件的序列-你可以包装subroute容器中WillPopScope ,定义有什么是应该当用户按下在做back按钮,并存储在堆栈中的子路径的序列。 但我不想提出这样的事情。

我的最终建议是实现普通路由 - 无“级别” - 管理自定义转换以隐藏“外部”布局的更改并通过页面传递数据或保留在适当的类中,为您提供应用程序状态。

PS:还可以查看英雄动画,他们可以为您提供您在视图之间寻找的连续性。

链接地址: http://www.djcxy.com/p/41065.html

上一篇: Nesting routes with flutter

下一篇: Navigation Bar is Black during Custom Unwind Segue