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