Flutter?頁面跳轉和傳值的實現(xiàn)
一、頁面跳轉
1.基本頁面跳轉
Navigator 介紹
在 Flutter 中,Navigator 是一個管理應用視圖(頁面)的組件,它使用棧(Stack)的方式來控制頁面的切換。每當你跳轉到一個新頁面時,Navigator 會將新頁面的 Route 壓棧(push),當你返回到之前的頁面時,它會將當前頁面的 Route 出棧(pop)。
為了使用 Navigator 進行頁面跳轉,我們需要使用 BuildContext,它表示當前 widget 在 widget 樹中的位置。BuildContext 是用于與 Navigator 進行交互的必要參數。
Navigator.push 方法
Navigator.push 方法用于將新的 Route 壓入棧中,從而導航到新頁面。
Navigator.push( context, MaterialPageRoute(builder: (context) => NewPage()), );
或這種寫法
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return NewPage();
},
));Navigator.pop 方法
Navigator.pop 方法用于將棧頂的 Route 彈出,返回到前一個頁面。
Navigator.pop(context);
MaterialPageRoute 和頁面跳轉動畫
MaterialPageRoute 是一種模態(tài)路由,它會根據目標平臺的規(guī)范,為頁面切換提供適當的動畫。在 Android 上,它通常是一個從屏幕底部向上滑入的動畫,而在 iOS 上,它通常是一個從屏幕右側滑入的動畫。
無參數頁面跳轉示例代碼
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Navigation Basics',
home: HomePage(),
));
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: ElevatedButton(
child: Text('Open New Page'),
onPressed: () {
// 使用 Navigator.push 方法來跳轉到新頁面
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewPage()),
);
},
),
),
);
}
}
class NewPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('New Page'),
),
body: Column(
children: [
Text('Welcome to the new page!'),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("pop"))
],
),
);
}
}2.命名路由和路由表
命名路由介紹
命名路由是一種用于管理頁面導航的技術,它允許你為每個頁面分配一個唯一的名稱,并通過這些名稱在應用程序中進行頁面之間的導航。命名路由,由一對字符串(路由名稱)和對應的屏幕(或稱為頁面/視圖)組成。
命名路由的好處
- 提高代碼可維護性:命名路由使得路由和它們對應的屏幕解耦,這讓查找和修改特定路由相關的代碼變得更加容易。
- 簡化路由管理:當應用的結構變得更為復雜時,使用命名路由可以幫助集中管理路由,而不是在代碼中散布大量的
Navigator.push和MaterialPageRoute。
配置命名路由
我們可以在 MaterialApp 的 routes 屬性中定義所有的命名路由。routes 是一個 Map,它的鍵是字符串(路由的名稱),而值是對應的構造器函數,返回相應的頁面 Widget。
MaterialApp(
title: 'Navigation with Named Routes',
// 初始路由,應用啟動時加載的路由
initialRoute: '/',
// 定義命名路由
routes: {
'/': (context) => HomePage(),
'/newPage': (context) => NewPage(),
'/thirdPage': (context) => ThirdPage(),
},
)
Navigator.pushNamed 方法
要使用命名路由進行頁面跳轉,可以調用 Navigator.pushNamed 方法,并傳入對應的路由名稱。
Navigator.pushNamed(context, '/newPage');
Navigator.pop 方法
Navigator.pop 方法用于將棧頂的 Route 彈出,返回到前一個頁面。
Navigator.pop(context);
Navigator.popAndPushNamed 方法
Navigator.popAndPushNamed 方法用于從當前頁面返回到上一個頁面,并立即導航到指定的命名路由。
該方法的作用是先執(zhí)行 Navigator.pop 方法返回到上一個頁面,然后立即執(zhí)行 Navigator.pushNamed 方法導航到新的命名路由。
Navigator.popAndPushNamed(context, '/thirdPage');
配置和使用命名路由示例代碼
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Navigation with Named Routes',
// 初始路由,應用啟動時加載的路由
initialRoute: '/',
// 定義命名路由
routes: {
'/': (context) => HomePage(),
'/newPage': (context) => NewPage(),
'/thirdPage': (context) => ThirdPage(),
},
));
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: ElevatedButton(
child: Text('Open New Page'),
// 使用命名路由進行頁面跳轉
onPressed: () {
Navigator.pushNamed(context, '/newPage');
},
),
),
);
}
}
class NewPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('New Page'),
),
body: Column(
children: [
Text('Welcome to the new page!'),
TextButton(
onPressed: () {
Navigator.popAndPushNamed(context, '/thirdPage');
},
child: Text("popAndPushNamed"))
],
),
);
}
}
class ThirdPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Third Page'),
),
body: Column(
children: [
Text('Welcome to the Third page!'),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("pop"))
],
),
);
}
}二、頁面?zhèn)髦?/h2>
1.push時向新頁面?zhèn)鬟f數據
(1).通過構造函數傳遞數據
最直接的方式是通過目標頁面的構造函數直接傳遞數據。
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: HomePage(),
));
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: ElevatedButton(
child: Text('Pass Data to New Page'),
onPressed: () {
// 通過構造函數直接傳遞數據
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewPage(data: 'Hello from Home Page!'),
),
);
},
),
),
);
}
}
class NewPage extends StatelessWidget {
final String data;
// 接收數據的構造函數
NewPage({required this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('New Page'),
),
body: Center(
child: Text(data), // 顯示傳遞過來的數據
),
);
}
}(2).使用 MaterialPageRoute 的 arguments 屬性
另一種傳遞數據的方式是使用 MaterialPageRoute 的 arguments 屬性,這在使用命名路由時尤其有用。
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
// 初始路由,應用啟動時加載的路由
initialRoute: '/',
// 定義命名路由
routes: {
'/': (context) => HomePage(),
'/newPage': (context) => NewPage(),
},
));
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: ElevatedButton(
child: Text('Pass Data to New Page'),
onPressed: () {
Navigator.pushNamed(
context,
'/newPage',
arguments: 'Hello from Home Page!',
);
},
),
),
);
}
}
class NewPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 獲取傳遞過來的數據
final String data = ModalRoute.of(context)!.settings.arguments as String;
return Scaffold(
appBar: AppBar(
title: Text('New Page'),
),
body: Center(
child: Text(data), // 顯示傳遞過來的數據
),
);
}
}2.pop時返回數據給前一個頁面
使用 Navigator.pop 返回結果
當從一個頁面返回到前一個頁面時,可以通過 Navigator.pop 方法返回數據:
// 假設這是 NewPage 中的一個按鈕,當點擊時返回數據給前一個頁面
ElevatedButton(
onPressed: () {
Navigator.pop(context, 'Result from New Page');
},
child: Text('Return Data to Home Page'),
),Navigator.push 和 await 結合使用
你可以使用 await 關鍵字等待一個頁面返回結果:
// ... HomePage 中的按鈕點擊事件
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewPage()),
);
// 使用 ScaffoldMessenger 顯示返回的結果
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(result?.toString() ?? 'No result')),
);
},或
onPressed: () async {
final result = await Navigator.pushNamed(
context,
'/newPage',
arguments: 'Hello from Home Page!',
);
// 使用 ScaffoldMessenger 顯示返回的結果
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(result?.toString() ?? 'No result')),
);
},
),使用 PopScope 攔截系統(tǒng)返回按鈕的行為
如果你不顯式的調用Navigator.pop(context, 'xxx'),就拿不到回傳結果。比如你從系統(tǒng)導航上點擊返回按鈕,就沒數據傳遞回去。
如果一定任何返回都回傳值,就需要定義導航欄,或者通過使用 PopScope widget 來攔截系統(tǒng)返回按鈕的行為,并執(zhí)行自定義的操作。
class NewPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('New Page'),
),
body: PopScope(
canPop: false, // 使用canPop提前禁用pop
onPopInvoked: (bool didPop) {
// canPop被設置為false時。didPop參數表示返回導航是否成功。
// `didPop`參數會告訴你路由是否成功地pop出
if (!didPop) {
// 在這里執(zhí)行你的操作,比如返回數據. 前面不禁用pop的話,這里就會pop兩次了。
Navigator.pop(context, 'Custom back button result');
}
},
child: Column(
children: [
TextButton(
onPressed: () {
Navigator.pop(context, 'Result from New Page');
},
child: Text("pop"))
],
),
),
);
}
}注意:這里用多個地方調用Navigator.pop,從不同地方返回時,回傳的值也會不同。如果要求回傳的數據一致,就將Navigator.pop方法抽離放到一個方法中,多個返回位置調用同一個方法回傳同樣的數據。
三、路由生成鉤子(onGenerateRoute)
在Flutter中,onGenerateRoute是一個非常強大的鉤子,允許開發(fā)者對路由進行自定義操作。它在MaterialApp或CupertinoApp中定義,并在導航到命名路由時被調用,特別是當使用Navigator.pushNamed時。它可以用于動態(tài)生成路由,傳遞參數到新頁面,甚至處理未知的路由。
下面是一個使用onGenerateRoute的示例,其中包括了處理動態(tài)路由和傳遞參數到未知頁面的代碼:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// 應用初始路由
initialRoute: '/',
// onGenerateRoute 用于處理動態(tài)路由
onGenerateRoute: (RouteSettings settings) {
// 獲取傳遞過來的參數,如果參數為null,則提供一個空的Map
final arguments = settings.arguments as Map<String, dynamic>? ?? {};
// 根據 settings.name 處理不同路由
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (context) => HomePage());
case '/details':
// 假設 DetailsPage 接受一個 'data' 參數
final String data = arguments['data'] as String? ?? '默認值';
return MaterialPageRoute(builder: (context) => DetailsPage(data: data));
default:
// 如果沒有匹配的路由,返回到一個未知頁面路由
return MaterialPageRoute(builder: (context) => UnknownPage());
}
},
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _data = "缺省值";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('首頁'),
),
body: Column(
children: [
Text(_data), // 顯示傳遞到本頁面的數據
ElevatedButton(
onPressed: () async {
// 導航到詳情頁,并傳遞數據。同時,使用await等待詳情頁返回的結果
final result = await Navigator.pushNamed(
context,
'/details',
arguments: {'data': '這是一個秘密信息!'},
);
final arguments = result as Map<String, dynamic>? ?? {};
setState(() {
if (mounted) {
_data = arguments["data"] as String? ?? "";
}
});
},
child: Text('前往詳情頁'),
),
],
),
);
}
}
class DetailsPage extends StatelessWidget {
final String data;
DetailsPage({required this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('詳情頁'),
),
body: Column(
children: [
Text(data), // 顯示傳遞到本頁面的數據
TextButton(
onPressed: () {
// 回傳數據數據
Navigator.pop(context, {'data': '詳情返回數據'});
},
child: Text("pop"))
],
),
);
}
}
class UnknownPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('未知頁面'),
),
body: Center(
child: Text('該路由名稱不存在。'),
),
);
}
}四、路由傳值的安全性
1.驗證傳入數據
當通過路由傳遞數據時,重要的是要驗證接收的數據是否符合預期。你可以使用類型檢查、正則表達式或自定義驗證函數來確保數據的有效性和安全性。
bool isValidData(dynamic data) {
// 在這里添加驗證邏輯,例如類型檢查、內容檢查等
return data is String && data.isNotEmpty;
}在使用數據之前,你可以調用這個函數來驗證:
if (isValidData(receivedData)) {
// 數據有效,可以安全使用
} else {
// 數據無效,可以拋出異?;蜻M行錯誤處理
}2.處理空值和異常
處理空值和異常是確保應用程序穩(wěn)定性的重要部分。當你從路由接收數據時,應該始終假設這些數據可能為空或者不是預期的格式。下面是一些處理這些情況的策略:
處理空值
當你期望的數據可能為空時,可以使用Dart的null-aware運算符來優(yōu)雅地處理:
String data = receivedData ?? '默認值';
或者在使用之前檢查數據是否為null:
if (receivedData != null) {
// 使用 receivedData
} else {
// 處理空值情況,例如返回錯誤提示或設置默認值
}異常處理
如果數據轉換或驗證過程中可能拋出異常,你應該使用try-catch語句來捕獲這些異常:
try {
// 嘗試使用 receivedData
} catch (e) {
// 處理異常,例如記錄日志、顯示錯誤信息等
}五、使用 Provider 管理跨頁面的狀態(tài)
Provider 是一個流行的狀態(tài)管理庫,它依賴于 Flutter 的 InheritedWidget 來向下傳遞數據。它能夠讓你在 widget 樹中跨越多個層級來傳遞和修改數據,而無需手動傳遞回調或數據。
使用 Provider,你可以在應用的頂層提供一個狀態(tài),然后在應用的任何其他部分訪問或修改這個狀態(tài)。這適用于跨多個頁面?zhèn)鬟f數據,甚至是整個應用的狀態(tài)管理。
詳細使用參見另一文http://www.dbjr.com.cn/program/319047n8q.htm
1.使用 Provider 進行狀態(tài)管理和傳值
首先,你需要在 pubspec.yaml 文件中添加 provider 依賴項:
dependencies:
flutter:
sdk: flutter
provider: ^6.1.2 # 使用適合你的版本然后,在應用頂層(即要包裹住MaterialApp)引入 Provider:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
// 通過 MultiProvider 可以提供多個對象
MultiProvider(
providers: [
// ChangeNotifierProvider 是 Provider 的一種,它可以響應通知
ChangeNotifierProvider(create: (context) => DataProvider()),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: HomePage(),
);
}
}// 定義一個繼承自 ChangeNotifier 的數據模型,用來傳遞和響應變化
class DataProvider extends ChangeNotifier {
String _data = "初始數據";
String get data => _data;
void setData(String newData) {
_data = newData;
notifyListeners(); // 當更新數據時,通知監(jiān)聽的 widgets 進行重建
}
}HomePage 中的按鈕點擊時,可以使用 Provider 來更新數據:
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 使用 Provider.of 來獲取最近的 DataProvider 實例
final dataProvider = Provider.of<DataProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text('首頁'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
// 更新數據
dataProvider.setData('更新的數據');
// 導航到詳情頁
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailsPage()),
);
},
child: Text('前往詳情頁并傳遞數據'),
),
),
);
}
}
class DetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 監(jiān)聽 DataProvider,當數據變化時重建這個 widget
final data = Provider.of<DataProvider>(context).data;
return Scaffold(
appBar: AppBar(
title: Text('詳情頁'),
),
body: Center(
// 顯示從 Provider 獲取的數據
child: Text(data),
),
);
}
}2.完整的Provider例子
下面是一個使用Provider進行狀態(tài)管理和跨頁面?zhèn)髦档耐暾纠?,包括異常處理和空值檢查:
首先,確保已經添加了provider依賴。
// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<DataModel>(
create: (_) => DataModel(),
child: MaterialApp(
title: 'Flutter Demo',
home: HomePage(),
),
);
}
}
class DataModel extends ChangeNotifier {
String _data = '';
String get data => _data;
void updateData(String newData) {
if (newData.isNotEmpty) {
_data = newData;
notifyListeners();
} else {
throw Exception('Data cannot be empty');
}
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: Consumer<DataModel>(
builder: (context, dataModel, child) {
return ElevatedButton(
onPressed: () {
try {
dataModel.updateData('New Data from Home');
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailsPage()),
);
} catch (e) {
// Handle the exception
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
}
},
child: Text('Go to Details'),
);
},
),
),
);
}
}
class DetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Details')),
body: Center(
child: Consumer<DataModel>(
builder: (context, dataModel, child) {
return Text(dataModel.data);
},
),
),
);
}
}在這個例子中,DataModel 是一個簡單的數據持有類,它通過Provider允許在應用程序的其他部分訪問和修改數據。HomePage 設置新的數據,并導航到DetailsPage。DetailsPage 顯示當前的數據。如果嘗試設置空的數據,DataModel 將拋出一個異常,該異常在HomePage中被捕獲并顯示為一個SnackBar。
到此這篇關于Flutter 頁面跳轉和傳值的實現(xiàn)的文章就介紹到這了,更多相關Flutter 頁面跳轉和傳值內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android?App應用退到后臺顯示通知的實現(xiàn)方法
當用戶收到app發(fā)過來的消息時,如果app沒有在前臺打開,需要提醒用戶有新的消息,所以這篇文章主要給大家介紹了關于Android?App應用退到后臺顯示通知的實現(xiàn)方法,需要的朋友可以參考下2022-01-01
Android實現(xiàn)屏幕旋轉四個方向準確監(jiān)聽
這篇文章主要為大家詳細介紹了Android實現(xiàn)屏幕旋轉四個方向準確監(jiān)聽,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07
Android應用中使用ViewPager和ViewPager指示器來制作Tab標簽
這篇文章主要介紹了Android中使用ViewPager和ViewPager指示器來制作Tab標簽的方法,ViewPager指示器ViewPageIndicator是一個開源庫,文中舉了一個仿網易新聞客戶端Tab標簽的例子,需要的朋友可以參考下2016-03-03
android判斷phonegap是否聯(lián)網且加載super.loadUrl網址
android判斷phonegap是否聯(lián)網動態(tài)加載super.loadUrl網址,接下來本文所提供的知識會幫助你解決以上問題,感興趣的你可不要錯過了哈2013-02-02
使用AccessibilityService實現(xiàn)微信自動切換賬號功能
這篇文章主要為大家詳細介紹了使用AccessibilityService實現(xiàn)微信自動切換賬號功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12
Android GridView 滑動條設置一直顯示狀態(tài)(推薦)
這篇文章主要介紹了Android GridView 滑動條設置一直顯示狀態(tài)的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-12-12
Android ViewPager實現(xiàn)動畫切換效果
這篇文章主要為大家詳細介紹了Android ViewPager實現(xiàn)動畫切換效果的相關資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01

