一、基本路由使用

路由是页面跳转的根本,也可以称为导航跳转,Flutter 提供的是 Navigator 这个 Widget,能够使我们在不同的页面之间进行切换。

Flutter 中一切都是 Widget,因此本质上不存在页面,所谓的页面也不过是在某个 Widget 封装而已,比如常见的,我们会使用 Scalfold 包装出一个页面。

如果写过 weex 或者是 react native ,对于 navigator.pushnavigator.pop 应该都比较熟悉

native 开发页面都是堆栈形式的压入和弹出,因此 push 和 pop 最形象

1、Navigator.push

navigator.push 的定义如下:

  @optionalTypeArgs
  static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
    return Navigator.of(context).push(route);
  }

可以看出,方法接收两个参数,一个是上下文,另外一个是 Route Widget,但是 Route 是一个抽象类,因此在实际使用中,我们不会直接传入Route,而是会使用 MaterialRoute

MaterialRoute 继承自 PageRoute,这个继承关系非常复杂:

  • MaterialRoute
  • PageRoute
  • ModalRoute
  • TransitionRoute with LocalHistoryRoute
  • OverlayRoute
  • Route

因此如果要自己实现一个用于 Navigator.push 的 Route 就比较麻烦了

MaterialRoute 的构造方式非常简单,就是一个 builder,然后返回一个页面即可:

其中 fullscreenDialog 是一种比较特殊的页面打开方式,本质上会从底部弹起类似一个 Dialog 对话框,但是是全屏的,具体效果参照手机淘宝 ios 客户端登录

  MaterialPageRoute({
    @required this.builder,
    RouteSettings settings,
    this.maintainState = true,
    bool fullscreenDialog = false,
  })

简单的使用 Navigator.push 跳转页面则是非常简单的,比如做了一个按钮,点击跳转:

FlatButton(
  child: Row(
    children: <Widget>[Icon(Icons.search), Text('搜索')],
  ),
  onPressed: () {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => SearchPage()),
    );
  },
  color: Theme.of(context).buttonColor,
),

跳转的页面 SearchPage 的实现就不重复了,完整代码在下面能够看到

效果:

2.gif

2、 Navigator.pop

pop 的定义就比较简单了:

  @optionalTypeArgs
  static bool pop<T extends Object>(BuildContext context, [ T result ]) {
    return Navigator.of(context).pop<T>(result);
  }

使用上一般也非常简单:

onPressed: () {
   Navigator.pop(context);
},

二、构造函数传值 - 基础路由传值

上面代码中实现了页面的跳转,但实际上没有办法传递参数给下一跳页面。

先看下上面的用法:

    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => SearchPage()),
    );

最终我们是返回了一个 MaterialPageRoute(builder: (context) => SearchPage()),

如果 SearchPage 页面需要参数,比如需要一个 id 呢?

首先创造一个页面,为了区分开,改了个名字:

// 登录
class LoginPage extends StatelessWidget {
  final String title;
  final String id;

  const LoginPage({Key key, this.title = '表单', this.id}) : super(key: key);

  Text _getTitle() {
    return Text(id != null ? '${this.title} - ID:${this.id}' : this.title);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: this._getTitle(),
      ),
      body: Center(
        child: _getTitle()
      )
    );
  }
}

上面这个 Widget 构造函数中需要一个 title 和 id 两个参数,而这个是需要上个页面中传过来的

所以上一跳页面跳转的时候,可以通过 Widget builder 的进行参数透传:

onPressed: () {
Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) => LoginPage(
      title: 'NewName',
      id: DateTime.now().toString(),
    ),
  ),
);

效果如下:

3.gif

从上面的结果中可以发现,最终可以每次跳转将数据带过去。

三、ModalRoute 传值 - 基础路由传值

上面传值是通过 Widget 的构造函数传递的,这种方式就必须依赖 Widget 写死构造参数

除此之外,还有另外一种方式,从 MaterialPageRoute 的构造参数中 可以看到 RouteSettings: settings 这个参数

RouteSettings 构造函数如下:

  const RouteSettings({
    this.name,
    this.isInitialRoute = false,
    this.arguments,
  });

其实 RouteSettings 就是路由的基本信息,arguments 可以用来存储路由相关的参数字段:

下面是一个基本的路由跳转,页面 Widget 构造函数不接受参合参数,但是路由的 settings 中配置了一个对象

Navigator.of(context).push(
  new MaterialPageRoute(
    builder: (context) {
      return NewRouteWidget();
    },
    settings: RouteSettings(
      arguments: {'name': 'postbird'},
    ), // 传参
    fullscreenDialog: true,
  ),
);

NewRouteWidget Widget 中想要拿到配置的 arguments 可以通过 ModalRoute 来拿

ModalRoute 构造如下:

  /// Creates a route that blocks interaction with previous routes.
  ModalRoute({
    RouteSettings settings,
  }) : super(settings: settings);

所以整个新页面 Widget 使用如下:

// 新路由页面
class NewRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Map args = ModalRoute.of(context).settings.arguments;
    return Scaffold(
        appBar: AppBar(title: Text('获取参数')),
        body: Center(
            child: Column(
          children: <Widget>[
            Text(args.toString()),
            FlatButton(
              child: Text('pop'),
              onPressed: () {
                Navigator.pop(context);
              },
            )
          ],
        )));
  }
}

效果:

GIF.gif

四、完整代码

2、基础路由使用 & 路由传值

https://github.com/postbird/FlutterHelloWorldDemo/blob/master/demo1/lib/bak/main.27-%E5%9F%BA%E7%A1%80%E8%B7%AF%E7%94%B1%26%E5%9F%BA%E7%A1%80%E8%B7%AF%E7%94%B1%E4%BC%A0%E5%80%BC.dart

1、Modal 路由传值

https://github.com/postbird/FlutterHelloWorldDemo/blob/master/demo1/lib/bak/main.4-Route.dart

--