我用 GetX寫了一個使用demo,和一個app,demo中導航的演示如下:
前言
GetX 是 Flutter 上的一個輕量且強大的解決方案:高性能的狀態管理、智能的依賴注入和便捷的路由管理。
爲什麼是 GetX,而不是 BLoC、MobX、Provider?
BLoC 非常安全和高效,但是對於初學者來說非常複雜,即使學會,樣板代碼也很多。
MobX 比 BLoC 更容易,而且是響應式的,但是需要使用一個代碼生成器,需要等很久,這降低了生產力。
GetX我喜歡的地方:
- 輕量。模塊單獨編譯,沒用到的功能不會編譯進我們的代碼。
- 語法簡潔。個人非常喜歡,顯而易見且實用,比如路由擺脫了 context 的依賴,
Get.to(SomePage())
就能導航到新路由。 - 性能。Provider、BLoC 等只能在父子組件保存狀態,同層級模塊狀態管理需要全局處理,存活在整個應用生命週期。而 GetX 可以隨時添加控制器和刪除控制器,並且會自動釋放使用完的控制器。
- 依賴注入。提供依賴注入功能,代碼層級可以完全分離,甚至依賴注入的代碼也是分離的。
- 豐富的api。許多複雜的操作,使用 GetX 就會有簡單的實現。
有的同學看過我寫的Flutter狀態管理provider的使用和封裝,講解了 Provider 的使用,其實在使用過程中發現了許多痛點,最致命的是 Provider 使用InheritedWidget
來傳遞相同的監聽器,這意味着對其 ChangeNotifier 類的任何訪問都必須在父子widget樹內。非父子組件的狀態管理問題,需要藉助別的手段(eventbus,全局,單例),十分痛苦,在改用GetX後,越來越舒服了。
路由
普通路由導航
打開到新的頁面:
Get.to(NextScreen());
對應原生路由:
Navigator.push(context, MaterialPageRoute<void>(
builder: (BuildContext context) {
return NextScreen();
},
));
返回:
Get.back();
對應原生路由:
Navigator.pop(context);
打開新頁面,並且用新頁面替換舊頁面(刪除舊頁面):
Get.off(NextScreen());
對應原生路由:
Navigator.pushReplacement(context, MaterialPageRoute<void>(
builder: (BuildContext context) {
return NextScreen();
},
));
打開新頁面並刪除之前的所有路由:
Get.offAll(NextScreen());
對應原生路由:
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) {
return NextScreen();
},
),
(Route<dynamic> route) => false,
);
導航到新頁面,在返回時接收返回數據:
var data = await Get.to(NextScreen());
對應原生路由:
var data = await Navigator.push(context, MaterialPageRoute<void>(
builder: (BuildContext context) {
return NextScreen();
},
));
帶返回值返回前一個路由,配合上面使用:
Get.back(result: 'success');
對應原生路由:
Navigator.pop(context, 'success');
別名路由導航
- 聲明別名:
abstract class Routes {
static const Initial = '/';
static const NextScreen = '/NextScreen';
}
- 註冊路由表:
abstract class AppPages {
static final pages = [
GetPage(
name: Routes.Initial,
page: () => HomePage(),
),
GetPage(
name: Routes.NextScreen,
page: () => NextScreen(),
),
];
}
- 替換MaterialApp爲GetMaterialApp:
void main() {
runApp(GetMaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
theme: appThemeData,
defaultTransition: Transition.fade,
getPages: AppPages.pages,
home: HomePage(),
));
}
使用
導航到下一個頁面:
Get.toNamed(Routes.NextScreen);
導航到下一個頁面並刪除前一個頁面:
Get.offNamed(Routes.NextScreen);
導航到下一個頁面並刪除以前所有的頁面:
Get.offAllNamed(Routes.NextScreen);
發送數據到別名路由:
Get在這裏接受任何東西,無論是一個字符串,一個Map,一個List,甚至一個類的實例。
Get.toNamed(Routes.NextScreen, arguments: '新垣結衣');
獲取參數:
String name=Get.arguments;
動態網頁鏈接:
像web一樣攜帶參數,適合前端開發的風格。
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
獲取參數:
int id = Get.parameters['id'];
// out: 354
String name=Get.parameters['name'];
還可以這樣定義路由別名:
GetPage(
name: '/profile/:user',
page: () => UserProfile(),
),
導航:
Get.toNamed("/profile/34954");
在第二個頁面上,通過參數獲取數據
print(Get.parameters['user']);
// out: 34954
中間件
在跳轉前做些事情,比如判斷是否登錄,可以使用routingCallback來實現:
GetMaterialApp(
routingCallback: (routing) {
if(routing.current == '/second'){
// 如果登錄。。。
}
}
)
小部件導航
SnackBars
彈出:
Get.snackbar('Hi', 'i am a modern snackbar');
對應原生寫法:
final snackBar = SnackBar(
content: Text('Hi!'),
action: SnackBarAction(
label: 'I am a old and ugly snackbar',
onPressed: (){}
),
);
//用Flutter創建一個簡單的SnackBar,你必須獲得Scaffold的context,或者你必須使用一個GlobalKey附加到你的Scaffold上。
Scaffold.of(context).showSnackBar(snackBar);
Dialogs
打開一個默認的Dialog:
Get.defaultDialog(
onConfirm: () => print("Ok"),
middleText: "Dialog made in 3 lines of code"
);
打開自定義的Dialog:
Get.dialog(YourDialogWidget());
BottomSheets
Get.bottomSheet類似於showModalBottomSheet,但不需要context:
Get.bottomSheet(
Container(
child: Wrap(
children: <Widget>[
ListTile(
leading: Icon(Icons.music_note),
title: Text('Music'),
onTap: () => {}
),
ListTile(
leading: Icon(Icons.videocam),
title: Text('Video'),
onTap: () => {},
),
],
),
)
);