路由与导航

路由与导航

在原生的Android中,一个Activity称为一个路由,在iOS中指的是一个ViewController。如果需要打开一个新的路由,在Android中可以使用startActivity()方法,并且传入参数。在iOS中可以使用pushViewController()。

而在Flutter中,使用了Router与Navigator来组成统一的管理,Router是页面的一个抽象概念,用这个可以创建界面、接收参数以及响应Navigator的打开和关闭;而Navigator则是用于管理和维护路由栈,打开路由页面或者关闭路由页面,即入栈出栈操作。

在Flutter中,路由分为两种,一种是基本路由,一种是命名路由。

  • 基本路由:无需提前注册,在切换页面的时候需要手动构造页面的实例。
  • 命名路由:需要提前注册路由页面标识符,在页面切换时通过路由标识符打开一个新的路由页面。

基本路由

如果需要打开一个新的页面,需要创建一个MaterialPageRoute对象实例,然后调用Navigator.push(),那么Flutter就会把新跳转的页面放到栈顶。如果需要关闭新的页面,那么使用Navigator.pop()即可。一个样例代码如下:

first_page.dart

import 'package:flutter/material.dart';
import 'second_page.dart';

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("first page"),),
      body: Center(
        child: TextButton(
          child: Text("turn to second page"),
          onPressed: () => Navigator.push(
              context, MaterialPageRoute(builder: (context) => SecondPage())),
        ),
      ),
    );
  }
}

second_page.dart

import 'package:flutter/material.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("second page"),),
      body: Center(
        child: TextButton(
          child: Text("back to first page"),
          onPressed: () => Navigator.pop(context),
        ),
      ),
    );
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_router_page/first_page.dart';

void main(List<String> args) => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: FirstPage(),);
  }
}

这样就可以做成一个简单的路由跳转了。创建新的路由对象使用的是MaterialPageRoute类,该类是PageRoute的子类,定义了路由创建及切换时过渡动画的相关接口和属性,并且自带页面切换动画。

命名路由

为了必要频繁的创建MaterialPageRoute实例,可以使用命名路由的方式。在命名路由中,需要提前创建好映射规则,即路由表。对应路由表中,第一个参数对应页面的名字,第二个参数对应页面,且需要以MaterialApp为根。

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_namerouter_page/first_page.dart';
import 'package:flutter_namerouter_page/second_page.dart';
import 'package:flutter_namerouter_page/unknown_page.dart';

void main(List<String> args) => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      /// 路由表结构
      routes: {
        'first': (context) => FirstPage(),
        'second': (context) => SecondPage(),
      },
      /// 初始化页面
      initialRoute: 'first',
      /// 路由表中没有的页面会跳转到的地方
      onUnknownRoute: (RouteSettings setting) =>
          MaterialPageRoute(builder: (context) => UnKnownPage()),
    );
  }
}

first_page.dart

import 'package:flutter/material.dart';

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("first page"),
      ),
      body: Center(
        child: TextButton(
          child: Text("turn to second page"),
          onPressed: () => {Navigator.pushNamed(context, "second")},
        ),
      ),
    );
  }
}

second_page.dart

import 'package:flutter/material.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("second page"),
      ),
      body: Center(
          child: Column(
        children: [
          TextButton(
            child: Text("back to first page"),
            onPressed: () => {Navigator.pop(context)},
          ),
          TextButton(
            child: Text("turn to first page"),
            onPressed: () => Navigator.pushNamed(context, 'first'),
          ),
          TextButton(
              onPressed: () => Navigator.pushNamed(context, 'third'),
              child: Text("turn to third page")),
        ],
      )),
    );
  }
}

unknown.dart

import 'package:flutter/material.dart';

class UnKnownPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("unknown page"),
      ),
      body: Center(
        child: TextButton(
          child: Text("unknown page"),
          onPressed: () => Navigator.pop(context),
        ),
      ),
    );
  }
}

路由传参

在Flutter中,路由类似于Activity一样。在Android中进行页面传参,使用的是Intent进行传递,然后使用startActivityForResult()进行传参。

在Flutter中,使用Navigator.of(context).pushNamed('页面名称', arguments: 传递的数据).then((value){如果是StatefulWidget的话,那么可以在此使用setState进行设置。})

在第二个页面中,使用 ModalRoute.of(context).settings.arguments 获取页面传输过来的参数,类似于Android中的getIntent() ,然后进行转换。在第二个页面中,可以使用 Navigator.pop(context,'传递的参数')

路由栈

在Android中,Activity具有四种不同的启动模式,standard,singleTask,singleTop,singleInstance。在Flutter中,使用了安卓中的启动模式的管理方式。

在Flutter中,可以单独移除路由栈中的一个特定的页面。使用Navigator.removeRoute() & Navigator.removeRouteBelow() 进行移除。

方式1: push() & pushNamed() => pop()

这种方式类似于Android中的standard的模式,模型图如下:

使用push或者pushNamed后的模型图

路由与导航

使用pop后的模型图

路由与导航

方式2: pushReplacement() & pushReplacementNamed()

这种方式用在路由栈顶页面的替换场景,比如栈顶是b,想替换成c,那么使用该函数即可。类似于Android中先把栈顶弹出,然后再放入c路由。这种模型的方式所展现的动画仅仅是路由c的进入动画,并没有b的退出动画。模型图如下:

路由与导航

方式3: popAndPushNamed()

这种方式类似于方式2,但是在动画方面有不一样的地方。使用这种方式,在b弹出的时候,会显示b的弹出动画,而c进入的时候,又回显示c的进入动画。比方式2多一个弹出动画。

方式4: pushAndRemoveUntil() & pushNamedAndRemoveUntil()

从路由栈中,新添加一个页面,并且删除路由栈中所有之前的路由。也就是说,使用这种方式进行路由的添加,路由栈中仅有一个路由。也可以使用这个方法,进行打开一个新的页面,并且制定路由栈中的某一个页面以上的路由页面都删除。

比如执行这条语句Navigator.pushNamedAndRemoveUtil(context,'page_e',ModalRoute.withName('page_b')) 意思是打开一个d页面,然后删除b页面以上的页面,示意图如下:

路由与导航

方式5: popUntil()

一直弹出,直到某一个页面位置,没有push的操作。Navigator.popUtil(context,ModalRoute.withName('page_a'))

路由与导航

自定义路由

如果在两个路由之间跳转,需要自定义动画,那么会使用到自定义路由。自定义路由需要继承PageRouteBuilder类。该类是所有自定义路由的基类。在PageRouteBuilder中,有几个属性比较重要,如下:

编号 名称 作用
1 pageBuilder 用来创建所需要跳转的路由页面
2 opaque 是否需要遮挡整个页面
3 transitionsBuilder 用于自定义专场动画
4 transitionDuration 自定义专场动画的执行时间
上一篇:人生苦短,我用python


下一篇:dart 序列化JSON数据