在 Flutter 中想要實現頁面間的跳轉和傳值的話,離不開兩種路由:
- 基本路由
- 命名式路由
官方說的是靜態路由和動態路由,不過我習慣了這樣叫,所以就暫且這樣說吧!
基本路由
基本跳轉
基本路由的跳轉
使用push
跳到指定的頁面,然後再使用pop
回到原來的頁面
不過當你跳轉過去的時候,人家預設會有一個返回的箭頭按鈕,點選就可以返回
開始擼程式碼
我在main.dart
檔案中引用了Home.dart
控制元件
在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()
)
);
},
)
],
),
);
}
}
複製程式碼
當我點選了按鈕的時候,就可以跳到詳情頁面:
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
//只貼按鈕的程式碼,其餘的和上面一樣
RaisedButton(
child: Text('跳到詳情頁面'),
onPressed: (){
//跳轉頁面
Navigator.of(context).push(
MaterialPageRoute(
//傳值
builder: (context)=>Detail(Test:'我是引數')
//沒傳值
//builder: (context)=>Detail()
)
);
},
)
複製程式碼
然後在目標頁面接收,如果沒傳值預設為上面定義的預設值
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.dart
和GetAddress.dart
檔案,在mian.dart
中使用GetAddress.dart
檔案
我們想要點選GetAddress.dart
檔案中的 選擇收穫地址 按鈕,跳到新增地址頁面,然後選擇地址後,直接帶著資料返回到首頁面並顯示出來
程式碼示例:
mian.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()
),
);
}
}
複製程式碼
GetAddress.dart
檔案 ,
注意獲取資料都是非同步的,需要加async、await
改變資料的唯一方式是利用setState
方法
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}')
],
),
),
),
);
}
}
複製程式碼
AddressList.dart
檔案
利用onTap
點選後直接使用pop
返回到首頁面,並把資料帶回去
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.dart
、Page1.dart
、Page1.dart
和Page3.dart
用來測試
引入檔案,
在main.dart
中的routes
中配置路徑(必須在main.dart中配置)
body
換成需要渲染的頁面
程式碼:
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
)
程式碼:
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.dart
、Page2.dart
、Page3.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),),
);
}
}
複製程式碼
原始碼
點選-->原始碼地址