Flutter Notes | GetX

Official website: https://pub.dev/packages/get
Chinese documentation: https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md

About GetX

Now there are many state management solutions for Flutter, such as redux, bloc, state, provider, and Getx.

Provider is an official state management solution, and its main function is state management. Getx is a third-party state management plug-in. It not only has the function of state management, but also has functions such as routing management, theme management, international multilingual management, Obx partial update, network request, data verification, etc. Compared with other state management plug-ins Getx is simpler , powerful and high performance.

To put it simply, the biggest advantage of GetX over the original and other solutions is that it is easy to use, rich in functions, and provides a family-style functional API from head to toe.

  • GetX is a lightweight and powerful solution on Flutter: high-performance state management, smart dependency injection and convenient routing management.

  • GetX has 3 basic principles:

    • Performance: GetX focuses on performance and minimal resource consumption. The packaged apk size and runtime memory usage of GetX are comparable to other state management plugins.
    • Efficiency: The syntax of GetX is very simple, and it maintains extremely high performance, which can greatly shorten your development time.
    • Structure: GetX can completely decouple the interface, logic, dependencies, and routing, making it cleaner to use, clearer in logic, and easier to maintain in code.
  • GetX is not bloated, but very lightweight. If you only use state management, only the state management module will be compiled, and other things that are not used will not be compiled into your code. It has numerous functions, but these functions are in separate containers, which are not launched until they are used.

  • Getx has a huge ecosystem and is able to run the same code on Android, iOS, Web, Mac, Linux, Windows and your server.
    Through Get Server , you can completely reuse the code you wrote on the front end on your back end.

GetX mainly has three main functions:

  • state management
  • routing management
  • dependency management

Introduce respectively below.

Install

Add Get to your pubspec.yaml file.

dependencies:
  get: ^4.6.5

Import it in the required file and it will be used.

import 'package:get/get.dart';

Set application entry

When we import dependencies, the first step is to replace main.dartthe original in the entry file as the top level, as shown belowMaterialAppGetMaterialApp

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 response state management

For example, using GetX to modify Flutter's default counter sample application requires only three steps:

Step 1: When defining a variable, add .obsthe suffix at the end of the variable

var _counter = 0.obs;

Step 2: Where variables are used, use Obxto wrap

Obx(() => Text("${
      
      _counter}"))

Step 3: When the button is clicked, modify the variable value directly, and the UI will automatically refresh

 onPressed: () => _counter++

That's all, it's as simple as that. It doesn't even setStateneed to be called.

There are three ways to declare a reactive variable:

The first uses 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>({
    
    });

The second is to use Rx, specifying a generic 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>(); // 自定义类 - 可以是任何类

The third, more practical, simpler, and preferable way, is to simply add .obsthe attribute as value. ( recommended )

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;  // 自定义类 - 可以是任何类

For another example, let's define a List:

 RxList _list = ["张三", "李四"].obs;

and then use it:

Obx(() {
    
    
  return Column (
    children: _list.map((v) {
    
     return ListTile(title: Text(v));}).toList(),
  );
}),

Modify it afterwards:

 onPressed: () => _list.add("王五");

Note: ObxThere is a one-to-one correspondence with the corresponding state variables. If there are multiple variables, you can’t just wrap one in the outermost layer Obx. Every place that uses Rx variables needs to be Obxwrapped.

Listen for changes in custom class data

1. Create a Person class and modify member variables in a responsive way

import 'package:get/get.dart';

class Person {
    
     
	RxString name = "Jimi".obs; // rx 变量
	RxInt age = 18.obs;
}

2. Get the class attribute value and change the value

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),
      ),
    );
  }
}

In another way, you can also define the entire class as responsive, for example:

import 'package:get/get.dart';

class Animal {
    
    
  String username;
  int age;
  Animal(this.username,this.age);
}

When creating the above Animalobject, you can directly add .obsthe suffix:

var a = Animal("xiao mao", 2).obs;

still use Obxthe packaging

Obx(() => Text(a.username))

When modifying, you need to modify the pointing of the variable pointer:

onPressed: () => a.value = Animal("小狗", 3)

GetX Routing Management

If you only need to configure the Home page, it MaterialAppis the same as:

GetMaterialApp( // Before: MaterialApp(
  home: MyHome(),
)

If you need to configure the routing table:

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 common route jump method:

