公式 Web サイト: https://pub.dev/packages/get
中国語ドキュメント: https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md
GetX について
現在、redux、bloc、state、provider、Getx など、Flutter 用の状態管理ソリューションが多数あります。
プロバイダーは公式の状態管理ソリューションであり、その主な機能は状態管理です。Getxはサードパーティ製の状態管理プラグインで、状態管理機能だけでなく、ルーティング管理、テーマ管理、国際多言語管理、Obx部分更新、ネットワークリクエスト、データ検証などの機能も備えています。他の状態管理プラグインと比較して、Getx はよりシンプル、強力、そして高性能です。
簡単に言うと、オリジナルのソリューションや他のソリューションに対する GetX の最大の利点は、使いやすく、機能が豊富で、最初から最後までファミリー スタイルの関数 API を提供することです。
-
GetX は、高性能の状態管理、スマートな依存関係の注入、便利なルーティング管理を備えた、Flutter 上の軽量かつ強力なソリューションです。
-
GetX には 3 つの基本原則があります。
- パフォーマンス: GetX はパフォーマンスと最小限のリソース消費に重点を置いています。GetX のパッケージ化された APK サイズとランタイム メモリ使用量は、他の状態管理プラグインと同等です。
- 効率: GetX の構文は非常にシンプルで、非常に高いパフォーマンスを維持するため、開発時間を大幅に短縮できます。
- 構造: GetX はインターフェイス、ロジック、依存関係、ルーティングを完全に分離できるため、使いやすく、ロジックが明確になり、コードの保守が容易になります。
-
GetX は肥大化しませんが、非常に軽量です。状態管理のみを使用する場合は、状態管理モジュールのみがコンパイルされ、使用されないその他のものはコードにコンパイルされません。多数の関数がありますが、これらの関数は別のコンテナーにあり、使用するまで起動されません。
-
Getx には巨大なエコシステムがあり、Android、iOS、Web、Mac、Linux、Windows、およびサーバー上で同じコードを実行できます。Get Server
を使用すると、フロントエンドで作成したコードをバックエンドで完全に再利用できます。
GetX には主に 3 つの主要な関数があります。
- 状態管理
- ルーティング管理
- 依存関係の管理
以下にそれぞれ紹介します。
インストール
Get を pubspec.yaml ファイルに追加します。
dependencies:
get: ^4.6.5
必要なファイルにインポートして使用します。
import 'package:get/get.dart';
アプリケーションエントリーを設定する
依存関係をインポートする場合、最初のステップは、以下に示すように、main.dart
エントリ ファイル内の元のファイルを最上位としてMaterialApp
置き換えることです。GetMaterialApp
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(GetXApp ());
}
class GetXApp extends StatelessWidget {
const GetXApp({
super.key});
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: const AppBarTheme(centerTitle: true,)
),
home: const GetXHomePage(),
);
}
}
GetX 応答状態管理
たとえば、GetX を使用して Flutter のデフォルトのカウンター サンプル アプリケーションを変更するには、次の 3 つの手順だけが必要です。
ステップ 1: 変数を定義するときに、変数の末尾に.obs
サフィックスを追加します
var _counter = 0.obs;
ステップ 2: 変数が使用されている場合は、 を使用してObx
ラップします。
Obx(() => Text("${
_counter}"))
ステップ 3: ボタンをクリックすると、変数値を直接変更すると、UI が自動的に更新されます。
onPressed: () => _counter++
それだけです、とても簡単です。setState
呼び出す必要すらありません。
リアクティブ変数を宣言するには 3 つの方法があります。
最初はRx{Type}を使用します
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final myMap = RxMap<String, int>({
});
2 つ目は、汎用 Rxを指定して Rx を使用する方法です。
final name = Rx<String>('');
final isLogged = Rx<Bool>(false);
final count = Rx<Int>(0);
final balance = Rx<Double>(0.0);
final number = Rx<Num>(0)
final items = Rx<List<String>>([]);
final myMap = Rx<Map<String, int>>({
});
final user = Rx<User>(); // 自定义类 - 可以是任何类
3 番目の、より実用的でシンプルで好ましい方法は、単に.obs
属性を値として追加することです。(推奨)
final name = ''.obs;
final isLogged = false.obs;
final count = 0.obs;
final balance = 0.0.obs;
final number = 0.obs;
final items = <String>[].obs;
final myMap = <String, int>{
}.obs;
final user = User().obs; // 自定义类 - 可以是任何类
別の例として、次を定義してみましょうList
。
RxList _list = ["张三", "李四"].obs;
そしてそれを使用します:
Obx(() {
return Column (
children: _list.map((v) {
return ListTile(title: Text(v));}).toList(),
);
}),
後で変更します。
onPressed: () => _list.add("王五");
注:Obx
対応する状態変数は 1 対 1 に対応します。複数の変数がある場合、1 つだけを最外層でラップすることはできません。RxObx
変数を使用するすべての場所をObx
ラップする必要があります。
カスタム クラス データの変更をリッスンする
1. Person クラスを作成し、応答性の高い方法でメンバー変数を変更します
import 'package:get/get.dart';
class Person {
RxString name = "Jimi".obs; // rx 变量
RxInt age = 18.obs;
}
2. class属性値を取得し、値を変更する
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import './person.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({
super.key});
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({
super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var person = Person(); // 定义 Person 对象
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Getx Obx"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Obx(() => Text( // 读取时使用Obx包裹
"我的名字是 ${
person.name}",
style: const TextStyle(color: Colors.red, fontSize: 30),
))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
person.name.value = person.name.value.toUpperCase(); // 修改值
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
別の方法では、クラス全体を応答型として定義することもできます。次に例を示します。
import 'package:get/get.dart';
class Animal {
String username;
int age;
Animal(this.username,this.age);
}
上記のオブジェクトを作成するときに、サフィックスをAnimal
直接追加できます。.obs
var a = Animal("xiao mao", 2).obs;
Obx
パッケージはまだ使用しています
Obx(() => Text(a.username))
変更する場合は、変数ポインタのポイントを変更する必要があります。
onPressed: () => a.value = Animal("小狗", 3)
GetX ルーティング管理
ホームページを設定するだけの場合は、MaterialApp
次と同じです。
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
ルーティング テーブルを設定する必要がある場合は、次のようにします。
GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: const AppBarTheme(centerTitle: true)
),
initialRoute: "/",
defaultTransition: Transition.rightToLeft, // 配置全局路由页面切换动画效果
getPages: AppPage.routes, // 配置路由表
);
// routes.dart
// 路由表, 可放在单独文件中
import 'package:get/get.dart';
class AppPage {
static final routes = [
GetPage(name: "/", page: () => const Tabs()),
GetPage(name: "/shop", page: () => const ShopPage(), middlewares: [ShopMiddleWare()]),
GetPage(name: "/login", page: () => const LoginPage()),
GetPage(name: "/registerFirst", page: () => const RegisterFirstPage(), transition: Transition.fade),// 单独指定某个路由页面切换效果
GetPage(name: "/registerSecond", page: () => const RegisterSecondPage()),
GetPage(name: "/registerThird", page: () => const RegisterThirdPage()),
];
}
GetX共通ルートジャンプメソッド:
方法 | 関数 |
---|---|
Get.to() |
新しいページにジャンプする |
Get.toNamed() |
名前付きルーティングジャンプ |
Get.back() |
戻るルート |
Get.off() |
次のページに進みますが、前のページに戻るオプションはありません (スプラッシュ スクリーン ページ、ログイン ページなどの場合) |
Get.offAll() |
次のページに移動して、以前のルートをすべてキャンセルします |
Get.offAndToNamed() |
現在のルート ページをジャンプして次のルートに置き換えます (検索ページなどの中間ページを閉じるために使用できます) |
Get.removeRoute() |
ルートを削除する |
Get.until() |
式が true を返すまで繰り返し返されます |
Get.offUntil() |
次のルートに移動し、式が true を返すまで前のルートをすべて削除します。 |
Get.offNamedUntil() |
次の名前付きルートに移動し、式が true を返すまで、以前のルートをすべて削除します。 |
Get.arguments |
現在のルーティング ページのパラメータを取得します |
Get.previousRoute |
以前のルート名を取得する |
Get.rawRoute |
訪問するための元のルートを教えてください |
ルートパス値:
Get.toNamed("/shop", arguments: {
"id":3456});
ページを受信:
print(Get.arguments);
中間ページのルーティング:
上記のルーティング テーブル設定GetPage
のパラメータmiddlewares
は、ルーティング ジャンプの中間ページを設定でき、許可の判断やインターセプトなどの一部のサービスをこのページで実行できます。次に例を示します。
// shopMiddleware.dart
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
class ShopMiddleWare extends GetMiddleware {
RouteSettings? redirect(String? route) {
print("跳转ShopPage路由页面的中间页面-------");
// return null; // 返回空表示不做任何操作,走原来的路由跳转逻辑,即跳转到ShopPage
// 此处可判断没有权限后跳转到登录页面
return const RouteSettings(name: "/login", arguments: {
});
}
}
優先順位
ミドルウェアの優先順位を設定すると、Getミドルウェアの実行順序が定義されます。
final middlewares = [
GetMiddleware(priority: 2),
GetMiddleware(priority: 5),
GetMiddleware(priority: 4),
GetMiddleware(priority: -8),
];
これらのミドルウェアは、8 => 2 => 4 => 5 の順序で実行されます。
GetMiddleware
他のいくつかの方法を次に示します。
- onPageCalled:この関数は、ページが呼び出されたとき、何かが作成される前に呼び出されます。これを使用して、ページに関する内容を変更したり、新しいページを追加したりできます。
GetPage onPageCalled(GetPage page) {
final authService = Get.find<AuthService>();
return page.copyWith(title: 'Welcome ${
authService.UserName}');
}
- **OnBindingsStart:** この関数は、バインディングが初期化される前に呼び出されます。ここで、このページのバインドを変更できます。
List<Bindings> onBindingsStart(List<Bindings> bindings) {
final authService = Get.find<AuthService>();
if (authService.isAdmin) {
bindings.add(AdminBinding());
}
return bindings;
}
- OnPageBuildStart:この関数はバインディングの初期化後に呼び出されます。ここでは、バインディングの作成後、ページ ウィジェットの作成前にいくつかの操作を実行できます。
GetPageBuilder onPageBuildStart(GetPageBuilder page) {
print('bindings are ready');
return page;
}
-
OnPageBuilt:この関数は GetPage.page が呼び出された後に呼び出され、関数の結果を返し、表示されるウィジェットを取得します。
-
OnPageDispose:この関数は、ページのすべての関連オブジェクト (コントローラー、ビューなど) を破棄した後に呼び出されます。
GetX 依存関係管理
GetX 依存関係管理は、Provider
またはよりInheritedWidget
も簡単で便利です。たとえば、カウンター サンプル アプリケーションを例として取り上げ、依存関係管理を使用して変更し、次の要件を実現します。
- クリックするたびに状態を変更します
- 異なるページ間で状態を共有できる
- ビジネスロジックをインターフェースから分離する
ステップ 1:Controller
クラスを定義する
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
decrement() => count--;
}
ステップ 2: ページを作成して使用するController
class Home extends StatelessWidget {
Widget build(context) {
final Controller c = Get.put(Controller()); // 使用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()) // 跳转Other页面
)
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: c.increment // 调用控制器的方法修改计数器的值
));
}
}
ステップ 3:他のルーティング ページのController
共有カウンタの値を取得する
class Other extends StatelessWidget {
final Controller c = Get.find(); // 通过 Get.find() 找到控制器对象
Widget build(context){
return Scaffold(body: Center(child: Text("${
c.count}"))); // 访问控制器的计数变量
}
}
ご覧のとおり、GetX 依存関係管理を使用した後は、それを使用する必要がなくなる場合がありますStatefulWidget
。GetX は非常に簡単なので、開発をスピードアップし、パフォーマンスを犠牲にすることなくすべてを予定どおりに提供します。
要約すると、GetX は次の 2 行のコードを使用してコントローラーを作成および取得します。
Controller controller = Get.put(Controller());
Controller controller = Get.find();
私たちがしなければならないのは、ページ内に点在するデータ変数を抽出し、そのデータ変数をコントローラーに変更するロジックを抽出することですController
。
はい、まるで魔法のようです。GetX がコントローラーを見つけてそれを渡します。100 万個のコントローラーをインスタンス化すると、GetX は常に正しいコントローラーを提供します。その後、後で取得したコントローラーデータを復元できます。
Text(controller.textFromApi);
無数のルートを参照し、コントローラーに残っているデータを取得する必要があると想像してください。GetX はコントローラーに必要なデータを自動的に見つけます。追加の依存関係さえ必要ありません。
依存関係管理の詳細については、ここを参照してください。
GetX バインディング
すべてのページで状態管理を使用する必要がある場合、ページごとにコントローラー インスタンスを作成する方法を使用するのは非常に面倒なので、Get.put(MyController())
GetX では、個別のページを必要とせずにグローバルかつ均一に設定できる、よりシンプルなBinding関数を提供します。一つずつ。
まず、Controller
GetX が提供するさまざまなインスタンス作成方法を理解しましょう。さまざまなビジネスに応じて選択できます。
- Get.put() : コントローラーインスタンスも使用せずに作成されます
- Get.lazyPut() : 使用される場合にのみ、遅延読み込み方式でインスタンスを作成します。
- Get.putAsync() : Get.put()の非同期バージョン
- Get.create() : 使用されるたびに新しいインスタンスが作成されます
バインド機能の使い方を見てみましょう
ステップ 1:Binding
クラスを作成する
import 'package:get/get.dart';
class AllControllerBinding implements Bindings {
void dependencies() {
// 所有的 Controller 都在这里配置
Get.lazyPut<BindingMyController>(() => BindingMyController()); // 懒加载方式
Get.lazyPut<BindingHomeController>(() => BindingHomeController());
}
}
ステップ 2:GetMaterialApp
バインディングを初期化する
return GetMaterialApp (
initialBinding: AllControllerBinding(),
home: GetXBindingExample(),
);
ステップ 3:ページ内のGet.find<MyController>()
状態マネージャーを取得して使用する
Obx(() => Text(
"计数器的值为 ${
Get.find<BindingMyController>().count}",
style: TextStyle(color: Colors.red, fontSize: 30),
)),
ElevatedButton(
onPressed: () {
Get.find<BindingMyController>().increment();
},
child: Text("点击加1")
),
ルーティング + バインド + GetView
Binding
GetX を使用するもう 1 つの方法は、GetX をルーティングにバインドすることです。これにより、Get.find
手順を節約できます。
ステップ 1:構成GetMaterialApp
のルーティング テーブルに属性GetPage
を設定するbinding
GetPage(
name: "/shop",
page: () => const ShopPage(),
binding: ShopControllerBinding(), // 设置该页面的 Binding
middlewares: [ShopMiddleWare()]
),
その内容ShopControllerBinding
:
import 'package:get/get.dart';
import '../controllers/shop.dart';
class ShopControllerBinding implements Bindings{
void dependencies() {
Get.lazyPut<ShopController>(() => ShopController());
}
}
ShopController
コンテンツ:
import 'package:get/get.dart';
class ShopController extends GetxController {
RxInt counter = 10.obs;
void inc() {
counter.value++;
update();
}
}
ステップ2:ShopController
使用ページの継承GetView
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/shop.dart';
class ShopPage extends GetView<ShopController> {
const ShopPage({
super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Title'),),
body: Center(
child: Column(
children: [
Obx(() => Text("${
controller.counter}")),
const SizedBox(height: 40,),
ElevatedButton (
onPressed: () {
controller.inc();},
child: const Text("shop counter+1"))
],
),
),
);
}
}
このように、 を記述する必要はなくGet.find
、直接使用できます。
ただし、この方法では、ルーティング ページがポップアップされると、対応するController
インスタンスは破棄されますが、他のメソッドは破棄されないことに注意してください (グローバル共有)。
コントローラーのライフサイクル
GetX のコントローラーにはライフサイクルがあり、次の 3 つのライフサイクル メソッドが提供されます。
- onInit():
widget
ページ コンポーネントがメモリから作成されるとすぐに実行され、ページの初期データ要求などの初期化サービスを実行するために使用できます。 - onReady():の 1 フレーム後に呼び出されます
onInit()
。スナックバー、ダイアログ、新しいルートや非同期リクエストなどのナビゲーション イベントを挿入するのに最適なタイミングです。 - onClose():ページが破棄されるときに呼び出され、リソースをクリーンアップしたり、永続的なデータを保存したりするために使用できます。
import 'package:get/get.dart';
class SearchController extends GetxController {
RxList hotList=[].obs;
void onInit() {
print("请求接口数据");
getHotList();
super.onInit();
}
void onReady() {
print("onReady");
super.onReady();
}
void onClose() {
print("onClose");
super.onClose();
}
void getHotList() {
hotList.add("我是一个模拟的数据");
update();
}
}
GetX の国際化とテーマの構成
グローバリゼーション
ステップ 1: 言語パックを定義する
import 'package:get/get.dart';
class Messages extends Translations {
Map<String, Map<String, String>> get keys => {
'zh_CN': {
'hello': '你好 世界',
},
'de_DE': {
'hello': 'Hallo Welt',
}
};
}
ステップ 2:GetMaterialApp
で構成する
- 翻訳: 国際化設定ファイル
- locale : デフォルトの言語を設定します。設定されていない場合は、システムの現在の言語になります。
- fallbackLocale : 上記で指定した言語翻訳が存在しない場合に備えて、コールバック言語オプションを追加します。
return GetMaterialApp(
translations: Messages(), // 你的翻译语言包
locale: Locale('zh', 'CN'), // 使用指定的语言翻译
fallbackLocale: Locale('en', 'US'), // 指定的语言翻译不存在时的默认语言回调
);
ステップ 3:translate を使用すると、append が.tr
指定されたキーに追加されるたびに、Get.locale
との現在の値がGet.fallbackLocale
変換に使用されます。
Text('title'.tr);
言語を変えてください
Get.updateLocale(locale)
ロケールを更新するために呼び出されます。その後、翻訳では自動的に新しい翻訳が使用されますlocale
。
var locale = Locale('en', 'US');
Get.updateLocale(locale);
システム言語を取得する
システム言語を読み取るには、 を使用できますwindow.locale
。
import 'dart:ui' as ui;
return GetMaterialApp(
locale: ui.window.locale,
);
テーマを変更する
GetMaterialApp
これより高いレベルのウィジェットを使用してテーマを更新しないでください。キーの重複が発生する可能性があります。ThemeProvider
多くの人は、アプリケーションのテーマを変更するために" " ウィジェットを作成することに慣れていますが、これはGetX™ではまったく必要ありません。
Get.changeTheme
カスタム テーマを作成し、テンプレートを使用せずに簡単に追加できます。
Get.changeTheme(ThemeData.light());
onTap
「 」でテーマ変更ボタンのようなものを作成したい場合は、2 つの GetX API Get.changeTheme
+を組み合わせてGet.isDarkMode
実現できます。次のコードonPressed
を に入れることができます。
Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
アクティブ化するとテーマdarkmode
に切り替わり、テーマがアクティブ化するとテーマに切り替わります。light
light
dark
その他の便利な機能
GetX の呼び出しの多くは に渡す必要がないためcontext
、非常に便利です。
方法 | 関数 |
---|---|
Get.defaultDialog() |
ポップアップを表示 |
Get.snackbar() |
スナックバーを表示する |
Get.bottomSheet() |
ボトムシートを表示 |
Get.isSnackbarOpen |
スナックバーが開いているかどうかを確認する |
Get.isDialogOpen |
ダイアログが開いているかどうかを確認する |
Get.isBottomSheetOpen |
ボトムシートが開いているかどうかを確認してください |
Get.changeTheme() |
テーマカラーの切り替え |
Get.isDarkMode |
判決テーマのスタイル |
Get.isAndroid Get.isIOS Get.isMacOS Get.isWindows Get.isLinux Get.isFuchsia |
アプリケーションがどのプラットフォームで実行されているかを確認する |
Get.isMobile Get.isDesktop Get.isWeb |
デバイスのタイプを確認してください。すべてのプラットフォームが Web ブラウザを個別にサポートしていることに注意してください。 |
Get.height Get.width |
に相当MediaQuery.of(context).size.height |
Get.context |
現在のコンテキストを提供する |
Get.contextOverlay |
コード内の任意の場所に、フォアグラウンドでスナックバー/ダイアログ/ボトムシートのコンテキストを提供します。 |
次のメソッドはcontext
コンテキストの拡張です。コンテキストは UI 内のどこからでもアクセスできるため、UI コード内のどこでも使用できます。
方法 | 関数 |
---|---|
context.width context.width |
変更可能な高さ/幅が必要な場合(デスクトップまたはブラウザウィンドウを拡大縮小できるように)、コンテキストを使用する必要があります。 |
context.mediaQuerySize() |
に似ているMediaQuery.of(context).size |
context.mediaQueryPadding() |
に似ているMediaQuery.of(context).padding |
context.mediaQueryViewPadding() |
に似ているMediaQuery.of(context).viewPadding |
context.mediaQueryViewInsets() |
に似ているMediaQuery.of(context).viewInsets |
context.orientation() |
に似ているMediaQuery.of(context).orientation |
context.devicePixelRatio |
に似ているMediaQuery.of(context).devicePixelRatio |
context.textScaleFactor |
に似ているMediaQuery.of(context).textScaleFactor |
context.mediaQueryShortestSide |
デバイスの最も短い辺をクエリします。 |
context.isLandscape() |
デバイスが横向きモードかどうかを確認する |
context.isPortrait() |
デバイスがポートレート モードであるかどうかを確認する |
context.heightTransformer() context.widthTransformer() |
让您可以定义一半的页面、三分之一的页面等。对响应式应用很有用。参数: dividedBy (double) 可选 - 默认值:1。参数: reducedBy (double) 可选 - 默认值:0。 |
context.showNavbar() |
如果宽度大于800,则为真 |
context.isPhone() |
如果最短边小于600p,则为真 |
context.isSmallTablet() |
如果最短边大于600p,则为真 |
context.isLargeTablet() |
如果最短边大于720p,则为真 |
context.isTablet() |
如果当前设备是平板电脑,则为真 |
context.responsiveValue<T>() |
根据页面大小返回一个值<T> 可以给值为:watch :如果最短边小于300mobile :如果最短边小于600tablet :如果最短边(shortestSide )小于1200desktop :如果宽度大于1200 |
GetUtils
GetX 还提供了一个方便的工具类 GetUtils
,里面提供了一些常用的方法,例如判断值是否为空、是否是数字、是否是视频、图片、音频、PPT、Word、APK、邮箱、手机号码、日期、MD5、SHA1等等。具体可以直接IDE查看 GetUtils
类。
GetConnect
GetConnect
可以便捷的通过http
或websockets
进行前后台通信。
默认配置
你能轻松的通过extend GetConnect
就能使用GET/POST/PUT/DELETE/SOCKET
方法与你的Rest API
或websockets
通信。
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');
}
}
自定义配置
GetConnect
具有多种自定义配置。你可以配置base Url
,配置响应,配置请求,添加权限验证,甚至是尝试认证的次数,除此之外,还可以定义一个标准的解码器,该解码器将把您的所有请求转换为您的模型,而不需要任何额外的配置。
class HomeProvider extends GetConnect {
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;
}
Future<Response<CasesModel>> getCases(String path) => get(path);
}
可选的全局设置和手动配置
GetMaterialApp
为你配置了一切,但如果你想手动配置Get。
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [GetObserver()],
);
你也可以在GetObserver
中使用自己的中间件,这不会影响任何事情。
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer) // Here
],
);
你可以为 "Get "创建全局设置。只需在推送任何路由之前将Get.config
添加到你的代码中。或者直接在你的GetMaterialApp
中做。
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
)
你可以选择重定向所有来自Get
的日志信息。如果你想使用你自己喜欢的日志包,并想查看那里的日志。
GetMaterialApp(
enableLog: true,
logWriterCallback: localLogWriter,
);
void localLogWriter(String text, {
bool isError = false}) {
// 在这里把信息传递给你最喜欢的日志包。
// 请注意,即使enableLog: false,日志信息也会在这个回调中被推送。
// 如果你想的话,可以通过GetConfig.isLogEnable来检查这个标志。
}
局部状态组件
这些Widgets允许您管理一个单一的值,并保持状态的短暂性和本地性。我们有Reactive和Simple两种风格。
例如,你可以用它们来切换TextField
中的obscureText
,也许可以创建一个自定义的可扩展面板(Expandable Panel),或者在"Scaffold "的主体中改变内容的同时修改BottomNavigationBar
中的当前索引。
ValueBuilder
StatefulWidget
的简化,它与setState
回调一起工作,并接受更新的值。
ValueBuilder<bool>(
initialValue: false,
builder: (value, updateFn) => Switch(
value: value,
onChanged: updateFn, // 你可以用( newValue )=> updateFn( newValue )。
),
// 如果你需要调用 builder 方法之外的东西。
onUpdate: (value) => print("Value updated: $value"),
onDispose: () => print("Widget unmounted"),
),
ObxValue
类似于ValueBuilder
,但这是Reactive版本,你需要传递一个Rx实例(还记得神奇的.obs吗?自动更新…是不是很厉害?)
ObxValue((data) => Switch(
value: data.value,
onChanged: data, // Rx 有一个 _callable_函数! 你可以使用 (flag) => data.value = flag,
),
false.obs,
),
有用的提示
.obs
(也称为_Rx_ Types)有各种各样的内部方法和操作符。
var message = 'Hello world'.obs;
print( 'Message "$message" has Type ${
message.runtimeType}');
即使message
prints实际的字符串值,类型也是RxString所以,你不能做message.substring( 0, 4 )
。你必须在_observable_里面访问真正的value
。最常用的方法是".value", 但是你也可以用…
final name = 'GetX'.obs;
//只有在值与当前值不同的情况下,才会 "更新 "流。
name.value = 'Hey';
// 所有Rx属性都是 "可调用 "的,并返回新的值。
//但这种方法不接受 "null",UI将不会重建。
name('Hello');
// 就像一个getter,打印'Hello'。
name() ;
///数字。
final count = 0.obs;
// 您可以使用num基元的所有不可变操作!
count + 1;
// 注意!只有当 "count "不是final时,这才有效,除了var
count += 1;
// 你也可以与数值进行比较。
count > 2;
/// booleans:
final flag = false.obs;
// 在真/假之间切换数值
flag.toggle();
/// 所有类型。
// 将 "value "设为空。
flag.nil();
// 所有的toString()、toJson()操作都会向下传递到`value`。
print( count ); // 在内部调用 "toString() "来GetRxInt
final abc = [0,1,2].obs;
// 将值转换为json数组,打印RxList。
// 所有Rx类型都支持Json!
print('json: ${
jsonEncode(abc)}, type: ${
abc.runtimeType}');
// RxMap, RxList 和 RxSet 是特殊的 Rx 类型,扩展了它们的原生类型。
// 但你可以像使用普通列表一样使用列表,尽管它是响应式的。
abc.add(12); // 将12添加到列表中,并更新流。
abc[3]; // 和Lists一样,读取索引3。
// Rx和值是平等的,但hashCode总是从值中提取。
final number = 12.obs;
print( number == 12 ); // prints > true
///自定义Rx模型。
// toJson(), toString()都是递延给子代的,所以你可以在它们上实现覆盖,并直接打印()可观察的内容。
class User {
String name, last;
int age;
User({
this.name, this.last, this.age});
String toString() => '$name $last, $age years old';
}
final user = User(name: 'John', last: 'Doe', age: 33).obs;
// `user`是 "响应式 "的,但里面的属性却不是!
// 所以,如果我们改变其中的一些变量:
user.value.name = 'Roi';
// 小部件不会重建!
// 对于自定义类,我们需要手动 "通知 "改变。
user.refresh();
// 或者我们可以使用`update()`方法!
user.update((value){
value.name='Roi';
});
print( user );
GetWidget
大多数人都不知道这个Widget,或者完全搞不清它的用法。这个用例非常少见且特殊:它 "缓存 "了一个Controller
,因为cache,所以不能成为一个const Stateless
。
那么,什么时候你需要 "缓存 "一个Controller?
如果你使用了GetX的另一个 "不常见 "的特性 Get.create()
Get.create(()=>Controller())
会在每次调用时生成一个新的Controller
Get.find<Controller>()
你可以用它来保存Todo项目的列表,如果小组件被 “重建”,它将保持相同的控制器实例。
GetxService
这个类就像一个 “GetxController”,它共享相同的生命周期(“onInit()”、“onReady()”、“onClose()”)。
但里面没有 “逻辑”。它只是通知GetX的依赖注入系统,这个子类不能从内存中删除。
所以这对保持你的 "服务 "总是可以被Get.find()
获取到并保持运行是超级有用的。比如
ApiService
,StorageService
,CacheService
。
Future<void> main() async {
await initServices(); /// 等待服务初始化.
runApp(SomeApp());
}
/// 在你运行Flutter应用之前,让你的服务初始化是一个明智之举。
因为你可以控制执行流程(也许你需要加载一些主题配置,apiKey,由用户自定义的语言等,所以在运行ApiService之前加载SettingService。
///所以GetMaterialApp()不需要重建,可以直接取值。
void initServices() async {
print('starting services ...');
///这里是你放get_storage、hive、shared_pref初始化的地方。
///或者moor连接,或者其他什么异步的东西。
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!');
}
}
实际删除一个GetxService
的唯一方法是使用Get.reset()
,它就像"热重启 "你的应用程序。
所以如果你需要在你的应用程序的生命周期内对一个类实例进行绝对的持久化,请使用GetxService
。
GetX CLI和插件
GetX 提供了一个cli工具 Get CLI,通过该工具,无论是在服务器上还是在客户端,整个开发过程都可以完全自动化。
例如,Get CLI一个比较实用的功能是可以根据返回json的接口URL自动生成对应的Dart Model类
get generate [ModelName] on [Dir] from "https://api.github.com/users/CpdnCristiano"
此外,为了进一步提高您的生产效率,我们还为您准备了一些插件
- getx_template:一键生成每个页面必需的文件夹、文件、模板代码等等
- GetX Snippets:输入少量字母,自动提示选择后,可生成常用的模板代码
GetX 原理机制
关于 GetX 的原理机制可以参考这篇文章:Flutter GetX深度剖析 该文作者已经写的非常详细了。这里不再赘述。
不过我们可以简单总结一下:
.obs
的写法其实是利用了Dart的扩展语法,内部实现是一个具有callback
以及运算符重载的数据类型- 当读取
.obs
数据类型的value
变量值的时候(get value
),会添加一个观察者进行监听 - 当修改
.obs
数据类型的value
变量值的时候(set value
),则会触发监听回调,内部会获取value变量进一步执行setState()
自动刷新操作
简单来讲它就是 Dart的扩展语法 + 观察者模式 的应用,但是实现这样一个简洁的框架还是不简单的,从其代码中的层层包装和代理转发可见其复杂性。