詳解Flutter?響應(yīng)式狀態(tài)管理框架GetX
一、狀態(tài)管理框架對比
在Flutter的狀態(tài)管理框架中,主流的狀態(tài)管理框架有四個:GetX(又稱為Get)、BLoC、MobX、Provider。
Provider
其中,Provider是Flutter社區(qū)提供的一種狀態(tài)管理工具,本質(zhì)上是對InheritedWidget組件的封裝,具有如下一些優(yōu)點(diǎn):
- 簡化的資源分配與處置
- 懶加載
- 創(chuàng)建新類時減少大量的模板代碼
- 支持 DevTools
- 更通用的調(diào)用 InheritedWidget 的方式
- 提升類的可擴(kuò)展性,整體的監(jiān)聽架構(gòu)時間復(fù)雜度以指數(shù)級增長
BLoC
BLoC是Business Logic Component的英文縮寫,中文譯為業(yè)務(wù)邏輯組件,是一種使用響應(yīng)式編程來構(gòu)建應(yīng)用的方式。BLoC最早由谷歌的Paolo Soares和Cong Hui設(shè)計(jì)并開發(fā),設(shè)計(jì)的初衷是為了實(shí)現(xiàn)頁面視圖與業(yè)務(wù)邏輯的分離。下圖演示了BLoC模式的應(yīng)用程序的架構(gòu)示意圖。

BLoC依賴Stream和StreamController,組件通過Sink發(fā)送狀態(tài)事件,然后再通過Stream通知其他組件進(jìn)行狀態(tài)刷新,事件的處理和通知更新都由BLoC負(fù)責(zé),如下圖所示。