method Function
Get.to() jump to new page
Get.toNamed() Named Routing Jump
Get.back() route back
Get.off() Goes to the next page, but has no option to go back to the previous page (for splash screen pages, login pages, etc.)
Get.offAll() Go to the next page and cancel all previous routes
Get.offAndToNamed() Jump and replace the current route page with the next route (can be used to close intermediate pages, such as search pages)
Get.removeRoute() delete a route
Get.until() returns repeatedly until the expression returns true
Get.offUntil() Go to the next route and delete all previous routes until the expression returns true
Get.offNamedUntil() Goes to the next named route and deletes all previous routes until the expression returns true
Get.arguments Get the parameters of the current routing page
Get.previousRoute Get the previous route name
Get.rawRoute Give the original route to visit

Route pass value:

Get.toNamed("/shop", arguments: {
    
    "id":3456});

Receive page:

print(Get.arguments);

Routing intermediate pages:

GetPageThe parameters in the above routing table configuration middlewarescan configure the intermediate page of the routing jump, and some services such as permission judgment and interception can be performed on this page, for example:

// 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: {
    
    });
  }
}

priority

Setting the priority of middleware defines the execution order of Get middleware.

final middlewares = [
  GetMiddleware(priority: 2),
  GetMiddleware(priority: 5),
  GetMiddleware(priority: 4),
  GetMiddleware(priority: -8),
];

These middleware will be executed in this order - 8 => 2 => 4 => 5

Here are GetMiddlewarea few other methods of :

  • onPageCalled: This function will be called when the page is called, before anything is created. You can use this to change something about the page or give it a new page.
GetPage onPageCalled(GetPage page) {
    
    
  final authService = Get.find<AuthService>();
  return page.copyWith(title: 'Welcome ${
      
      authService.UserName}');
}
  • **OnBindingsStart:** This function will be called before the bindings are initialized. Here you can change the bindings for this page.
List<Bindings> onBindingsStart(List<Bindings> bindings) {
    
    
  final authService = Get.find<AuthService>();
  if (authService.isAdmin) {
    
    
    bindings.add(AdminBinding());
  }
  return bindings;
}
  • OnPageBuildStart: This function will be called after binding initialization. Here you can perform some operations after creating bindings and before creating page widgets.
GetPageBuilder onPageBuildStart(GetPageBuilder page) {
    
    
  print('bindings are ready');
  return page;
}
  • OnPageBuilt: This function will be called after GetPage.page is called, and give the result of the function, and get the widget to be displayed.

  • OnPageDispose: This function will be called after disposing all related objects of the page (Controllers, views, …).

GetX dependency management

GetX dependency management is simpler and more convenient than Provideror . InheritedWidgetFor example, take the counter sample application as an example, use dependency management to modify it, and realize the following requirements:

  • Change state on every click
  • State can be shared between different pages
  • Separate business logic from interface

Step 1: Define a Controllerclass

class Controller extends GetxController{
    
    
  var count = 0.obs;
  increment() => count++;
  decrement() => count--;
}

Step 2: Create and use in the pageController

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 // 调用控制器的方法修改计数器的值
      ));
  }
}

ControllerStep 3: Obtain the value of the shared counter in other routing pages

class Other extends StatelessWidget {
    
    
  
