Flutter基础——导航

Posted by lingjye on July 18, 2019

Flutter使用Navigator和Routes来管理页面导航,相应的,在iOS中有push/pop和‘present/dismiss’等方式。一个路由是App中‘屏幕’或‘页面’的抽象,一个Navigator是管理多个路由的Widget,它的工作原理与iOS中的UINavigationController相似。

在页面之间跳转可以有以下选择:

  1. 具体制定一个有路由名构成的Map。(MaterailApp)
  2. 直接跳转到一个路由。(WidgetApp)

构建Map:

1
2
3
4
5
6
7
8
9
10
void main() {
  runApp(MaterialApp(
    home: MyAppHome(), // becomes the route named '/'
    routes: <String, WidgetBuilder> {
      '/a': (BuildContext context) => MyPage(title: 'page A'),
      '/b': (BuildContext context) => MyPage(title: 'page B'),
      '/c': (BuildContext context) => MyPage(title: 'page C'),
    },
  ));
}

通过路由名字,实现跳转(push):

1
Navigator.of(context).pushNamed('/b');

自定义路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Navigator.push(context, PageRouteBuilder(
    opaque: false,
    pageBuilder: (BuildContext context, _, __){
      return Center(child: Text('My Page Route'));
    },
    transitionsBuilder: (__, Animation<double> animation, ____, Widget child) {
      return FadeTransition(
        opacity: animation,
        child: RotationTransition(
          turns: Tween<double>(begin: 0.5, end: 1.0).animate(animation),
          child: child,
        ),
      );
    }
));

页面路径由两部分构成,“页面”和“转场”(transition)。该页面成为传递给该transitionsBuilder函数的子代的后代。通常情况下,该页面仅内置一次,因为它不依赖于它的动画参数(在这个例子中_ ,__是省略的参数)。过渡是基于持续过程的每一帧。

使用Navigator类还可以用来获取push到栈中的路由返回的结果,通过await等待路由返回的结果,或者使用then()方法来获取:

1
2
3
4
5
6
Map coordinates = await Navigator.of(context).pushNamed('/c');

// 或者
// Navigator.of(context).pushNamed('/c').then((data){
//    print('then=$data');
// });

注意,此处使用了await,该函数应该添加async关键字。

在 location 路由中,一旦用户选择了地点,携带结果一起 pop() 出栈:

1
Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});

导航示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class NavigationSampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      title: 'Navigation',
      theme: ThemeData(
        primarySwatch: Colors.red
      ),
      home: NavigationSampleAppPage(title: 'Home Page',),
      routes: <String, WidgetBuilder> {
        '/a': (BuildContext context) => NavigationSampleAppPage(title: 'Home Page'),
        '/b': (BuildContext context) => NavigationSampleAppPage(title: 'page two'),
        '/c': (BuildContext context) => NavigationSampleAppPage(title: 'page three'),
      },
    );
  }
}

class NavigationSampleAppPage extends StatefulWidget {
  final String title;
  NavigationSampleAppPage({Key key, this.title}) : super(key: key);

  _NavigationSampleAppPageState createState() => _NavigationSampleAppPageState(title);
}

class _NavigationSampleAppPageState extends State<NavigationSampleAppPage> {
  final String title;

  _NavigationSampleAppPageState(this.title);

  Future _push() async {
    var result;
    if (this.title.contains('Home')) {
      Navigator.of(context).pushNamed('/b');
    } else if (this.title.contains('two')) {
      // 带返回值,方式一
      Navigator.of(context).pushNamed('/c').then((data){
        print('then=$data');
      });
      // 带返回值方式二
      // result = await Navigator.of(context).pushNamed('/c');
    } else if (this.title.contains('three')) {
      // 返回page one
      Navigator.of(context).popUntil(ModalRoute.withName('/b'));
      // 带返回值
      // Navigator.of(context).pop({'key': 'value'});
    }
    print('result = $result');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.favorite),
            onPressed: _push,
          )
        ],
      ),
    );
  }
}

跳转第三方APP

在iOS中,使用URL Scheme方式。在Flutter中,可以创建一个原省平台的整合层,或者使用现有插件,例如:url_launcher

本文Demo