推開程序員的大門時,前面展現的是無止境的學習旅途。
本次是Flutter開發列車,大家上車后記得系好安全帶。
原文地址:https://juejin.cn/user/2840793776393847
前言
使用Bloc的時候,有一個讓我至今為止十分在意的問題,無法真正的跨頁面交互!在反復的查閱官方文檔后,使用一個全局Bloc的方式,實現了“偽”跨頁面交互,詳細可查看:flutter_bloc使用解析;fish_redux的廣播機制是可以比較完美的實現跨頁面交互的,我也寫了一篇近萬字介紹如何使用該框架:fish_redux使用詳解,對於中小型項目使用fish_redux,這會一定程度上降低開發效率,最近嘗試了GetX相關功能,解決了我的相當一部分痛點。
把整篇文章寫完后,我馬上把自己的一個demo里面所有Bloc代碼全用GetX替換,且去掉了Fluro框架;感覺用Getx雖然會省掉大量的模板代碼,但還是有些重復工作:創建文件夾,創建幾個必備文件,寫那些必須要寫的初始化代碼和類;略微繁瑣,為了對得起GetX給我開發帶來的巨大便利,我就花了一些時間,給它寫了一個插件! 上面這重復的代碼,文件,文件夾統統能一鍵生成!
GetX相關優勢
build刷新方法極度簡單!
-
getx:Obx(() => Text())
-
這是我非常非常在意的一個方面,因為bloc的build刷新組件方法要傳倆個泛型,加上build方法里面的倆個參數,導致一個build方法如果不使用箭頭方法簡寫,幾乎占四五行,用起來實在蛋筒,導致我平時開發直接把BlocBuilder方法直接寫在頁面頂層(不提倡寫頂層),一個頁面只用寫一次了,不用定點到處寫BlocBuilder了,手動滑稽.jpg
跨頁面交互
這絕對是GetX的一個優點!對於復雜的生產環境,跨頁面交互的場景,實在太常見了,GetX的跨頁面交互,幾乎和fish_redux一樣簡單,愛了愛了
路由管理
是的,getx內部實現了路由管理,而且用起來,那叫一個簡單!bloc沒實現路由管理,這讓我不得不去找一個star量高的路由管理框架,就選擇了fluro,但是讓我不得不說,這個fluro用起來真的叫一個折磨人,每次新建一個頁面,最讓我抗拒的就是去寫fluro路由代碼,橫跨幾個文件來回寫,真是肝疼。
GetX實現了動態路由傳參,也就是說直接在命名路由上拼參數,然后能拿到這些拼在路由上的參數,也就是說用flutter寫H5,直接能通過Url傳值(fluro也能做到),OMG!可以無腦舍棄復雜的fluro了。
實現了全局BuildContext
國際化,主題實現
上面單單是build簡寫的優勢,就會讓我考慮是否去使用了,而且還能實現跨頁面的功能,這還考慮啥,開搞!接下來將全面的介紹GetX的使用,文章也不分篇水閱讀量了,力求一文寫清楚,方便大家隨時查閱。
准備
引入
首先導入GetX的插件
# getx 狀態管理框架 https://pub.flutter-io.cn/packages/get
get: ^3.24.0
GetX地址Github:
Pub:
主入口配置
只需要將MaterialApp改成GetMaterialApp即可。
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: CounterGetPage(),
);
}
}
各模塊導包,均使用下面包即可。
import 'package:get/get.dart';
插件
吐槽下寫插件的過程,實際寫這種模板代碼生成插件,其實也不難,網上有很多人寫了范例,參考參考思路,能較快的整出來,就是有些配置比較蛋疼。
一開始選擇Plugin DevKit模式整的,都已經寫好,但是看官網文檔的時候,官方文檔開頭就說了:建議使用Gradle模式開發插件,又巴拉巴拉列了一堆好處;考慮良久,決定用Gradle模式重寫。
這個Gradle模式,最煩的還是開頭新建項目的時候,那個Gradle死活下載不下來,科學全局上網都不行,然后手動下載了Gradle,指定本地Gradle,開全局再次同步時,會下載一個較大的社區版IDEA,但是使用本地Gradle加載完,存在一個很大的BUG!main文件夾下,不會自動生成Java文件夾!
點擊其它的文件夾,右擊:New -> Plugin DevKit 居然不會沒有Action選項,差點把我勸退了,換了了七八個版本IDEA試了都不行!Action選項出不來,過了倆天后,晚上無意嘗試在main文件夾下面新建了一個Java文件,然后在這個java文件上右擊:New -> Plugin DevKit,Action選項出現了!還有個巨坑的問題,在Gradle模式下開發插件,把模板代碼文件放在main文件下、放在src下、放在根目錄下,都獲取不到文件里面的內容,這個真是坑了我不少時間,搜了很多博客,都發現沒寫這個問題,官方文檔范例看了幾遍也沒發現有啥說明,后來找到了一個三年前的項目,翻了翻代碼發現,所有的資源文件都必須放在resources文件夾下,才能讀取到文件內容。
說明
插件效果
-
看下插件使用的效果圖吧,樣式參考了fish_redux插件樣式
-
有一些可選擇的功能,所以做成多按鈕的樣式,大家可以按照自己的需求進行操作
說下插件的功能含義
-
Model:生成GetX的模式,
-
Default:默認模式,生成三個文件:state,logic,view
-
Easy:簡單模式,生成倆個文件:logic,view
-
Function:功能選擇
-
useFolder:使用文件,選擇后會生成文件夾,大駝峰命名自動轉換為:小寫+下划線
-
usePrefix:使用前綴,生成的文件前加上前綴,前綴為:大駝峰命名自動轉換為:小寫+下划線
-
Module Name:模塊的名稱,請使用大駝峰命名
安裝
- 在設置里面選擇:Plugins ---> 輸入“getx”搜索 ---> 選擇名字為:“GeX” ---> 然后安裝 ---> 最后記得點擊下“Apply”
- 如果在使用該插件的過程中有什么問題,請在該項目的github上給我提issue,我看到后,會盡快處理
計數器
效果圖
實現
首頁,當然是實現一個簡單的計數器,來看GetX怎么將邏輯層和界面層解耦的。來使用插件生成下簡單文件:
-
模式選擇:Easy
-
功能選擇:useFolder
來看下生成的默認代碼,默認代碼十分簡單,詳細解釋放在倆種狀態管理里。
logic
import 'package:get/get.dart';
class CounterGetLogic extends GetxController {
}
view
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'logic.dart';
class CounterGetPage extends StatelessWidget {
final CounterGetLogic logic = Get.put(CounterGetLogic());
@override
Widget build(BuildContext context) {
return Container();
}
}
響應式狀態管理
當數據源變化時,將自動執行刷新組件的方法。logic層因為是處理頁面邏輯的,加上Controller單詞過長,也防止和Flutter自帶的一些控件控制器弄混,所以該層用logic結尾,這里就定為了logic層,當然這點隨個人意向,寫Event,Controller均可。這里變量數值后寫.obs操作,是說明定義了該變量為響應式變量,當該變量數值變化時,頁面的刷新方法將自動刷新;基礎類型,List,類都可以加.obs,使其變成響應式變量
class CounterGetLogic extends GetxController {
var count = 0.obs;
///自增
void increase() => ++count;
}
view層
這地方獲取到Logic層的實例后,就可進行操作了,大家可能會想:WTF,為什么實例的操作放在build方法里?逗我呢?--------- 實際不然,stl是無狀態組件,說明了他就不會被二次重組,所以實例操作只會被執行一次,而且Obx()方法是可以刷新組件的,完美解決刷新組件問題了。
class CounterGetPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
CounterGetLogic logic = Get.put(CounterGetLogic());
return Scaffold(
appBar: AppBar(title: const Text('GetX計數器')),
body: Center(
child: Obx(
() => Text('點擊了 ${logic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
}
當然,也可以這樣寫。
class CounterGetPage extends StatelessWidget {
final CounterGetLogic logic = Get.put(CounterGetLogic());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('GetX計數器')),
body: Center(
child: Obx(
() => Text('點擊了 ${logic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
}
可以發現刷新組件的方法極其簡單:Obx(),這樣可以愉快的到處寫定點刷新操作了。
Obx()方法刷新的條件
只有當響應式變量的值發生變化時,才會會執行刷新操作,當某個變量初始值為:“test”,再賦值為:“test”,並不會執行刷新操作。
當你定義了一個響應式變量,該響應式變量改變時,包裹該響應式變量的Obx()方法才會執行刷新操作,其它的未包裹該響應式變量的Obx()方法並不會執行刷新操作,Cool!
來看下如果把整個類對象設置成響應類型,如何實現更新操作呢?
下面解釋來自官方README文檔。
這里嘗試了下,將整個類對象設置為響應類型,當你改變了類其中一個變量,然后執行更新操作,只要包裹了該響應類變量的Obx(),都會實行刷新操作,將整個類設置響應類型,需要結合實際場景使用。
// model
// 我們將使整個類成為可觀察的,而不是每個屬性。
class User() {
User({this.name = '', this.age = 0});
String name;
int age;
}
// controller
final user = User().obs;
//當你需要更新user變量時。
user.update( (user) { // 這個參數是你要更新的類本身。
user.name = 'Jonny';
user.age = 18;
});
// 更新user變量的另一種方式。
user(User(name: 'João', age: 35));
// view
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
// 你也可以不使用.value來訪問模型值。
user().name; // 注意是user變量,而不是類變量(首字母是小寫的)。
簡單狀態管理
GetBuilder:這是一個極其輕巧的狀態管理器,占用資源極少!
logic:先來看看logic層
class CounterEasyGetLogic extends GetxController {
var count = 0;
void increase() {
++count;
update();
}
}
view
class CounterEasyGetPage extends StatelessWidget {
final CounterEasyGetLogic logic = Get.put(CounterEasyGetLogic());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('計數器-簡單式')),
body: Center(
child: GetBuilder<CounterEasyGetLogic>(
builder: (logicGet) => Text(
'點擊了 ${logicGet.count} 次',
style: TextStyle(fontSize: 30.0),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
}
分析下:GetBuilder這個方法。
-
init:雖然上述代碼沒用到,但是,這個參數是存在在GetBuilder中的,因為在加載變量的時候就使用Get.put()生成了CounterEasyGetLogic對象,GetBuilder會自動查找該對象,所以,就可以不使用init參數
-
builder:方法參數,擁有一個入參,類型便是GetBuilder所傳入泛型的類型
-
initState,dispose等:GetBuilder擁有StatefulWidget所有周期回調,可以在相應回調內做一些操作
總結
分析
-
GetBuilder內部實際上是對StatefulWidget的封裝,所以占用資源極小
-
響應式變量,因為使用的是StreamBuilder,會消耗一定資源
使用場景
-
一般來說,對於大多數場景都是可以使用響應式變量的
-
但是,在一個包含了大量對象的List,都使用響應式變量,將生成大量的StreamBuilder,必將對內存造成較大的壓力,該情況下,就要考慮使用簡單狀態管理了
跨頁面交互
跨頁面交互,在復雜的場景中,是非常重要的功能,來看看GetX怎么實現跨頁面事件交互的。
實現
頁面一,常規代碼。
logic
這里的自增事件,是供其它頁面調用的,該頁面本身沒使用。
class JumpOneLogic extends GetxController {
var count = 0.obs;
///跳轉到跨頁面
void toJumpTwo() {
Get.toNamed(RouteConfig.jumpTwo, arguments: {'msg': '我是上個頁面傳遞過來的數據'});
}
///跳轉到跨頁面
void increase() => count++;
} </pre>
**view**此處就一個顯示文字和跳轉功能。
<pre style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;">class JumpOnePage extends StatelessWidget {
/// 使用Get.put()實例化你的類,使其對當下的所有子路由可用。
final JumpOneLogic logic = Get.put(JumpOneLogic());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text('跨頁面-One')),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.toJumpTwo(),
child: const Icon(Icons.arrow_forward_outlined),
),
body: Center(
child: Obx(
() => Text('跨頁面-Two點擊了 ${logic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
);
}
}
頁面二,這個頁面就是重點了。
logic
-
將演示怎么調用前一個頁面的事件
-
怎么接收上個頁面數據
-
請注意,GetxController包含比較完整的生命周期回調,可以在onInit()接受傳遞的數據;如果接收的數據需要刷新到界面上,請在onReady回調里面接收數據操作,onReady是在addPostFrameCallback回調中調用,刷新數據的操作在onReady進行,能保證界面是初始加載完畢后才進行頁面刷新操作的
class JumpTwoLogic extends GetxController {
var count = 0.obs;
var msg = ''.obs;
@override
void onReady() {
var map = Get.arguments;
msg.value = map['msg'];
super.onReady();
}
///跳轉到跨頁面
void increase() => count++;
}
view
-
加號的點擊事件,點擊時,能實現倆個頁面數據的變換
-
重點來了,這里通過Get.find(),獲取到了之前實例化GetXController,獲取某個模塊的GetXController后就很好做了,可以通過這個GetXController去調用相應的事件,也可以通過它,拿到該模塊的數據!
class JumpTwoPage extends StatelessWidget {
final JumpOneLogic oneLogic = Get.find();
final JumpTwoLogic twoLogic = Get.put(JumpTwoLogic());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text('跨頁面-Two')),
floatingActionButton: FloatingActionButton(
onPressed: () {
oneLogic.increase();
twoLogic.increase();
},
child: const Icon(Icons.add),
),
body: Center(
child: Column(mainAxisSize: MainAxisSize.min, children: [
//計數顯示
Obx(
() => Text('跨頁面-Two點擊了 ${twoLogic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
//傳遞數據
Obx(
() => Text('傳遞的數據:${twoLogic.msg.value}',
style: TextStyle(fontSize: 30.0)),
),
]),
),
);
}
}
總結
GetX這種的跨頁面交互事件,真的是非常簡單了,侵入性也非常的低,不需要在主入口配置什么,在復雜的業務場景下,這樣簡單的跨頁面交互方式,就能實現很多事了。
進階吧!計數器
我們可能會遇到過很多復雜的業務場景,在復雜的業務場景下,單單某個模塊關於變量的初始化操作可能就非常多,在這個時候,如果還將state(狀態層)和logic(邏輯層)寫在一起,維護起來可能看的比較暈,這里將狀態層和邏輯層進行一個拆分,這樣在稍微大一點的項目里使用GetX,也能保證結構足夠清晰了!在這里就繼續用計數器舉例吧!
實現
此處需要划分三個結構了:state(狀態層),logic(邏輯層),view(界面層)。這里使用插件生成下模板代碼:
-
Model:選擇Default(默認)
-
Function:useFolder(默認中)
來看下生成的模板代碼。state
class CounterHighGetState {
CounterHighGetState() {
///Initialize variables
}
}
logic
import 'package:get/get.dart';
import 'state.dart';
class CounterHighGetLogic extends GetxController {
final state = CounterHighGetState();
}
view
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'logic.dart';
import 'state.dart';
class CounterHighGetPage extends StatelessWidget {
final CounterHighGetLogic logic = Get.put(CounterHighGetLogic());
final CounterHighGetState state = Get.find<CounterHighGetLogic>().state;
@override
Widget build(BuildContext context) {
return Container();
}
}
為什么寫成這樣三個模塊,需要把State單獨提出來,請速速瀏覽下方。state這里可以發現,count類型使用的是RxInt,沒有使用var,使用該變量類型的原因,此處是將所有的操作都放在構造函數里面初始化,如果直接使用var沒有立馬賦值,是無法推導為Rx類型,所以這里直接定義為RxInt,實際很簡單,基礎類型將開頭字母大寫,然后加上Rx前綴即可。實際上直接使用var也是可以的,但是,使用該響應變量的時候.value無法提示,需要自己手寫,所以還是老老實實的寫明Rx具體類型吧。
class CounterHighGetState {
RxInt count;
CounterHighGetState() {
count = 0.obs;
}
}
logic
邏輯層就比較簡單,需要注意的是:開始時需要實例化狀態類。
class CounterHighGetLogic extends GetxController {
final state = CounterHighGetState();
///自增
void increase() => ++state.count;
}
view實際上view層,和之前的幾乎沒區別,區別的是把狀態層給獨立出來了。因為CounterHighGetLogic被實例化,所以直接使用Get.find
class CounterHighGetPage extends StatelessWidget {
final CounterHighGetLogic logic = Get.put(CounterHighGetLogic());
final CounterHighGetState state = Get.find<CounterHighGetLogic>().state;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('計數器-響應式')),
body: Center(
child: Obx(
() => Text('點擊了 ${state.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
}
看了上面的改造,屏幕前的你可能想吐槽了:坑比啊,之前簡簡單單的邏輯層,被拆成倆個,還搞得這么麻煩,你是猴子請來的逗比嗎?大家先別急着吐槽,當業務過於復雜,state層,也是會維護很多東西的,讓我們看看下面的一個小栗子,下面實例代碼是不能直接運行的,想看詳細運行代碼,請查看項目:flutter_use項目地址:
state
class MainState {
///選擇index - 響應式
RxInt selectedIndex;
///控制是否展開 - 響應式
RxBool isUnfold;
///分類按鈕數據源
List<BtnInfo> list;
///Navigation的item信息
List<BtnInfo> itemList;
///PageView頁面
List<Widget> pageList;
PageController pageController;
MainState() {
//初始化index
selectedIndex = 0.obs;
//默認不展開
isUnfold = false.obs;
//PageView頁面
pageList = [
keepAliveWrapper(FunctionPage()),
keepAliveWrapper(ExamplePage()),
keepAliveWrapper(Center(child: Container())),
];
//item欄目
itemList = [
BtnInfo(
title: "功能",
icon: Icon(Icons.bubble_chart),
),
BtnInfo(
title: "范例",
icon: Icon(Icons.opacity),
),
BtnInfo(
title: "設置",
icon: Icon(Icons.settings),
),
];
//頁面控制器
pageController = PageController();
}
}
logic
class MainLogic extends GetxController {
final state = MainState();
///切換tab
void switchTap(int index) {
state.selectedIndex.value = index;
}
///是否展開側邊欄
void onUnfold(bool unfold) {
state.isUnfold.value = !state.isUnfold.value;
}
} </pre>
**view**
<pre style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;">class MainPage extends StatelessWidget {
final MainLogic logic = Get.put(MainLogic());
final MainState state = Get.find<MainLogic>().state;
@override
Widget build(BuildContext context) {
return BaseScaffold(
backgroundColor: Colors.white,
body: Row(children: [
///側邊欄區域
Obx(
() => SideNavigation(
selectedIndex: state.selectedIndex.value,
sideItems: state.itemList,
onItem: (index) {
logic.switchTap(index);
state.pageController.jumpToPage(index);
},
isUnfold: state.isUnfold.value,
onUnfold: (unfold) {
logic.onUnfold(unfold);
},
),
),
///Expanded占滿剩下的空間
Expanded(
child: PageView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: state.pageList.length,
itemBuilder: (context, index) => state.pageList[index],
controller: state.pageController,
),
)
]),
);
}
}
從上面可以看出,state層里面的狀態已經較多了,當某些模塊涉及到大量的:提交表單數據,跳轉數據,展示數據等等,state層的代碼會相當的多,相信我,真的是非常多,一旦業務發生變更,還要經常維護修改,就蛋痛了。在復雜的業務下,將狀態層(state)和業務邏輯層(logic)分開,絕對是個明智的舉動。
最后
簡單的業務模塊,可以使用倆層結構:logic,view;復雜的業務模塊,推薦使用三層結構:state,logic,view。
路由管理
GetX實現了一套用起來十分簡單的路由管理,可以使用一種極其簡單的方式導航,也可以使用命名路由導航。
簡單路由
主入口配置
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: MainPage(),
);
}
} </pre>
路由的相關使用使用是非常簡單,使用Get.to()之類api即可,此處簡單演示,詳細api說明,放在本節結尾。
<pre style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;">//跳轉新頁面
Get.to(SomePage());
命名路由導航
這里是推薦使用命名路由導航的方式。
-
統一管理起了所有頁面
-
在app中可能感受不到,但是在web端,加載頁面的url地址就是命名路由你所設置字符串,也就是說,在web中,可以直接通過url導航到相關頁面
下面說明下,如何使用。首先,在主入口出配置下
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
initialRoute: RouteConfig.main,
getPages: RouteConfig.getPages,
);
}
}
RouteConfig類下面是我的相關頁面,和其映射的頁面,請根據自己的頁面進行相關編寫。
class RouteConfig{
///主頁面
static final String main = "/";
///dialog頁面
static final String dialog = "/dialog";
///bloc計數器模塊
static final String counter = "/counter";
///測試布局頁面
static final String testLayout = "/testLayout";
///演示SmartDialog控件
static final String smartDialog = "/smartDialog";
///Bloc跨頁面傳遞事件
static final String spanOne = "/spanOne";
static final String spanTwo = "/spanOne/spanTwo";
///GetX 計數器 跨頁面交互
static final String counterGet = "/counterGet";
static final String jumpOne = "/jumpOne";
static final String jumpTwo = "/jumpOne/jumpTwo";
///別名映射頁面
static final List<GetPage> getPages = [
GetPage(name: main, page: () => MainPage()),
GetPage(name: dialog, page: () => Dialog()),
GetPage(name: counter, page: () => CounterPage()),
GetPage(name: testLayout, page: () => TestLayoutPage()),
GetPage(name: smartDialog, page: () => SmartDialogPage()),
GetPage(name: spanOne, page: () => SpanOnePage()),
GetPage(name: spanTwo, page: () => SpanTwoPage()),
GetPage(name: counterGet, page: () => CounterGetPage()),
GetPage(name: jumpOne, page: () => JumpOnePage()),
GetPage(name: jumpTwo, page: () => JumpTwoPage()),
];
}
路由API
請注意命名路由,只需要在api結尾加上Named即可,舉例:
- 默認:Get.to(SomePage());
- 命名路由:Get.toNamed(“/somePage”);
詳細Api介紹,下面內容來自GetX的README文檔,進行了相關整理。導航到新的頁面。
Get.to(NextScreen());
Get.toNamed("/NextScreen");
關閉SnackBars、Dialogs、BottomSheets或任何你通常會用Navigator.pop(context)關閉的東西。
Get.back();
進入下一個頁面,但沒有返回上一個頁面的選項(用於SplashScreens,登錄頁面等)。
Get.off(NextScreen());
Get.offNamed("/NextScreen");
進入下一個界面並取消之前的所有路由(在購物車、投票和測試中很有用)。
Get.offAll(NextScreen());
Get.offAllNamed("/NextScreen");
發送數據到其它頁面只要發送你想要的參數即可。Get在這里接受任何東西,無論是一個字符串,一個Map,一個List,甚至一個類的實例。
Get.to(NextScreen(), arguments: 'Get is the best');
Get.toNamed("/NextScreen", arguments: 'Get is the best');
在你的類或控制器上:
print(Get.arguments);
//print out: Get is the best
要導航到下一條路由,並在返回后立即接收或更新數據:
var data = await Get.to(Payment());
var data = await Get.toNamed("/payment");
在另一個頁面上,發送前一個路由的數據:
Get.back(result: 'success');
// 並使用它,例:
if(data == 'success') madeAnything();
如果你不想使用GetX語法,只要把 Navigator(大寫)改成 navigator(小寫),你就可以擁有標准導航的所有功能,而不需要使用context,例如:
// 默認的Flutter導航
Navigator.of(context).push(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return HomePage();
},
),
);
// 使用Flutter語法獲得,而不需要context。
navigator.push(
MaterialPageRoute(
builder: (_) {
return HomePage();
},
),
);
// get語法
Get.to(HomePage());
動態網頁鏈接
這是一個非常重要的功能,在web端,可以保證通過url傳參數到頁面里。Get提供高級動態URL,就像在Web上一樣。Web開發者可能已經在Flutter上想要這個功能了,Get也解決了這個問題。
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
在你的controller/bloc/stateful/stateless類上:
print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
你也可以用Get輕松接收NamedParameters。
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(
name: '/',
page: () => MyHomePage(),
),
GetPage(
name: '/profile/',
page: () => MyProfile(),
),
//你可以為有參數的路由定義一個不同的頁面,也可以為沒有參數的路由定義一個不同的頁面,但是你必須在不接收參數的路由上使用斜杠"/",就像上面說的那樣。
GetPage(
name: '/profile/:user',
page: () => UserProfile(),
),
GetPage(
name: '/third',
page: () => Third(),
transition: Transition.cupertino
),
],
)
);
}
發送命名路由數據:
Get.toNamed("/profile/34954");
在第二個頁面上,通過參數獲取數據:
print(Get.parameters['user']);
// out: 34954
現在,你需要做的就是使用Get.toNamed()來導航你的命名路由,不需要任何context(你可以直接從你的BLoC或Controller類中調用你的路由),當你的應用程序被編譯到web時,你的路由將出現在URL中。
最后
文中DEMO地址:
GetX插件地址:
歡迎大家加入我們,一起學習進步!