  final Controller c = Get.find(); // 通过 Get.find() 找到控制器对象

  
  Widget build(context){
    
    
     return Scaffold(body: Center(child: Text("${
      
      c.count}"))); // 访问控制器的计数变量
  }
}

As you can see, after using GetX dependency management, you may no longer need to use it StatefulWidget. It's so easy, GetX speeds up development and delivers everything on time without sacrificing performance.

To sum up, GetX creates and acquires controllers using the following two lines of code:

Controller controller = Get.put(Controller());  
Controller controller = Get.find();

What we have to do is to extract the data variables scattered in the page and the logic of modifying the data variables into the Controllercontroller.

Yes, it looks like magic, GetX will find your controller and give it to you. You can instantiate a million controllers and GetX will always give you the correct one. Then you can restore the controller data you got later.

Text(controller.textFromApi);

Imagine that you have browsed countless routes, and now you need to get a data that is left in the controller, GetX will automatically find the data you want for your controller, and you don't even need any additional dependencies relation.

See here for more details on dependency management .

GetX Binding

If all pages need to use state management, it would be too troublesome to use the method of creating controller instances in each page, Get.put(MyController())so GetX provides a simpler Binding function, which can be configured globally and uniformly, without the need for separate pages Create them one by one.

Let’s first understand the many Controllermethods of creating instances provided by GetX, which can be selected according to different businesses:

  • Get.put() : A controller instance will also be created without using it
  • Get.lazyPut() : Create an instance in a lazy loading manner, only when it is used
  • Get.putAsync() : an asynchronous version of Get.put()
  • Get.create() : A new instance will be created every time it is used

Let's take a look at how to use the Binding function

Step 1: Create a Bindingclass

import 'package:get/get.dart';

class AllControllerBinding implements Bindings {
    
    
	
	void dependencies() {
    
     
		// 所有的 Controller 都在这里配置
		Get.lazyPut<BindingMyController>(() => BindingMyController()); // 懒加载方式
		Get.lazyPut<BindingHomeController>(() => BindingHomeController());
	}
}

Step 2: GetMaterialAppInitialize the binding in

return GetMaterialApp (
	initialBinding: AllControllerBinding(),
	home: GetXBindingExample(),
);

Get.find<MyController>()Step 3: Get and use the state manager in the page

Obx(() => Text(
	"计数器的值为 ${
      
      Get.find<BindingMyController>().count}",
	style: TextStyle(color: Colors.red, fontSize: 30),
)), 
ElevatedButton(
	onPressed: () {
    
    
		Get.find<BindingMyController>().increment();
	},
	child: Text("点击加1")
),

Routing + Binding + GetView

Another Bindingway to use GetX is to bind it with routing, which can save Get.findsteps.

Step 1: Set the attribute in GetMaterialAppthe routing table of the configurationGetPagebinding

GetPage(
    name: "/shop",
    page: () => const ShopPage(),
    binding: ShopControllerBinding(), // 设置该页面的 Binding 
    middlewares: [ShopMiddleWare()]
),

Contents of it ShopControllerBinding:

import 'package:get/get.dart';

import '../controllers/shop.dart';

class ShopControllerBinding implements Bindings{
    
    
  
  void dependencies() {
    
      
    Get.lazyPut<ShopController>(() => ShopController());
  }
}

ShopControllerContent:

import 'package:get/get.dart';

class ShopController extends GetxController {
    
    
  RxInt counter = 10.obs;
  
  void inc() {
    
    
    counter.value++;
    update();
  }
}

Step 2: ShopControllerInheritance in the used pageGetView

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"))
          ],
        ),
      ),
    );
  }
}

In this way, there is no need to write Get.find, and you can use it directly.

But it should be noted that in this way, when the routing page is popped up, the corresponding Controllerinstance will be destroyed, while other methods will not be destroyed (global sharing).

Controller life cycle

The Controller in GetX has a life cycle, which provides three life cycle methods:

  • onInit():widget Executed immediately when the page component is created from memory, and can be used to perform initialization services, such as page initial data requests .
  • onReady(): Called 1 frame after onInit(), it is an ideal time to insert navigation events, such as snackbar, dialogs, or a new route or asynchronous request.
  • onClose(): Called when the page is destroyed, it can be used to clean up resources or save persistent data.
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 internationalization and theme configuration

globalization

Step 1: Define a language pack

import 'package:get/get.dart';

class Messages extends Translations {
    
    
  
  Map<String, Map<String, String>> get keys => {
    
    
        'zh_CN': {
    
    
          'hello': '你好 世界',
        },
        'de_DE': {
    
    
          'hello': 'Hallo Welt',
        }
      };
}

Step Two: GetMaterialAppConfigure in

  • translations : internationalization configuration file
  • locale : Set the default language, if not set, it will be the current language of the system
  • fallbackLocale : Adds a callback language option in case the language translation specified above does not exist
return GetMaterialApp(
    translations: Messages(), // 你的翻译语言包
    locale: Locale('zh', 'CN'), // 使用指定的语言翻译
    fallbackLocale: Locale('en', 'US'), // 指定的语言翻译不存在时的默认语言回调
);

Step 3: Using translate, whenever append is .trappended to the specified key, the current value of Get.localeand will be used Get.fallbackLocalefor translation.

Text('title'.tr);

change language

Called Get.updateLocale(locale)to update the locale. Then the translation will automatically use the new one locale.

var locale = Locale('en', 'US');
Get.updateLocale(locale);

