Flutter 路由跳转及传值详解(Navigator的使用)

9,009 阅读3分钟

在 Flutter 中想要实现页面间的跳转和传值的话,离不开两种路由:

  • 基本路由
  • 命名式路由

官方说的是静态路由和动态路由,不过我习惯了这样叫,所以就暂且这样说吧!

基本路由

基本跳转

基本路由的跳转
使用push跳到指定的页面,然后再使用pop回到原来的页面

不过当你跳转过去的时候,人家默认会有一个返回的箭头按钮,点击就可以返回

开始撸代码

我在main.dart文件中引用了Home.dart文件
Home.dart文件中有一个按钮,当我点击的时候,可以跳到详情页面

注:如果用stl生成的静态控件是不能用跳转按钮的

Home.dart文件代码:

//Home.dart
import 'package:flutter/material.dart';
import './Detail.dart';

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          RaisedButton(
            child: Text('跳到详情页面'),
            onPressed: (){
              //跳转页面
              Navigator.of(context).push(
                MaterialPageRoute(
                  //没有传值
                  builder: (context)=>Detail()
                )
              );
            },
          )
        ],
      ),
    );
  }
}

当我点击了按钮的时候,就可以跳到详情页面:
Detail.dart文件代码:

import 'package:flutter/material.dart';

class Detail extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //浮动按钮
      floatingActionButton: FloatingActionButton(
        child: Text('返回'),
        onPressed: (){
          Navigator.of(context).pop();
        },
      ),
      appBar: AppBar(title: Text("详情页面"),),
      body: Text("详情页面"),
    );
  }
}

如图,跳过去后默认会有一个返回按钮,点击后就可以返回 ,
也可以自己定义一个按钮,利用pop方法点击后也可以返回

跳转传值

有时候我们跳转的时候需要传递参数,
这时候我们就要携带参数跳转,当然非常简单

我们需要在使用参数的页面定义一个变量,注意需要设置默认值,如果没给你传值的时候使用默认值
然后在跳转的时候给目标页面传值

Home.dart文件代码:

//Home.dart
//只贴按钮的代码,其余的和上面一样

RaisedButton(
	child: Text('跳到详情页面'),
	onPressed: (){
	  //跳转页面
	  Navigator.of(context).push(
		MaterialPageRoute(
		  //传值
		  builder: (context)=>Detail(Test:'我是参数')
		  //没传值
		  //builder: (context)=>Detail()
		)
	  );
	},
  )

然后在目标页面接收,如果没传值默认为上面定义的默认值

Detail.dart文件代码:

class Detail extends StatelessWidget {
  //需要定义变量和默认值
  String Test;
  Detail({this.Test='没有给我传值'});
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //浮动按钮
      floatingActionButton: FloatingActionButton(
        child: Text('返回'),
        onPressed: (){
          Navigator.of(context).pop();
        },
      ),
      appBar: AppBar(title: Text("详情页面"),),
      body: Text(this.Test),
    );
  }
}

返回时接收值

有时候我们跳转的时候不需要传值,而当你返回的时候需要从子组件中接收值

用下面这个例子来说明:

新建两个dart文件AddressList.dartGetAddress.dart文件,在mian.dart中使用GetAddress.dart文件

我们想要点击GetAddress.dart文件中的 选择收获地址 按钮,跳到添加地址页面,然后选择地址后,直接带着数据返回到首页面并显示出来

代码示例:

main.dart文件只改了需要渲染的控件

import 'package:flutter/material.dart';
import './address/GetAddress.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Route"),
        ),
        body: GetAddress()
      ),
    );
  }
}

注意获取数据都是异步的,需要加async、await
改变数据的唯一方式是利用setState方法

GetAddress.dart文件代码:

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

class GetAddress extends StatefulWidget {
  @override
  _GetAddressState createState() => _GetAddressState();
}

class _GetAddressState extends State<GetAddress> {
  //定义一个变量
  String _ads = '';
  
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        appBar: AppBar(title: Text('获取收获地址'),),
        body: Center(
          child: Column(
            //垂直居中
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              RaisedButton(
                //按钮主题
                textTheme: ButtonTextTheme.primary,
                color: Theme.of(context).accentColor,
                child: Text('选择收货地址'),
                //点击
                onPressed: () async {
                    //通过路由跳转从子页面中传递过来的数据,都是异步的
                    var ads = await Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (BuildContext context){
                          return AddressList();
                        }
                      )
                    );
                    setState(() {
                     _ads = ads; 
                    });
                },
              ),
              Text('${_ads==""?"未查到收货地址":_ads}')
            ],
          ),
        ),
      ),
    );
  }
}

利用onTap点击后直接使用pop返回到首页面,并把数据带回去

AddressList.dart文件代码:

import 'package:flutter/material.dart';

