每一個應用都有不少個頁面,在flutter中一樣也有不少頁面,被稱之爲路由(Router),頁面之間的跳轉經過導航器(Navigator)進行管理。 其中 Navigator.push 和 Navigator.pop 是最簡單的跳轉到新頁面和返回到上一級界面的方式。程序員
路由分爲靜態路由(即命名路由)和動態路由。頁面之間跳轉時每每須要傳遞參數,這稱之爲路由傳值。下面咱們會一一帶領你們學習。app
經過本篇文章的學習咱們的目標是熟練掌握路由及傳值,之後進行應用開發時對頁面跳轉方面再也不疑惑。框架
flutter中萬物皆widget,咱們的頁面(route)也是widget的子類,因此咱們定義一個界面也是經過繼承widget實現。 前面的博客咱們已經定義過界面了,好比計數器實例,就是一個簡單的頁面,也就是一個路由。下面咱們來詳細實現一個界面。 首先我須要一個入口函數,這個相信你們已經很熟悉了,就是在main方法中調用runApp函數進入應用,咱們就不作詳細介紹了,直接給出代碼。less
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 經常使用固定寫法,生成Material風格的App
return MaterialApp(
title: "路由使用",
theme: ThemeData(
// 默認爲亮色主題,能夠設置[Brightness.dark]變成黑暗模式
brightness: Brightness.light,
),
home: HomePage(), // 首頁面
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 經過Scaffold能夠方便的生成一個Material風格的頁面
return Scaffold(
// 頂部導航欄
appBar: AppBar(
title: Text("主頁面"),
),
body: Center(
child: RaisedButton(
child: Text("我是第一個界面,點擊我進入第二個界面"),
onPressed: () {
print("我是第一個界面,點擊我進入第二個界面");
},
),
),
);
}
}
複製代碼
上面的代碼是咱們最常規的包含一個主頁面的應用。後續咱們寫應用時候的基本框架也是在此基礎上進行擴展。 如今咱們的想法是點擊這個頁面上的按鈕跳轉的第二個界面,首先咱們須要構造第二個界面。構造第二個界面其實和咱們構造第一個 界面HomePage同樣,繼承widget重寫本身想要的樣式便可。實現了頁面就要開始跳轉邏輯。ide
靜態路由即命名路由,在經過Navigator進行跳轉以前,須要在MaterialApp組件內顯式聲明路由的名稱,一旦聲明,路由的跳轉 方式就固定了,因此稱之爲靜態路由,有惟一的名稱因此也稱之爲命令路由。顯式聲明路由經過在MaterialApp內的routes屬性進行定義。函數
若是咱們有不少個頁面和不少個其餘類型的組件都放在lib下,對於後期維護簡直是一大折磨,因此分包是大多數平臺的常規操做, 就是對有同一種特性的東西放置在同一個包下,好比頁面類的組件都放在pages包,工具類的組件放在utils包下等。 接下來咱們就新建一個pages包,把第二個界面SecondPage放進去,把第一個界面HomePage也提取出來放到這個包下。工具
咱們分爲一下三步進行靜態路由的跳轉:學習
import 'package:flutter/material.dart';
// 引入頁面路徑
import 'pages/HomePage.dart';
import 'pages/SecondPage.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 經常使用固定寫法,生成Material風格的App
return MaterialApp(
title: "路由使用",
theme: ThemeData(
// 默認爲亮色主題,能夠設置[Brightness.dark]變成黑暗模式
brightness: Brightness.light,
),
// 默認加載的頁面
initialRoute: '/', // 首頁面
// 顯式聲明界面列表
routes: {
'/': (context) => HomePage(),
'/secondPage': (context) => SecondPage(),
},
);
}
}
複製代碼
首頁面單獨提取出來以後的代碼以下。優化
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 經過Scaffold能夠方便的生成一個Material風格的頁面
return Scaffold(
// 頂部導航欄
appBar: AppBar(
title: Text("主頁面"),
),
body: Center(
child: RaisedButton(
child: Text("我是第一個界面,點擊我進入第二個界面"),
onPressed: () {
print("我是第一個界面,點擊我進入第二個界面");
// 跳轉到第二個界面
Navigator.pushNamed(context, '/secondPage');
},
),
),
);
}
}
複製代碼
第二個頁面提取以後的代碼。ui
import 'package:flutter/material.dart';
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 經過Scaffold能夠方便的生成一個Material風格的頁面
return Scaffold(
// 頂部導航欄
appBar: AppBar(
title: Text("第二個界面"),
),
body: Center(
child: RaisedButton(
child: Text("我是第二個界面,點擊我進入第二個界面"),
onPressed: () {
print("我是第二個界面,點擊我返回到第一個界面");
// 返回上一個界面
Navigator.pop(context);
},
),
),
);
}
}
複製代碼
對於命名路由的跳轉,經過Navigator.pushNamed方法調用,經過Navigator.pop方法返回上一級界面。
動態路由不須要顯示聲明,直接經過代碼實現。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 經常使用固定寫法,生成Material風格的App
return MaterialApp(
title: "路由使用",
theme: ThemeData(
// 默認爲亮色主題,能夠設置[Brightness.dark]變成黑暗模式
brightness: Brightness.light,
),
home: HomePage(),
);
}
}
複製代碼
在HomePage界面經過調用Navigator.push方法實現跳轉。第二個頁面的返回邏輯不變。
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
));
複製代碼
有時候咱們不只須要跳轉到對應界面,還須要傳遞一些參數給下一個界面,同時下一個界面返回時,把某些參數再次傳遞給該界面。 咱們修改SecondPage組件的構造方法,爲了接收須要傳遞的參數。這時候咱們第二個頁面結構以下:
class SecondPage extends StatelessWidget {
// 定義一個須要變量, 接收傳遞的參數
final String title;
// 爲title設置一個默認參數,這樣的跳轉該界面時能夠不傳值。
SecondPage({Key key, this.title = "第二個界面"});
@override
Widget build(BuildContext context) {
// 經過Scaffold能夠方便的生成一個Material風格的頁面
return Scaffold(
// 頂部導航欄
appBar: AppBar(
title: Text(title),
),
body: Center(
child: RaisedButton(
child: Text("我是第二個界面,點擊我進入第二個界面"),
onPressed: () {
print("我是第二個界面,點擊我返回到第一個界面");
// 返回上一個界面
Navigator.pop(context);
},
),
),
);
}
}
複製代碼
第一個界面跳轉的地方代碼是這樣的。
Navigator.push(
context,
MaterialPageRoute(
// 傳遞title爲SecondPage,跳轉到第二個界面就會把標題設置爲SecondPage
builder: (context) => SecondPage(title: "SecondPage"),
));
},
複製代碼
說完了從第一個頁面往第二個頁面傳遞了參數,若是第二個頁面返回時傳遞一句話,而後第一個頁面接收到這句話而後打印出來, 代碼修改以下:
// HomePage頁面代碼
Navigator.push(
context,
MaterialPageRoute(
// 傳遞title爲SecondPage,跳轉到第二個界面就會把標題設置爲SecondPage
builder: (context) => SecondPage(title: "SecondPage"),
// 調用then等待接收返回數據
)).then((value) => print(value));
// SecondPage頁面代碼
Navigator.pop(context, "返回傳遞數據");
複製代碼
講完了動態路由及動態路由傳參以後,咱們來說一下靜態路由傳參,參數的傳遞方式是flutter爲咱們定義好的,咱們只須要把固定 代碼拷貝回來,稍微修改便可。爲了更具備廣泛性,咱們再定義一個頁面ThirdPage。
在咱們顯示聲明瞭routes以後,還須要在MaterialApp組件內添加onGenerateRoute屬性內容進行參數傳遞的處理。 完整代碼以下:
import 'package:flutter/material.dart';
import 'pages/HomePage.dart';
import 'pages/SecondPage.dart';
import 'pages/ThirdPage.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// 聲明全部的頁面
final routes = {
'/': (context, {arguments}) => HomePage(),
'/secondPage': (context, {arguments}) => SecondPage(),
'/thirdPage': (context, {arguments}) => ThirdPage(arguments: arguments),
};
@override
Widget build(BuildContext context) {
// 經常使用固定寫法,生成Material風格的App
return MaterialApp(
title: "路由使用",
theme: ThemeData(
// 默認爲亮色主題,能夠設置[Brightness.dark]變成黑暗模式
brightness: Brightness.light,
),
// home: HomePage(),
initialRoute: '/', // 默認界面
// 當頁面跳轉時進行參數處理
onGenerateRoute: (RouteSettings settings) {
// 獲取聲明的路由頁面函數
var pageBuilder = routes[settings.name];
if (pageBuilder != null) {
if (settings.arguments != null) {
// 建立路由頁面並攜帶參數
return MaterialPageRoute(
builder: (context) =>
pageBuilder(context, arguments: settings.arguments));
} else {
return MaterialPageRoute(
builder: (context) => pageBuilder(context));
}
}
return MaterialPageRoute(builder: (context) => HomePage());
},
);
}
}
複製代碼
第二個頁面傳遞參數時使用Navigator.pushNamed方法,具體代碼以下:
import 'package:flutter/material.dart';
class SecondPage extends StatelessWidget {
// 定義一個須要變量, 接收傳遞的參數
final String title;
// 爲title設置一個默認參數,這樣的跳轉該界面時能夠不傳值。
SecondPage({Key key, this.title = "第二個界面"});
@override
Widget build(BuildContext context) {
// 經過Scaffold能夠方便的生成一個Material風格的頁面
return Scaffold(
// 頂部導航欄
appBar: AppBar(
title: Text(title),
),
body: Center(
child: RaisedButton(
child: Text("我是第二個界面,點擊我進入第二個界面"),
onPressed: () {
print("我是第二個界面,點擊我進入第三個界面");
// 經過arguments指定參數
Navigator.pushNamed(context, "/thirdPage",
arguments: {'title': "命令路由傳遞過來的title"});
},
),
),
);
}
}
複製代碼
第三個頁面獲取參數,完整代碼以下:
import 'package:flutter/material.dart';
class ThirdPage extends StatelessWidget {
final Map arguments;
// 爲title設置一個默認參數,這樣的跳轉該界面時能夠不傳值。
ThirdPage({Key key, this.arguments});
@override
Widget build(BuildContext context) {
// 經過Scaffold能夠方便的生成一個Material風格的頁面
return Scaffold(
// 頂部導航欄
appBar: AppBar(
title: Text("${arguments != null ? arguments['title'] : "ThirdPage"}"),
),
body: Center(
child: RaisedButton(
child: Text("我是第三個界面,點擊我進入第二個界面"),
onPressed: () {
print("我是第三個界面,點擊我返回到第二個界面");
// 返回上一個界面
Navigator.pop(context, "返回傳遞數據Page3");
},
),
),
);
}
}
複製代碼
上面咱們已經實現了參數的傳遞,可是routes頁面列表和onGenerateRoute比較固定,咱們可以把這兩個單獨提取出來成爲 一個單獨的類,這樣後期再建立頁面或者維護的時候只須要修改這一個類就好了。
咱們新建一個PageConstants類,進行提取,修改後的代碼以下:
import 'package:flutter/material.dart';
// 引入頁面路徑
import '../pages/HomePage.dart';
import '../pages/SecondPage.dart';
import '../pages/ThirdPage.dart';
// 聲明全部頁面
final routes = {
'/': (context, {arguments}) => HomePage(),
'/secondPage': (context, {arguments}) => SecondPage(),
'/thirdPage': (context, {arguments}) => ThirdPage(arguments: arguments),
};
// 處理參數傳遞
// ignore: top_level_function_literal_block
var onGenerateRoute = (RouteSettings settings) {
// 獲取聲明的路由頁面函數
var pageBuilder = routes[settings.name];
if (pageBuilder != null) {
if (settings.arguments != null) {
// 建立路由頁面並攜帶參數
return MaterialPageRoute(
builder: (context) =>
pageBuilder(context, arguments: settings.arguments));
} else {
return MaterialPageRoute(
builder: (context) => pageBuilder(context));
}
}
return MaterialPageRoute(builder: (context) => HomePage());
};
複製代碼
這時候咱們只須要簡單修改MyApp組件便可:
import 'package:flutter/material.dart';
import 'package:hello_flutter/pages/PageConstants.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 經常使用固定寫法,生成Material風格的App
return MaterialApp(
title: "路由使用",
theme: ThemeData(
// 默認爲亮色主題,能夠設置[Brightness.dark]變成黑暗模式
brightness: Brightness.light,
),
initialRoute: '/', // 默認界面
// 經過PageConstants引入
onGenerateRoute: onGenerateRoute,
);
}
}
複製代碼
這樣來看就會清爽不少。
篇幅所限,此次的內容就先講到這裏,下篇文章繼續講日後的內容,應該會單獨講一講實現仿閒魚底部tab頁面切換和仿頭條多tab頁切換。
爲了第一時間獲取最新文章,請關注公衆號 -- 程序員指北,每個關注都能讓做者多搬一塊磚。