轉載請註明出處: learnandfish.com/
概述
每個應用都有很多個頁面,在flutter中同樣也有很多頁面,被稱之為路由(Router),頁面之間的跳轉通過導航器(Navigator)進行管理。 其中 Navigator.push 和 Navigator.pop 是最簡單的跳轉到新頁面和返回到上一級介面的方式。
路由分為靜態路由(即命名路由)和動態路由。頁面之間跳轉時往往需要傳遞引數,這稱之為路由傳值。下面我們會一一帶領大家學習。
通過本篇文章的學習我們的目標是熟練掌握路由及傳值,以後進行應用開發時對頁面跳轉方面不再疑惑。
靜態路由(即命名路由)
flutter中萬物皆widget,我們的頁面(route)也是widget的子類,所以我們定義一個介面也是通過繼承widget實現。 前面的部落格我們已經定義過介面了,比如計數器例項,就是一個簡單的頁面,也就是一個路由。下面我們來詳細實現一個介面。 首先我需要一個入口函式,這個相信大家已經很熟悉了,就是在main方法中呼叫runApp函式進入應用,我們就不做詳細介紹了,直接給出程式碼。
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重寫自己想要的樣式即可。實現了頁面就要開始跳轉邏輯。
靜態路由即命名路由,在通過Navigator進行跳轉之前,需要在MaterialApp元件內顯式宣告路由的名稱,一旦宣告,路由的跳轉 方式就固定了,所以稱之為靜態路由,有唯一的名稱所以也稱之為命令路由。顯式宣告路由通過在MaterialApp內的routes屬性進行定義。
如果我們有很多個頁面和很多個其他型別的元件都放在lib下,對於後期維護簡直是一大折磨,所以分包是大多數平臺的常規操作, 就是對有同一種特性的東西放置在同一個包下,比如頁面類的元件都放在pages包,工具類的元件放在utils包下等。 接下來我們就新建一個pages包,把第二個介面SecondPage放進去,把第一個介面HomePage也提取出來放到這個包下。
我們分為一下三步進行靜態路由的跳轉:
- 首先在lib目錄右鍵新建pages包,接著在pages包下新建SecondPage.dart檔案,然後把HomePage提取到pages下,成為單獨的類。
- 在RouteDemo類中的MaterialApp內宣告routes屬性,為了顯示宣告路由的名稱。
- 使用Navigator進行頁面的跳轉和返回。
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');
},
),
),
);
}
}
複製程式碼
第二個頁面提取之後的程式碼。
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頁切換。
為了第一時間獲取最新文章,請關注公眾號 -- 程式設計師指北,每一個關注都能讓作者多搬一塊磚。