Flutter 之Router 页面跳转

页面跳转在移动开发中是很常见的事情,在Android中打开另外一个页面主要是用startActivity这个方法,在Flutter中也是提供这种能力,主要的使用方式就是通过Navigator 去打开一个页面

1.跳转到另外一个页面

构建FirstScreen和SecondScreen 页面


import 'package:flutter/material.dart';

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) {
              return SecondScreen();
            }));
          },
          child: Text("next screen"),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: RaisedButton(
            onPressed: () {
               Navigator.pop(context);
            },
          child: Text("back"),
        ),
      ),
    );
  }
}

 

这里就是跳转的主要代码


Navigator.push(context, MaterialPageRoute(builder: (context) {
  return SecondScreen();
}));

push方式详解


static Future<T> push<T extends Object>(BuildContext context, Route<T> route)

 

第一个参数就是上下问信息,类似Android中的Context,第二个参数就是路由信息,也就是要打开的主要页面是哪个,MaterialPageRoute 就是Route其中的一个子类,用于在Material Desgin 模式下打开页面的


Navigator.pop(context);

是用来返回上一个页面的

 

android页面向flutter页面传递数据 flutter页面跳转_ide

 


 

android页面向flutter页面传递数据 flutter页面跳转_Text_02

2.通过routes路径方式跳转到下一个页面

先定义Routes路由表,实际上就是一个Map结构,key是路径,value就是对应的页面


import 'package:flutter/material.dart';

import 'navigation/navigation_demo.dart';

void main() {
  runApp(MaterialApp(
    initialRoute: "/",
    routes: {
      "/": (context) => FirstScreen(),
      '/second': (context) => SecondScreen(),
    },
  ));
}

  

routes 就是一个map结构,根目录/对应的页面就是FirstScreen,/second路径对应的页面就是ScendScreen,在FirstScreen中打开SecondScreen的方式我们换一下,要通过Navigator.pushNamed方式打开一个在路由表中已经存在的页面


class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pushNamed(context, "/second");
//            Navigator.push(context, MaterialPageRoute(builder: (context) {
//              return SecondScreen();
//            }));
          },
          child: Text("next screen"),
        ),
      ),
    );
  }
}

  

3.传递数据到下一个页面

传递数据到下一个页面也是比较常见的情况,例如说在一个相册应用中,有一个列表页面,单击列表中某一个item,应该跳转到照片的详情页面,其实这种情况就应该把照片的信息传递给另外一个页面

传递的方式有两种:

  • 在构造方法中传递数据
  • 在Route中传递数据给下一个页面

在第一个页面构造要传递的数据


class Photo {
  String title;
  String message;

  Photo({this.title, this.message});
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pushNamed(context, "/second", arguments: Photo(title: "pass title",message: "pass message"));
//            Navigator.push(context, MaterialPageRoute(builder: (context) {
//              return SecondScreen();
//            }));
          },
          child: Text("next screen"),
        ),
      ),
    );
  }
}

  

在第二个页面获取数据


class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final Photo photo=ModalRoute.of(context).settings.arguments;
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen ${photo.title}"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text("back ${photo.message}"),
        ),
      ),
    );
  }
}

  

这种方式有种不太好的地方就是需要在下一个页面通过ModalRoute.of(context).settings.arguments; 方式获取传递的数据,其实Flutter中已经提供了这种方式简便处理方式


import 'package:flutter/material.dart';

import 'navigation/navigation_demo.dart';

void main() {
  runApp(MaterialApp(
    home: FirstScreen(),
    onGenerateRoute: (settings) {
      if (settings.name == ThreeScreen.routeName) {
        final Photo args = settings.arguments;
        return MaterialPageRoute(builder: (context) {
          return ThreeScreen(
            title: args.title,
            message: args.message,
          );
        });
      }
    },
  ));
}

 

onGenerateRoute 是用来统一拦截传递参数的方法,我们可以在这个地方获取传递的数据,然后在构造页面的时候把参数传递给目标页面,这样在目标页面也就是不用考虑如何解析传递过来的数据了


class ThreeScreen extends StatelessWidget {
  static const routeName = '/extractArguments';

  final String title;
  final String message;

  ThreeScreen({this.title, this.message});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("second"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(title),
            Text(message),
          ],
        ),
      ),
    );
  }
}

 

在这个页面,数据都是通过构造方法中传递了,减少了在页面获取传递数据的代码


class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pushNamed(context, ThreeScreen.routeName,
                arguments: Photo(title: "args title", message: "args message"));
          },
          child: Text("next screen"),
        ),
      ),
    );
  }
}

 

发送方式的代码没有改变

 

android页面向flutter页面传递数据 flutter页面跳转_ide_03

4.接收页面返回值

有的时候我们希望在前一个页面接收另外一个页面的数据,这个怎么处理呢


class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: FirstButton(),
    );
  }
}

class FirstButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        onPressed: () {
          Navigator.pushNamed(context, "/second",
                  arguments:
                      Photo(title: "pass title", message: "pass message"))
              .then((vale) {
            final snackBar = SnackBar(
              content: Text('Yay! A SnackBar!'),
              action: SnackBarAction(
                label: 'Undo',
                onPressed: () {
                },
              ),
            );
            Scaffold.of(context).showSnackBar(snackBar);

          });
        },
        child: Text("next screen"),
      ),
    );
  }
}

 

关键的代码是这段,then 方法用来处理接收数据后的处理逻辑,这个例子中主要通过SnackBar 展示一下接收的信息


Navigator.pushNamed(context, "/second",
        arguments:
            Photo(title: "pass title", message: "pass message"))
    .then((vale) {
  final snackBar = SnackBar(
    content: Text('Yay! A SnackBar!'),
    action: SnackBarAction(
      label: 'Undo',
      onPressed: () {
      },
    ),
  );
  Scaffold.of(context).showSnackBar(snackBar);
});

 

为什么要单独抽取出FirstButton组件?

是因为SnackBar只能在Scaffold 组件代码中使用会报错

下面代码是用于在推出当前页面的时候,处理了ok 给前一个页面


Navigator.pop(context, "ok");

 

 

android页面向flutter页面传递数据 flutter页面跳转_ide_04

 

总结

使用上跟Android 的使用方式类似,有点经验的人掌握这个不是很难

https://docs.flutter.io/flutter/widgets/Navigator-class.html

https://www.raywenderlich.com/110-flutter-navigation-tutorial

作者:饥饿的大灰狼