FlutterFlutter

Flutter Navigation(导航)

2018-12-06  本文已影响103人  iwakevin

在 Flutter 里实现页面的导航需要使用两个类:NavigatorRoute。Navigator 负责页面的栈结构处理,Route 复杂页面的路径处理。

在 Flutter 里导航器使用栈的结构管理页面,当需要添加一个页面时,使用入栈的方式。当需要退出一个页面时,使用出栈的方式。也可以不入栈而是直接替换当前页面。

路由表

移动应用程序通常管理大量的路由,通过名称来引用它们通常是最容易的。

简单的做法是在 runApp 里,就定义好所有的路由,这样可以做集中式管理,也是非常建议的做法。

一个 MaterialApp 是最简单的设置方式,MaterialApp 的 home 成为导航器堆栈底部的路由。要在堆栈上推送(push)一个新路由,可以创建一个具有构建器功能的MaterialPageRoute 实例。

void main() {
    runApp(new MaterialApp(
        home: new MyAppHome(),
        // 路由
        routes: <String, WidgetBuilder> {
            '/a': (BuildContext context) => new MyPage(title: 'page A'),
            '/b': (BuildContext context) => new MyPage(title: 'page B'),
            '/c': (BuildContext context) => new MyPage(title: 'page C'),
        },
    ));
}

当需要路由切换的时候,可以这样处理。

Navigator.pushNamed(context, '/b');

传递数据

很多时候在切换路由时需要传递一些数据,比如 id 之类的,那么应该怎么做?

在使用动态路由时,可以通过闭包环境来传递数据。

String id = 'abc';                                              // <--- id

Navigator.push(context, new MaterialPageRoute<void> (
    // 新的页面
    builder: (BuildContext context) {
        return new Scaffold(
            body: new Center(
                child: new FlatButton(
                    child: new Text('POP'),
                    onPressed: () {
                        print(this.id);                         // <--- id
                        Navigator.pop(context);
                    },
                ),
            ),
        );
    },
));

在使用路由表的时候,只能提供全局变量来传递信息。

var id = 'abc';

void main() {
    runApp(new MaterialApp(
        home: new MyAppHome(),
        // 路由
        routes: <String, WidgetBuilder> {
            '/a': (BuildContext context) => new MyPage(id: id),
        },
    ));
}

入栈一个页面

当需要入栈一个页面时,使用 Navigator.push 函数,这个函数接受一个上下文和新的页面结构。第二个参数是一个路由器 MaterialPageRoute

// 在某个点击事件里
Navigator.push(context, new MaterialPageRoute<void> (
    // 新的页面
    builder: (BuildContext context) {
        return new Scaffold(
            appBar: new AppBar(title: new Text('My Page')),
            body: new Center(
                child: new FlatButton(
                    child: new Text('POP'),
                    onPressed: () {
                        Navigator.pop(context);
                    },
                ),
            ),
        );
    },
));

出栈一个页面

当需要出栈一个页面时,使用 Navigator.pop 函数,这个函数接受一个上下文。

new FlatButton(
    child: new Text('POP'),
    onPressed: () {
        Navigator.pop(context);
    },
),

接受返回值

实际上 pushNamed 是一个异步函数,它返回一个 Future,在使用 Navigator.pop 的时候,第二个参数就是返回的值。

Navigator.pop(context, 'abc');

Navigator.pushNamed(context, '/router/second').then((value) {
    print(value);   // --> abc
});

导航切换动画

默认的导航切换动画,在 Android 上是一个从底端到顶端的弹出过程,在 iOS 上是一个从右边到左边的平移过程。

为了统一,可以通过自定义一个路由,包括路由模态屏障的颜色和行为以及路由其他方面的动画转换。

// 动画效果,要把它放在外面去,并且是 static 的。
static SlideTransition createTransition(Animation<double> animation, Widget child) {
    return new SlideTransition(
        // 从右到左
        position: new Tween<Offset>(
            begin: const Offset(1.0, 0.0),
            end: const Offset(0.0, 0.0),
        ).animate(animation),
        child: child,
    );
}

Navigator.of(context).push(
    new PageRouteBuilder(
        pageBuilder: (
            BuildContext context,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
        ) {
        // 目标页
        return new PageView();
    },
    transitionsBuilder: (               // --> 固定写法
        BuildContext context,
        Animation<double> animation,
        Animation<double> secondaryAnimation,
        Widget child,
    ) => HomePageState.SlideTransition(animation, child),
));

实际上,默认在 Android 上的效果是:

// 从下到上
position: new Tween<Offset>(
    begin: const Offset(0.0, 1.0), // (x, y)
    end: const Offset(0.0, 0.0),
).animate(animation),

这里只是把 x 与 y 对换就变成了平移。

得出几个不同的方向动画。

// 从上到下
position: new Tween<Offset>(
    begin: const Offset(0.0, -1.0), // (x, y)
    end: const Offset(0.0, 0.0),
).animate(animation),

// 从左到右
position: new Tween<Offset>(
    begin: const Offset(-1.0, 0.0), // (x, y)
    end: const Offset(0.0, 0.0),
).animate(animation),

神奇的是 begin 可以使用对角移动的方式,也就是 (1.0, 1.0)、(-1.0, -1.0)

除了平移之外,还可以定义一些旋转的动画。

static FadeTransition createFadeTransition(Animation<double> animation, Widget child) {
    return new FadeTransition(
        opacity: animation,
        child: new RotationTransition(
            turns: new Tween<double>(
                begin: 0.8,
                end: 1.0
            ).animate(animation),
            child: child,
        ),
    );
}

当 begin 与 end 都为 1.0 时,FadeTransition 是一个透明渐变过程。

上一篇 下一篇

猜你喜欢

热点阅读