class AddressList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("我的地址列表"),),
      body: ListView(
        padding: EdgeInsets.all(10),
        children: <Widget>[
          GestureDetector(
            onTap: (){
              //pop后面可以跟上参数
              Navigator.of(context).pop('北京');
            },
            //给子组件添加事件
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(color: Colors.black26)
              ),
              child: ListTile(
                leading: Icon(
                  Icons.account_box,
                  color: Colors.blue,
                ),
                title: Text(
                  '北京',
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
            ),
          ),
          //给控件中间加间隙
          SizedBox(height: 10),
          GestureDetector(
            onTap: (){
              //pop后面可以跟上参数
              Navigator.of(context).pop('河南');
            },
            //给子组件添加事件
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(color: Colors.black26)
              ),
              child: ListTile(
                leading: Icon(
                  Icons.account_box,
                  color: Colors.blue,
                ),
                title: Text(
                  '河南',
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
            ),
          ),
          //给控件中间加间隙
          SizedBox(height: 10),
          GestureDetector(
            onTap: (){
              //pop后面可以跟上参数
              Navigator.of(context).pop('上海');
            },
            //给子组件添加事件
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(color: Colors.black26)
              ),
              child: ListTile(
                leading: Icon(
                  Icons.account_box,
                  color: Colors.blue,
                ),
                title: Text(
                  '上海',
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
            ),
          )
        ],
      ),
    );
  }
}

命名式路由

类似于 Vue 中的路由

基本跳转

新建了几个页面Main.dartPage1.dartPage1.dartPage3.dart 用来测试

引入文件,
main.dart中的routes中配置路径(必须在main.dart中配置)
body换成需要渲染的页面

main.dart文件代码:

import 'package:flutter/material.dart';
import './files/Page1.dart';
import './files/Page2.dart';
import './files/Page3.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Route"),
        ),
        body: Main(),
      ),
      //定义路由
      routes: {
        //需要使用context指定上下文
        '/page1': (context) => Page1(),
        '/page2': (context) => Page2(),
        '/page3': (context) => Page3(),
      },
    );
  }
}

Main.dart文件中写两个按钮用来跳转(注意一个是main.dart一个是Main.dart

Main.dart文件代码:

import 'package:flutter/material.dart';

class Main extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              //定义按钮
              RaisedButton(
                  child: Text("跳转到Page1"),
                  onPressed: () {
                    //需要使用pushNamed方法
                    Navigator.pushNamed(context, "/page1");
                  },
              ),
              SizedBox(height: 20),
              RaisedButton(
                  child: Text("跳转到Page2"),
                  onPressed: () {
                    Navigator.pushNamed(context, "/page2");
                  },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

点击按钮跳转到指定页面,Page1.dartPage2.dartPage3.dart页面的基本结构,只贴一个页面看一下:

Page1.dart文件代码:

import 'package:flutter/material.dart';

class Page1 extends StatefulWidget {
  @override
  _Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Page1"),),
      body: Text("Page1",style: TextStyle(fontSize: 40),),
    );
  }
}

路由抽离跳转

一般我们使用命名式路由的话,不用上面的方式,我们都是单独把路由抽离出去

优化上面的代码:

新建一个Routes.dart文件用来存放路由规则,

Routes.dart文件代码:

import 'package:flutter/material.dart';
//引入文件
import '../files/Main.dart';
import '../files//Page1.dart';
import '../files//Page2.dart';
import '../files//Page3.dart';

//配置路由规则
final routes = {
  '/': (context) => Main(),
  '/page1': (context) => Page1(),
  '/page2': (context) => Page2(),
  '/page3': (context) => Page3(),
};

// 如果你要把路由抽离出去,必须写下面这一堆的代码,不用理解什么意思
var onGenerateRoute = (RouteSettings settings) {
  // 统一处理
  final String name = settings.name;
  final Function pageContentBuilder = routes[name];
  if (pageContentBuilder != null) {
    if (settings.arguments != null) {
      final Route route = MaterialPageRoute(
          builder: (context) =>
              pageContentBuilder(context, arguments: settings.arguments));
      return route;
    } else {
      final Route route =
          MaterialPageRoute(builder: (context) => pageContentBuilder(context));
      return route;
    }
  }
};

然后在main.dart文件中配置使用:

import 'package:flutter/material.dart';
//引入Routes.dart文件
import './routes/Routes.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',  //配置默认访问路径
      onGenerateRoute:onGenerateRoute,  //必须加上这一行,固定写法
    );
  }
}

配置好后,别的地方都不用动,这样就实现了我们的路由抽离

跳转传值

紧跟着上面的代码,当我们使用命名式路由,并且把路由抽离后进行跳转时,我们想要跳转过去的时候给他传值,那么怎么办呢?很简单

在上面代码的基础上改动

改变Main.dart文件中的按钮方法,当跳转的时候传值,如下:

只粘贴第二个跳转按钮,别的代码不用动

RaisedButton(
	  child: Text("跳转到Page2"),
	  onPressed: () {
		Navigator.pushNamed(context, "/page2",arguments: {
		  "id":102
		});
	  },
  ),

然后在Routes.dart文件中改变page2路由规则:

'/page2': (context,{arguments}) => Page2(arguments:arguments),

最后在Page2.dart页面定义变量,重构,接收传递过来的值:

import 'package:flutter/material.dart';

class Page2 extends StatefulWidget {
  //定义一个变量
  final arguments;
  //重构
  Page2({this.arguments});
  
  @override
  _Page2State createState() => _Page2State();
}

class _Page2State extends State<Page2> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Page2"),),
      //使用传递过来的值
      body: Text("${widget.arguments['id']}",style: TextStyle(fontSize: 40),),
    );
  }
}

源码

点击-->源码地址


@_@