GetX
GetX是一種輕量級且強(qiáng)大的Flutter解決方案,集高性能狀態(tài)管理、智能依賴注入和路由管理于一體,是Flutter開發(fā)不可多得的工具。具有如下優(yōu)勢:
- 依賴注入: 依賴注入是一種消除組件之間依賴的方式,用來降低使用者與其依賴組件之間的耦合度。使用依賴注入具有便于重構(gòu)和便于擴(kuò)展的好處,比如獲取實(shí)例無需BuildContext、GetBuilder自動化處理及減少入?yún)⒌鹊取?/li>
- 跨頁面交互: 跨頁面交互是一種常見的場景,GetX可以很優(yōu)雅的實(shí)現(xiàn)跨頁面交互,如參數(shù)傳遞和跨頁面的狀態(tài)管理。
- 路由管理:Getx內(nèi)部實(shí)現(xiàn)了路由管理,而且使用起來也很簡單。同時,GetX實(shí)現(xiàn)了動態(tài)路由傳參,也就是說直接在命名路由上拼參數(shù)。
二、基本使用
2.1 安裝與引用
和其他的Flutter插件一樣,使用GetX之前需要先在項(xiàng)目中導(dǎo)入GetX插件,GetX插件分為非空安全和空安全兩個版本,分別是應(yīng)對2.0之前和之后的版本。
#非空安全最后一個版本(flutter 2.0之前版本) get: ^3.26.0 #空安全版本 get: ^4.6.5
然后,在需要使用的地方引入get。
import 'package:get/get.dart';
2.2 使用GetX改造Counter App
為了展示GetX的強(qiáng)大功能,我們將對Flutter官方的計(jì)數(shù)器示例使用GetX進(jìn)行改造。并且,使用GetX之后,業(yè)務(wù)邏輯和屏幕之間的共享狀態(tài)的管理將會發(fā)生變化,經(jīng)過改造之后,原本需要近100行代碼才能使用的功能,現(xiàn)在26行就能夠?qū)崿F(xiàn)。
第1步,將項(xiàng)目的MaterialApp變成GetMaterialApp,如下所示。
void main() => runApp(GetMaterialApp(home: Home()));
需要說明的是,在應(yīng)用的最頂層使用GetMaterialApp會創(chuàng)建路由,如果您只是想使用GetX進(jìn)行狀態(tài)管理或依賴項(xiàng)管理,則沒有必要將MaterialApp修改為GetMaterialApp。如果項(xiàng)目中需要使用諸如路由、snackbar、國際化、bottomSheets、對話框和上下文相關(guān)的高級api,那么可以使用GetMaterialApp。
同時,如果使用了GetMaterialApp的路由功能,那么可以使用Get.to(), Get.back()等函數(shù)來管理路由時。如果不打算使用它,那么就沒有必要執(zhí)行第一步。
第2步,創(chuàng)建業(yè)務(wù)邏輯類,并將所有變量、方法和控制器放在里面。然后使用.obs創(chuàng)建可觀察變量。
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
}
第3步,創(chuàng)建視圖,然后GetX的Controller獲取狀態(tài),在創(chuàng)建視圖時使用StatelessWidget即可,不再需要使用Statfulwidget,更加節(jié)約內(nèi)存和性能開銷。
class Home extends StatelessWidget {
@override
Widget build(context) {
final Controller c = Get.put(Controller());
return Scaffold(
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
body: Center(child: ElevatedButton(
child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
}
}
class Other extends StatelessWidget {
final Controller c = Get.find();
@override
Widget build(context){
return Scaffold(body: Center(child: Text("${c.count}")));
}
}
從示例可以看到,引入GetX狀態(tài)管理框架之后,代碼的結(jié)構(gòu)也相對的發(fā)生了變化,并且邏輯上也變得更加清晰,非常適合中大型項(xiàng)目。
2.3 GetX代碼插件
由于GetX的使用流程比較固定,我們完全可以將它開發(fā)成一個IDE插件,下面是社區(qū)上開源的一個IDE插件,大家可以將其下載下來,然后進(jìn)行自定義的升級。
- Github:getx_template
- Jetbrains:getx_template
目前,插件已經(jīng)支持生成GetX的所有文件。首先,我們打開Android Studuo,在Plugins 搜索GeX,然后點(diǎn)擊安裝。

接著,生成GetX的模式,目前提供了兩種生成方式:
- Default:默認(rèn)模式,生成三個文件:state,logic,view
- Easy:簡單模式,生成倆個文件:logic,view

同時,插件還提供后綴名修改和數(shù)據(jù)的持久化。

插件還提供了快捷鍵功能,如使用【Alt + Enter】快捷鍵可以調(diào)出包裹Widget,有四種可選:GetBuilder、GetBuilder(Auto Dispose),Obx、GetX。

三、其他功能
除了基本的狀態(tài)管理外,GetX還支持依賴關(guān)系管理、路由管理、snackbar、國際化、bottomSheets、上下文相關(guān)的API等功能。
3.1 路由管理
如果在項(xiàng)目中有涉及路由、snackbar、對話框、bottomSheets的功能需求,那么GetX也是非常不錯的。為了使用GetX的路由功能,我們需要使用GetMaterialApp替換MaterialApp。
GetMaterialApp( home: MyHome(), )
然后,要打開一個新頁面時,使用Get.to()或者Get.toNamed()。
Get.to(NextScreen());
Get.toNamed('/details');
使用命名路由打開一個新的頁面時,需要先在GetMaterialApp中進(jìn)行申明,比如。
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => MyHomePage()),
GetPage(name: '/second', page: () => Second()),
GetPage(
name: '/third',
page: () => Third(),
transition: Transition.zoom
),
],
)
);
}
如果使用的是系統(tǒng)默認(rèn)的AppBar,使用的是Navigator.pop(context)來關(guān)閉頁面,如果是自定義的AppBar,并且使用了GetX的路由功能,那么需要使用下面得代碼來關(guān)閉頁面。
Get.back();
有時候,我們打開一個新頁面時候,在關(guān)閉得時需要返回上一個頁面,那么可以使用Get.off()。
Get.off(NextScreen());
如果打開頁面并且需要清除路由棧的其他頁面,那么可以使用Get.offAll()。
Get.offAll(NextScreen());
同時,GetX的路由提供了獲取上下文的功能,這也是GetX路由管理的最大優(yōu)點(diǎn)之一。有了它,開發(fā)者可以從控制器類中獲取路由的所有方法。
3.2 依賴關(guān)系管理
除了狀態(tài)管理和路由管理外,GetX還支持依賴關(guān)系管理,它能夠?qū)崿F(xiàn)Bloc或Controller相同功能,而實(shí)現(xiàn)上只需要1行代碼。
Controller controller = Get.put(Controller());
同時,Get依賴管理與包的其他部分是分離的,如果你的應(yīng)用程序已經(jīng)在使用狀態(tài)管理器,那么不需要再重寫它,比如。
controller.fetchApi();
如果您的項(xiàng)目中使用了很多的路由,并且這些路由已經(jīng)存在了GetX的路由棧中,那么可以使用Get.find()來獲取路由棧的相關(guān)信息。
Controller controller = Get.find(); Text(controller.textFromApi);
3.3 工具
除了上面介紹的核心功能外,GetX還提供了很多的工具功能,比如國際化。使用GetX實(shí)現(xiàn)國際化時,首先需要創(chuàng)建一個繼承自Translations的類。
import 'package:get/get.dart';
class Messages extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'en_US': {
'hello': 'Hello World',
},
'de_DE': {
'hello': 'Hallo Welt',
}
};
}
然后,只需將.tr附加到指定的鍵,它將使用GetX的當(dāng)前值進(jìn)行轉(zhuǎn)換。系統(tǒng)將會依據(jù)地區(qū)和Get.fallbackLocale的返回值進(jìn)行轉(zhuǎn)換。
Text('title'.tr);
除此之外,我們還可以使用參數(shù)的方式進(jìn)行轉(zhuǎn)換。
import 'package:get/get.dart';
Map<String, Map<String, String>> get keys => {
'en_US': {
'logged_in': 'logged in as @name with email @email',
},
'es_ES': {
'logged_in': 'iniciado sesión como @name con e-mail @email',
}
};
Text('logged_in'.trParams({
'name': 'Jhon',
'email': 'jhon@example.com'
}));
最后,還需要在GetMaterialApp中定義語言的環(huán)境和翻譯內(nèi)容。
return GetMaterialApp(
translations: Messages(),
locale: Locale('en', 'US'),
fallbackLocale: Locale('en', 'UK'),
);
如果需要改變本地的默認(rèn)的語言環(huán)境,可以使用下面的方式。
var locale = Locale('en', 'US');
Get.updateLocale(locale);
3.4 改變主題
當(dāng)然,我們可以可以使用GetX來實(shí)現(xiàn)默認(rèn)主題的修改。這種功能類似于使用ThemeProvider來改變應(yīng)用的主題。首先,您需要創(chuàng)建自定義主題并將其添加到Get中。
Get.changeTheme(ThemeData.light());
比如,我們想在onTap中創(chuàng)建一個按鈕來改變主題。
Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
3.5 GetConnect
GetConnect是一種可以使用http或websockets實(shí)現(xiàn)和服務(wù)器通信的方法,可以使用它實(shí)現(xiàn)GET/POST/PUT/DELETE/SOCKET網(wǎng)絡(luò)請求。
class UserProvider extends GetConnect {
// Get request
Future<Response> getUser(int id) => get('http://youapi/users/$id');
// Post request
Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
// Post request with File
Future<Response<CasesModel>> postCases(List<int> image) {
final form = FormData({
'file': MultipartFile(image, filename: 'avatar.png'),
'otherFile': MultipartFile(image, filename: 'cover.png'),
});
return post('http://youapi/users/upload', form);
}
GetSocket userMessages() {
return socket('https://yourapi/users/socket');
}
}
和其他的網(wǎng)絡(luò)請求庫一樣,GetConnect也支持自定義基Url、請求頭、自定義請求修飾符,以及請求重試,以及解碼器和將請求結(jié)果轉(zhuǎn)換為model等。
class HomeProvider extends GetConnect {
@override
void onInit() {
// All request will pass to jsonEncode so CasesModel.fromJson()
httpClient.defaultDecoder = CasesModel.fromJson;
httpClient.baseUrl = 'https://api.covid19api.com';
// baseUrl = 'https://api.covid19api.com'; // It define baseUrl to
// Http and websockets if used with no [httpClient] instance
// It's will attach 'apikey' property on header from all requests
httpClient.addRequestModifier((request) {
request.headers['apikey'] = '12345678';
return request;
});
// Even if the server sends data from the country "Brazil",
// it will never be displayed to users, because you remove
// that data from the response, even before the response is delivered
httpClient.addResponseModifier<CasesModel>((request, response) {
CasesModel model = response.body;
if (model.countries.contains('Brazil')) {
model.countries.remove('Brazilll');
}
});
httpClient.addAuthenticator((request) async {
final response = await get("http://yourapi/token");
final token = response.body['token'];
// Set the header
request.headers['Authorization'] = "$token";
return request;
});
//Autenticator will be called 3 times if HttpStatus is
//HttpStatus.unauthorized
httpClient.maxAuthRetries = 3;
}
}
@override
Future<Response<CasesModel>> getCases(String path) => get(path);
}
3.6 GetPage中間件
Priority
GetPage是一個新的屬性,它接受一個GetMiddleWare列表,然后按照列表的順序來執(zhí)行這些中間件。要運(yùn)行的中間件的順序,可以通過GetMiddleware中的順序來設(shè)置。
final middlewares = [ GetMiddleware(priority: 2), GetMiddleware(priority: 5), GetMiddleware(priority: 4), GetMiddleware(priority: -8), ];
Redirect
當(dāng)需要執(zhí)行路由的重定向時,就可以調(diào)用此函數(shù),比如使用它實(shí)現(xiàn)強(qiáng)制登錄邏輯,即沒有登錄時跳轉(zhuǎn)登錄邏輯。
RouteSettings redirect(String route) {
final authService = Get.find<AuthService>();
return authService.authed.value ? null : RouteSettings(name: '/login')
}
onPageCalled
有時候,我們需要在頁面創(chuàng)建之前調(diào)用某個額函數(shù),比如可以使用它來更改頁面的某些內(nèi)容或?yàn)槠涮峁┬马撁妗?/p>
GetPage onPageCalled(GetPage page) {
final authService = Get.find<AuthService>();
return page.copyWith(title: 'Welcome ${authService.UserName}');
}
OnBindingsStart
此函數(shù)將在初始化綁定之前被調(diào)用。在這個函數(shù)中,我們可以更改頁面的綁定。
List<Bindings> onBindingsStart(List<Bindings> bindings) {
final authService = Get.find<AuthService>();
if (authService.isAdmin) {
bindings.add(AdminBinding());
}
return bindings;
}
OnPageBuildStart
此函數(shù)將在綁定初始化之后被調(diào)用。在這個函數(shù)中,我們可以在創(chuàng)建綁定之后和創(chuàng)建頁面小部件之前執(zhí)行一些操作。
GetPageBuilder onPageBuildStart(GetPageBuilder page) {
print('bindings are ready');
return page;
}
3.7 全局設(shè)置和手動配置
默認(rèn)情況下,GetMaterialApp提供了一下默認(rèn)的配置,但如果你想手動配置GetX,那也是可以得。
MaterialApp( navigatorKey: Get.key, navigatorObservers: [GetObserver()], );
我們還可以將GetObserver替換成自己的中間件。
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer) // Here
],
);
當(dāng)然,我們也可以為GetX創(chuàng)建一個全局設(shè)置。比如在打開路由時修改默認(rèn)的配置。
GetMaterialApp( enableLog: true, defaultTransition: Transition.fade, opaqueRoute: Get.isOpaqueRouteDefault, popGesture: Get.isPopGestureEnable, transitionDuration: Get.defaultDurationTransition, defaultGlobalState: Get.defaultGlobalState, ); Get.config( enableLog = true, defaultPopGesture = true, defaultTransition = Transitions.cupertino )
3.8 StateMixin
處理UI狀態(tài)的另一種方法是使用StateMixin,使用前需要使用with將StateMixin添加到T模型的控制器中。
class Controller extends GetController with StateMixin<User>{}
mixin是Dart中一個非常重要的概念,是一種在多個類層次結(jié)構(gòu)中復(fù)用類代碼的方法。而change()方法是一種可以隨時更改狀態(tài)的方法。
change(data, status: RxStatus.success());
RxStatus支持的狀態(tài)有如下一些:
RxStatus.loading();
RxStatus.success();
RxStatus.empty();
RxStatus.error('message');
然后,我們使用obx根據(jù)狀態(tài)來加載不同的視圖。
class OtherClass extends GetView<Controller> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: controller.obx(
(state)=>Text(state.name),
onLoading: CustomLoadingIndicator(),
onEmpty: Text('No data found'),
onError: (error)=>Text(error),
),
);
}
3.9 GetxService
GetxService的作用類似于GetxController,可以共享相同的生命周期(onInit(), onReady(), onClose()),GetxService可以用來實(shí)現(xiàn)后臺服務(wù)。例如:ApiService, StorageService, CacheService。
Future<void> main() async {
await initServices();
runApp(SomeApp());
}
void initServices() async {
print('starting services ...');
await Get.putAsync(() => DbService().init());
await Get.putAsync(SettingsService()).init();
print('All services started...');
}
class DbService extends GetxService {
Future<DbService> init() async {
print('$runtimeType delays 2 sec');
await 2.delay();
print('$runtimeType ready!');
return this;
}
}
class SettingsService extends GetxService {
void init() async {
print('$runtimeType delays 1 sec');
await 1.delay();
print('$runtimeType ready!');
}
}
參考:Get官方文檔
以上就是詳解Flutter 響應(yīng)式狀態(tài)管理框架GetX的詳細(xì)內(nèi)容,更多關(guān)于Flutter響應(yīng)式狀態(tài)管理GetX的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android 選項(xiàng)卡(TabHost)如何放置在屏幕的底部
如何將TAB放置在屏幕的底端,有很多的新手都想實(shí)現(xiàn)這種效果,本文搜集整理了一些,感興趣的朋友可以參考下哦2013-01-01
Android實(shí)現(xiàn)IOS相機(jī)滑動控件
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)IOS相機(jī)滑動控件的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-08-08
androidx下的fragment的lazy懶加載問題詳解
這篇文章主要介紹了androidx下的fragment的lazy懶加載問題詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Android在WebView中調(diào)用系統(tǒng)下載的方法
這篇文章主要為大家詳細(xì)介紹了Android在WebView中調(diào)用系統(tǒng)下載的簡單使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05
android實(shí)現(xiàn)下拉菜單三級聯(lián)動
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)下拉菜單三級聯(lián)動,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-10-10
Android實(shí)現(xiàn)實(shí)時滑動ViewPager的2種方式
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)實(shí)時滑動ViewPager的2種方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10
Java4Android開發(fā)教程(三)java基本概念
本文介紹了Java4Android的基本概念,都是開發(fā)必備的基礎(chǔ)知識,希望能對大家有所幫助2014-10-10

