Flutter 路由跳轉及傳值詳解(Navigator的使用)

夜夕i發表於2019-09-22

在 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),),
    );
  }
}
複製程式碼

圖片載入失敗!

原始碼

點選-->原始碼地址


@_@

相關文章