Get system language

To read the system language, you can use window.locale.

import 'dart:ui' as ui;

return GetMaterialApp(
    locale: ui.window.locale,
);

change theme

Please do not GetMaterialAppupdate the theme with widgets of a higher level than this, which may cause duplication of keys. Many people are used to creating a " ThemeProvider" widget to change the application theme, which is absolutely unnecessary in GetX™ .

You can create your custom theme and simply add it Get.changeThemewithout any templates.

Get.changeTheme(ThemeData.light());

If you want to onTapcreate something like a change theme button in " ", you can combine the two GetX APIs Get.changeTheme+ Get.isDarkModeto achieve it. You can put the following code onPressedin .

Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());

When darkmodeactivated, it will switch to lightthe theme, and when lightthe theme is activated, it will switch to darkthe theme.

Other useful functions

Many calls of GetX do not need to be passed in context, which is very convenient to use.

method Function
Get.defaultDialog() show popup
Get.snackbar() show snackbar
Get.bottomSheet() Show BottomSheet
Get.isSnackbarOpen Check if snackbar is open
Get.isDialogOpen Check if the dialog is open
Get.isBottomSheetOpen Check if bottomsheet is open
Get.changeTheme() Toggle theme colors
Get.isDarkMode Judgment theme style
Get.isAndroid
Get.isIOS
Get.isMacOS
Get.isWindows
Get.isLinux
Get.isFuchsia
Check which platform the application is running on
Get.isMobile
Get.isDesktop
Get.isWeb
Check the device type, note that all platforms support web browsers independently
Get.height
Get.width
equivalent toMediaQuery.of(context).size.height
Get.context provide the current context
Get.contextOverlay Anywhere in your code, provide the snackbar/dialog/bottomsheet context in the foreground

The following methods are contextextensions to context. Because the context is accessible anywhere in your UI, you can use it anywhere in your UI code.

method Function
context.width
context.width
If you need a changeable height/width (like the desktop or browser window can be scaled) you will need to use the context
context.mediaQuerySize() similar toMediaQuery.of(context).size
context.mediaQueryPadding() similar toMediaQuery.of(context).padding
context.mediaQueryViewPadding() similar toMediaQuery.of(context).viewPadding
context.mediaQueryViewInsets() similar toMediaQuery.of(context).viewInsets
context.orientation() similar toMediaQuery.of(context).orientation
context.devicePixelRatio similar toMediaQuery.of(context).devicePixelRatio
context.textScaleFactor similar toMediaQuery.of(context).textScaleFactor
context.mediaQueryShortestSide Query the shortest side of the device.
context.isLandscape() Check if device is in landscape mode
context.isPortrait() Check if device is in portrait mode
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:如果最短边小于300
mobile:如果最短边小于600
tablet:如果最短边(shortestSide)小于1200
desktop:如果宽度大于1200

GetUtils

GetX 还提供了一个方便的工具类 GetUtils,里面提供了一些常用的方法,例如判断值是否为空、是否是数字、是否是视频、图片、音频、PPT、Word、APK、邮箱、手机号码、日期、MD5、SHA1等等。具体可以直接IDE查看 GetUtils类。

在这里插入图片描述

GetConnect

GetConnect可以便捷的通过httpwebsockets进行前后台通信。

默认配置

你能轻松的通过extend GetConnect就能使用GET/POST/PUT/DELETE/SOCKET方法与你的Rest APIwebsockets通信。

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()获取到并保持运行是超级有用的。比如
ApiServiceStorageServiceCacheService

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 原理机制

关于 GetX 的原理机制可以参考这篇文章:Flutter GetX深度剖析 该文作者已经写的非常详细了。这里不再赘述。

不过我们可以简单总结一下:

  • .obs的写法其实是利用了Dart的扩展语法,内部实现是一个具有callback以及运算符重载的数据类型
  • 当读取.obs数据类型的value变量值的时候(get value),会添加一个观察者进行监听
  • 当修改.obs数据类型的value变量值的时候(set value),则会触发监听回调,内部会获取value变量进一步执行 setState()自动刷新操作

简单来讲它就是 Dart的扩展语法 + 观察者模式 的应用,但是实现这样一个简洁的框架还是不简单的,从其代码中的层层包装和代理转发可见其复杂性。

Guess you like

Origin blog.csdn.net/lyabc123456/article/details/131054411