Jonny Borges
Committed by GitHub

Merge pull request #1455 from khangahs/vietnamese_translation

Vietnamese Translation
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
**Ngôn ngữ: Tiếng Việt (file này), [English](README.md), [Indonesian](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md), [French](README-fr.md).**
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
[![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
[![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
[![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](https://communityinviter.com/apps/getxworkspace/getx)
[![Telegram](https://img.shields.io/badge/chat-on%20Telegram-blue.svg)](https://t.me/joinchat/PhdbJRmsZNpAqSLJL6bH7g)
<a href="https://github.com/Solido/awesome-flutter">
<img alt="Awesome Flutter" src="https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true&style=flat-square" />
</a>
<a href="https://www.buymeacoffee.com/jonataslaw" target="_blank"><img src="https://i.imgur.com/aV6DDA7.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important; box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" > </a>
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/getx.png)
- [Về Getx](#about-get)
- [Cài Đặt](#installing)
- [Counter App với GetX](#counter-app-with-getx)
- [Tam Trụ](#the-three-pillars)
- [Quản lý State](#state-management)
- [Quản lý Reactive State](#reactive-state-manager)
- [Thêm thông tin về quản lý state](#more-details-about-state-management)
- [Quản lý route](#route-management)
- [Thêm thông tin về quản lý route](#more-details-about-route-management)
- [Quản lý dependency](#dependency-management)
- [Thêm thông tin về quản lý dependency](#more-details-about-dependency-management)
- [Utils](#utils)
- [Internationalization](#internationalization)
- [Dịch thuật](#translations)
- [Sử dụng bản dịch thuật](#using-translations)
- [Locales](#locales)
- [Change locale](#change-locale)
- [System locale](#system-locale)
- [Đổi Theme](#change-theme)
- [GetConnect](#getconnect)
- [Cấu hình mặc định](#default-configuration)
- [Cấu hình tùy chỉnh](#custom-configuration)
- [GetPage Middleware](#getpage-middleware)
- [Ưu tiên](#priority)
- [Chuyển hướng](#redirect)
- [onPageCalled](#onpagecalled)
- [OnBindingsStart](#onbindingsstart)
- [OnPageBuildStart](#onpagebuildstart)
- [OnPageBuilt](#onpagebuilt)
- [OnPageDispose](#onpagedispose)
- [APIs nâng cao khác](#other-advanced-apis)
- [Cấu hình thủ công và cài đặt chung tùy chọn](#optional-global-settings-and-manual-configurations)
- [Local State Widgets](#local-state-widgets)
- [ValueBuilder](#valuebuilder)
- [ObxValue](#obxvalue)
- [Mẹo hữu ích](#useful-tips)
- [GetView](#getview)
- [GetResponsiveView](#getresponsiveview)
- [Hướng dẫn sử dụng trước khi dùng](#how-to-use-it)
- [GetWidget](#getwidget)
- [GetxService](#getxservice)
- [Thay đổi đột phá 2.0](#breaking-changes-from-20)
- [Tại sao lại dùng GetX](#why-getx)
- [Cộng đồng](#community)
- [Kênh Cộng đồng](#community-channels)
- [Cách cống hiến](#how-to-contribute)
- [Các bài báo và video](#articles-and-videos)
# Về Getx
- GetX hướng tới sự nhỏ gọn và giải pháp tối ưu cho Flutter với tốc độ ưu việt trong quản lý state, nạp dependency thông minh, và quản lý route nhanh chóng và thực tế.
- GetX hướng tới 3 tham vọng chính, nghĩa là tất cả các tài nguyên của thư viện sẽ dành cho những điểm ưu tiên sau: **NĂNG SUẤT, HIỆU SUẤT VÀ TỔ CHỨC.**
- **HIỆU SUẤT:** GetX tập trung vào hiệu suất và mức tiêu thụ tài nguyên tối thiểu, do đó nó không sử dụng Streams hoặc ChangeNotifier.
- **NĂNG SUẤT:** GetX sử dụng một cú pháp dễ dàng và dễ thở. Bất kể bạn muốn làm gì, luôn có một cách dễ dàng hơn với GetX. Nó sẽ tiết kiệm hàng giờ phát triển và sẽ cung cấp hiệu suất tối đa mà ứng dụng của bạn có thể mang lại.
Nói chung, nhà phát triển nên quan tâm đến việc xóa controller ra khỏi memory. Với GetX, các tài nguyên sẽ
TỰ ĐỘNG xóa khỏi memory khi không dùng theo mặc định. Nếu bạn muốn giữ nó trong memory, bạn phải khai báo rõ ràng "permanent: true" trong phần dependency của mình. Từ đó, bạn sẽ tiết kiệm thời gian và ít phụ thuộc vào memory. Theo mặc định, tính năng tải dependency cũng lười biếng.
- **TỔ CHỨC:**
GetX cho phép tách toàn bộ Chế độ xem, presentation logic, business logic, nạp dependencies và điều hướng. Bạn không "context" để điều hướng giữa các route, vì vậy bạn sẽ độc lập trong sơ đồ widget (trực quan hóa). Bạn không cần "context" để truy cập Controller / Blocs của mình thông qua một InheritedWidget, vì vậy bạn hoàn toàn tách rời presentation logic và business logic khỏi lớp trực quan của mình. Bạn không cần phải đưa các Controller / Models / Blocs vào sơ đồ widget của mình thông qua `MultiProvider`, vì GetX sử dụng tính năng nạp dependency của riêng nó, tách hoàn toàn DI khỏi chế độ xem của nó.
Với GetX, bạn biết nơi tìm từng tính năng ứng dụng của mình, với cơ chế clean code theo mặc định. Ngoài việc giúp bảo trì dễ dàng, GetX giúp việc chia sẻ các mô-đun trở thành khả thi trong Flutter.
BLoC là điểm khởi đầu để tổ chức code trong Flutter, nó tách biệt logic nghiệp vụ với trực quan. GetX nảy sinh từ điều này, không chỉ tách biệt presentation logic mà còn cả business logic. Nạp dependency bổ sung và route cũng được tách ra và lớp dữ liệu cũng biến mất. Bạn sẽ biết mọi thứ ở đâu và sẽ hình dung tất cả những điều này dễ hơn cả "Hello World".
GetX là cách dễ nhất, thiết thực và có thể mở rộng để xây dựng các ứng dụng hiệu suất cao với Flutter SDK. GetX chứa đựng một hệ sinh thái rộng lớn xung quanh nó hoạt động hoàn hảo cùng nhau, rất dễ dàng cho người mới bắt đầu và nó chính xác cho các chuyên gia. Nó an toàn, ổn định, cập nhật và cung cấp một loạt các API được tích hợp sẵn mà không có trong Flutter SDK mặc định.
- GetX không cồng kềnh và có vô số tính năng cho phép bạn bắt đầu lập trình mà không cần lo lắng về bất cứ điều gì. Đặc biệt, nó cho phép mỗi tính năng này nằm trong các vùng chứa riêng biệt và chỉ được bắt đầu sau khi sử dụng. Nếu bạn chỉ sử dụng quản lý state thì sẽ chỉ có quản lý state được sử dụng. Nếu bạn chỉ sử dụng route, thì GetX không complie quản lý state.
- GetX có một hệ sinh thái khổng lồ, một cộng đồng lớn, một số lượng lớn cộng tác viên và sẽ được duy trì miễn là Flutter còn tồn tại. GetX có khả năng chạy đồng dạng trên Android, iOS, Web, Mac, Linux, Windows và trên máy chủ của bạn.
**Bạn hoàn toàn có thể sử dụng lại mã của mình trên frontend qua backend với [Get Server](https://github.com/jonataslaw/get_server)**.
**Ngoài ra, toàn bộ quá trình phát triển có thể hoàn toàn tự động, cả trên máy chủ và frontend với [Get CLI](https://github.com/jonataslaw/get_cli)**.
**Ngoài ra, nhằm tăng thêm năng suất của bạn, chúng tôi hỗ trợ
[extension to VSCode](https://marketplace.visualstudio.com/items?itemName=get-snippets.get-snippets)[extension to Android Studio/Intellij](https://plugins.jetbrains.com/plugin/14975-getx-snippets)**
# Cài Đặt
Thêm Get vào pubspec.yaml file:
```yaml
dependencies:
get:
```
Import get vào file cần sử dụng:
```dart
import 'package:get/get.dart';
```
# Counter App with GetX
Dự án "counter" được tạo theo mặc định trên dự án mới trên Flutter có hơn 100 dòng (có comments). Để thể hiện sức mạnh của Get, tôi sẽ trình bày cách tạo "counter" thay đổi trạng thái với mỗi lần nhấp, chuyển đổi giữa các trang và chia sẻ trạng thái giữa các màn hình, tất cả đều theo cách có tổ chức, tách biệt logic nghiệp vụ khỏi chế độ xem, CHỈ VỚI 26 DÒNG!
- Step 1:
Thêm "Get" trước MaterialApp, nó sẽ thành GetMaterialApp
```dart
void main() => runApp(GetMaterialApp(home: Home()));
```
- Chí ú: điều này không sửa đổi MaterialApp của Flutter, GetMaterialApp không phải là MaterialApp được sửa đổi, nó chỉ là một Widget được tạo trước với MaterialApp mặc định là child. Bạn có thể cấu hình điều này theo cách thủ công, nhưng nó chắc chắn là không cần thiết. GetMaterialApp sẽ tạo các route, đưa chúng vào, đưa bản dịch, đưa mọi thứ bạn cần để điều hướng route. Nếu bạn chỉ sử dụng Get để quản lý trạng thái hoặc quản lý phụ thuộc, thì không cần thiết phải sử dụng GetMaterialApp. Tóm lại, GetMaterialApp CHỈ cần thiết cho các route, snacksbar, internationalization, bottomSheets, Dialog và các APIs cấp cao liên quan đến route và không có "context".
- Chí ú²: Một lần nữa, bước này CHỈ cần thiết nếu bạn sử dụng quản lý route (`Get.to ()`, `Get.back () ', v.v.). Nếu bạn không sử dụng nó thì không cần thực hiện bước 1
- Step 2:
Tạo lớp business logic của bạn và đặt tất cả các variables, function và controller bên trong nó.
Bạn có thể làm cho bất kỳ variables nào có thể quan sát được bằng cách sử dụng ".obs" đơn giản.
```dart
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
}
```
- Step 3:
Tạo widget của bạn, sử dụng StatelessWidget và tiết kiệm RAM, với Get, bạn có thể không cần sử dụng StatefulWidget nữa.
```dart
class Home extends StatelessWidget {
@override
Widget build(context) {
// Instantiate your class using Get.put() to make it available for all "child" routes there.
final Controller c = Get.put(Controller());
return Scaffold(
// Use Obx(()=> to update Text() whenever count is changed.
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
// Replace the 8 lines Navigator.push by a simple Get.to(). You don't need context
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 {
// You can ask Get to find a Controller that is being used by another page and redirect you to it.
final Controller c = Get.find();
@override
Widget build(context){
// Access the updated count variable
return Scaffold(body: Center(child: Text("${c.count}")));
}
}
```
Kết quả:
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/counter-app-gif.gif)
Đây là một dự án đơn giản nhưng nó đã cho thấy rõ sức mạnh của Get. Khi dự án của bạn phát triển, sự khác biệt này sẽ trở nên đáng kể hơn.
Get được thiết kế để làm việc với các nhóm, nhưng nó làm cho công việc của một nhà phát triển cá nhân trở nên đơn giản.
Cải thiện thời gian, giao mọi thứ đúng hạn mà không làm giảm hiệu suất. Get không dành cho tất cả mọi người, nhưng nếu bạn đã xác định gắn với Get, Get sẽ "get" bạn!
# Tam Trụ
## Quản lý State
Get has two different state managers: the simple state manager (we'll call it GetBuilder) and the reactive state manager (GetX/Obx)
### Quản lý Reactive State
Lập trình phản ứng (reactive programming) có thể khiến nhiều người xa lánh vì nó được cho là phức tạp. GetX biến lập trình phản ứng thành một thứ khá đơn giản:
- Bạn sẽ không cần tạo StreamControllers.
- Bạn sẽ không cần tạo StreamBuilder cho mỗi biến
- Bạn sẽ không cần tạo một lớp cho mỗi trạng thái.
- Bạn sẽ không cần tạo get cho một giá trị ban đầu.
- Bạn sẽ không cần sử dụng trình tạo mã
Lập trình phản ứng với Get dễ dàng như sử dụng setState.
Hãy tưởng tượng rằng bạn có một biến tên và muốn rằng mỗi khi bạn thay đổi nó, tất cả các widget sử dụng nó sẽ được tự động thay đổi.
Đây là biến đếm của bạn:
```dart
var name = 'Jonatas Borges';
```
Để lắng nghe nó, bạn chỉ cần thêm ".obs" ở cuối:
```dart
var name = 'Jonatas Borges'.obs;
```
Và trong UI, khi bạn muốn hiển thị giá trị đó và cập nhật màn hình bất cứ khi nào giá trị thay đổi, chỉ cần thực hiện điều này:
```dart
Obx(() => Text("${controller.name}"));
```
Thế thôi. Chỉ là _thế_ thôi người ơi~.
### Thêm thông tin về Quản lý state
**Xem thông tin cụ thể tại dây [here](./documentation/en_US/state_management.md). Tại đó, bạn có thể tham khảo ví dụ và so sánh sự khác nhau giữa quản lý state cơ bản và quản lý state reactive**
Bạn sẽ hình dung sức mạnh của GetX.
## Quản lý route
Nếu bạn chỉ sử dụng routes/snackbars/dialogs/bottomsheets không có context, GetX là lựa chọn số 2 trừ 1, nhìn đây:
Thêm "Get" trước MaterialApp, nó sẽ biến thành GetMaterialApp
```dart
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
```
Di chuyển tới màn hình mới:
```dart
Get.to(NextScreen());
```
Di chuyển tới màn hình mới theo tên. Xem thêm tại đây [here](./documentation/en_US/route_management.md#navigation-with-named-routes)
```dart
Get.toNamed('/details');
```
Để đóng snackbars, dialogs, bottomsheets, hay bất kì thứ gì, bạn có thể xài cái này để thay Navigator.pop(context);
```dart
Get.back();
```
Đi đến màn hình kế tiếp và bỏ luôn màn hình cũ (thường dùng cho màn hình giới thiệu, màn hình đăng nhập, etc.)
```dart
Get.off(NextScreen());
```
Đi đến màn hình kế tiếp và đóng tất cả các routes (hữu dụng cho shopping cart, polls, và tests)
```dart
Get.offAll(NextScreen());
```
Bạn có thấy nãy giờ chúng ta không sử dụng từ khóa "context"? Đây chính là thang điểm 9 + 1 của quản lý route ở Get. Với điểm mạnh trên, bạn có thể thao tác bất cứ đâu, kể cả trong controller class.
### Thêm thông tin về quản lý route
**Get works with named routes and also offers lower-level control over your routes! There is in-depth documentation [here](./documentation/en_US/route_management.md)**
## Quản lý dependency
Get hỗ trợ tính năng giúp bạn lấy class như Bloc hoặc Controller chỉ với 1 dòng, khỏi cần Provider context hay InheritedWidget:
```dart
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
```
- Chí ú: Nếu bạn dùng cái này, nhớ đặt attention to thànhe bindings API, which will make it easier to connect your view to your controller.
Thay vì khởi tạo class của bạn trong class bạn đang sử dụng, bạn đang khởi tạo nó trong phiên bản Get, điều này sẽ làm cho nó có sẵn trên toàn bộ Ứng dụng của bạn.
Vì vậy, bạn có thể sử dụng bộ điều khiển (hoặc Bloc) của mình một cách bình thường
**Mẹo:** Nhận quản lý dependency được tách ra khỏi các phần khác của package, vì vậy, ví dụ: nếu ứng dụng của bạn đã sử dụng trình quản lý trạng thái (bất kỳ cái nào, không quan trọng), bạn không cần phải viết lại tất cả, bạn có thể sử dụng điều này nạp dependency vô lo
```dart
controller.fetchApi();
```
Hãy tưởng tượng rằng bạn đã điều hướng qua nhiều route và bạn cần dữ liệu bị còn sót trong controller của mình, bạn sẽ cần một trình quản lý dependency kết hợp với Provider hoặc Get_it, đúng hơm? Với Get, sử dụng Get to "find" cho controller, bạn sẽ hoàn toàn độc lập:
```dart
Controller controller = Get.find();
//Yes, it looks like Magic, Get will find your controller, and will deliver it to you. You can have 1 million controllers instantiated, Get will always give you the right controller.
```
Và sau đó, bạn sẽ có thể khôi phục dữ liệu controller của mình đã lấy được ở đó:
```dart
Text(controller.textFromApi);
```
### Thêm thông tin về quản lý dependency
**Xem thêm tại đây [here](./documentation/en_US/dependency_management.md)**
# Utils
## Internationalization
### Dịch thuật
Các bản dịch được lưu giữ như một bản đồ từ điển key-value đơn giản.
Để thêm các bản dịch tùy chỉnh, hãy tạo một class và extends `Translation`.
```dart
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',
}
};
}
```
#### Sử dụng bản dịch thuật
Chỉ cần append `.tr` vào key được chỉ định và nó sẽ được dịch, sử dụng giá trị hiện tại của` Get.locale` và `Get.fallbackLocale`.
```dart
Text('title'.tr);
```
#### Sử dụng bản dịch thuật với số ít và số nhiều
```dart
var products = [];
Text('singularKey'.trPlural('pluralKey', products.length, Args));
```
#### Sử dụng bản dịch với tham số (parameters)
```dart
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'
}));
```
### Locales
Chuyển các tham số (parameters) cho `GetMaterialApp` để xác định ngôn ngữ và bản dịch.
```dart
return GetMaterialApp(
translations: Messages(), // your translations
locale: Locale('en', 'US'), // translations will be displayed in that locale
fallbackLocale: Locale('en', 'UK'), // specify the fallback locale in case an invalid locale is selected.
);
```
#### Đổi locale
Gọi `Get.updateLocale (locale)` 'để cập nhật ngôn ngữ. Các bản dịch sau đó sẽ tự động sử dụng ngôn ngữ mới.
```dart
var locale = Locale('en', 'US');
Get.updateLocale(locale);
```
#### System locale
Để đọc system locale, sử dụng `Get.deviceLocale`.
```dart
return GetMaterialApp(
locale: Get.deviceLocale,
);
```
## Đổi Theme
Vui lòng không sử dụng bất kỳ Widget con nào cấp cao hơn `GetMaterialApp` để cập nhật nó. Điều này có thể kích hoạt các key trùng lặp. Rất nhiều người đã quen với cách tiếp cận thời tiền sử là tạo tiện ích "ThemeProvider" chỉ để thay đổi chủ đề ứng dụng của bạn và điều này KHÔNG cần thiết với ** GetX ™ **.
Bạn có thể tạo chủ đề tùy chỉnh của mình và chỉ cần thêm nó vào trong `Get.changeTheme` mà không cần bất kỳ điều gì khác:
```dart
Get.changeTheme(ThemeData.light());
```
Nếu bạn muốn tạo một cái gì đó giống như một nút thay đổi Theme với `onTap`, bạn có thể kết hợp hai API ** GetX ™ ** cho điều đó:
- Api kiểm tra xem `Theme` tối có đang được sử dụng hay không.
-`Theme` sẽ thay đổi API, bạn sử dụng với `onPressed`:
```dart
Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
```
Khi bật `.darkmode`, nó sẽ chuyển _light theme_, và khi bật _light theme_ , nó sẽ chuyển _dark theme_.
## GetConnect
GetConnect tạo giao thức tới http hoặc websockets
### Cấu hình mặc định
Đơn giản hóa các lệnh GET/POST/PUT/DELETE/SOCKET khi giao tiếp Rest API hoặc websockets.
```dart
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');
}
}
```
### Tùy chỉnh
GetConnect có khả năng tùy chỉnh cao Bạn có thể xác định Url chính như answers, modifiers như request, xác địng authenticator và thậm chí số lần thử mà nó sẽ cố gắng authenticate, ngoài việc cung cấp khả năng xác định bộ giải mã chuẩn sẽ chuyển đổi tất cả các request của bạn thành Model mà không cần bất kỳ cấu hình bổ sung nào.
```dart
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);
}
```
## GetPage Middleware
GetPage hiện có thuộc tính mới lấy danh sách GetMiddleWare và chạy chúng theo thứ tự cụ thể.
**Chí ú**: Khi GetPage có Middleware (phần trung gian), tất cả các children của trang này sẽ tự động có cùng middlewares.
### Ưu tiên
Thứ tự ưu tiên của Middlewares có thể đặt như sau trong GetMiddleware.
```dart
final middlewares = [
GetMiddleware(priority: 2),
GetMiddleware(priority: 5),
GetMiddleware(priority: 4),
GetMiddleware(priority: -8),
];
```
và chúng sẽ chạy như thế này **-8 => 2 => 4 => 5**
### Chuyển hướng
Khi bạn muốn tìm kiếm trang của route được gọi, function (hàm) sẽ khởi động. Kết quả là phải có RouteSettings để chuyển hướng đến. Hoặc cung cấp cho nó null và chuyển hướng không xảy ra.
```dart
RouteSettings redirect(String route) {
final authService = Get.find<AuthService>();
return authService.authed.value ? null : RouteSettings(name: '/login')
}
```
### onPageCalled
Khi bạn gọi một trang trước mọi thứ được tạo, hàm này sẽ khởi động và
bạn có thể sử dụng nó để thay đổi điều gì đó về trang hoặc tạo cho nó một trang mới
```dart
GetPage onPageCalled(GetPage page) {
final authService = Get.find<AuthService>();
return page.copyWith(title: 'Welcome ${authService.UserName}');
}
```
### OnBindingsStart
Function này sẽ khởi động trước khi Bindinds diễn ra và bạn có thể thay đổi Bindings cho trang này.
```dart
List<Bindings> onBindingsStart(List<Bindings> bindings) {
final authService = Get.find<AuthService>();
if (authService.isAdmin) {
bindings.add(AdminBinding());
}
return bindings;
}
```
### OnPageBuildStart
Function này sẽ khởi động sau khi Bindings diễn ra. Ở đây, bạn có thể làm thứ gì đó sau khi bạn tạo Bindings và trước khi tạo trange widget.
```dart
GetPageBuilder onPageBuildStart(GetPageBuilder page) {
print('bindings are ready');
return page;
}
```
### OnPageBuilt
Function này sẽ khởi động ngay sau khi GetPage.page được gọi và sẽ cho bạn kết quả của function và lấy widget được hiển thị.
### OnPageDispose
Function này sẽ khởi động ngay sau khi hủy bỏ tất cả các đối tượng liên quan (Controller, views, ...) của trang.
## APIs nâng cao khác
```dart
// give the current args from currentScreen
Get.arguments
// give name of previous route
Get.previousRoute
// give the raw route to access for example, rawRoute.isFirst()
Get.rawRoute
// give access to Routing API from GetObserver
Get.routing
// check if snackbar is open
Get.isSnackbarOpen
// check if dialog is open
Get.isDialogOpen
// check if bottomsheet is open
Get.isBottomSheetOpen
// remove one route.
Get.removeRoute()
// back repeatedly until the predicate returns true.
Get.until()
// go to next route and remove all the previous routes until the predicate returns true.
Get.offUntil()
// go to next named route and remove all the previous routes until the predicate returns true.
Get.offNamedUntil()
//Check in what platform the app is running
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia
//Check the device type
GetPlatform.isMobile
GetPlatform.isDesktop
//All platforms are supported independently in web!
//You can tell if you are running inside a browser
//on Windows, iOS, OSX, Android, etc.
GetPlatform.isWeb
// Equivalent to : MediaQuery.of(context).size.height,
// but immutable.
Get.height
Get.width
// Gives the current context of the Navigator.
Get.context
// Gives the context of the snackbar/dialog/bottomsheet in the foreground, anywhere in your code.
Get.contextOverlay
// Chí ú: Nếu bạn dùng cái này, nhớ đặtt. thànhnce you
// have access to context in any place of your UI, you can use it anywhere in the UI code
// If you need a changeable height/width (like Desktop or browser windows that can be scaled) you will need to use context.
context.width
context.height
// Gives you the power to define half the screen, a third of it and so on.
// Useful for responsive applications.
// param dividedBy (double) optional - default: 1
// param reducedBy (double) optional - default: 0
context.heightTransformer()
context.widthTransformer()
/// Similar to MediaQuery.of(context).size
context.mediaQuerySize()
/// Similar to MediaQuery.of(context).padding
context.mediaQueryPadding()
/// Similar to MediaQuery.of(context).viewPadding
context.mediaQueryViewPadding()
/// Similar to MediaQuery.of(context).viewInsets;
context.mediaQueryViewInsets()
/// Similar to MediaQuery.of(context).orientation;
context.orientation()
/// Check if device is on landscape mode
context.isLandscape()
/// Check if device is on portrait mode
context.isPortrait()
/// Similar to MediaQuery.of(context).devicePixelRatio;
context.devicePixelRatio()
/// Similar to MediaQuery.of(context).textScaleFactor;
context.textScaleFactor()
/// Get the shortestSide from screen
context.mediaQueryShortestSide()
/// True if width be larger than 800
context.showNavbar()
/// True if the shortestSide is smaller than 600p
context.isPhone()
/// True if the shortestSide is largest than 600p
context.isSmallTablet()
/// True if the shortestSide is largest than 720p
context.isLargeTablet()
/// True if the current device is Tablet
context.isTablet()
/// Returns a value<T> according to the screen size
/// can give value for:
/// watch: if the shortestSide is smaller than 300
/// mobile: if the shortestSide is smaller than 600
/// tablet: if the shortestSide is smaller than 1200
/// desktop: if width is largest than 1200
context.responsiveValue<T>()
```
### Cấu hình thủ công và cài đặt chung tùy chọn
GetMaterialApp configures everything for you, but if you want to configure Get manually.
```dart
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [GetObserver()],
);
```
You will also be able to use your own Middleware within `GetObserver`, this will not influence anything.
```dart
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer) // Here
],
);
```
You can create _Global Settings_ for `Get`. Just add `Get.config` to your code before pushing any route.
Or do it directly in your `GetMaterialApp`
```dart
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
)
```
You can optionally redirect all the logging messages from `Get`.
If you want to use your own, favourite logging package,
and want to capture the logs there:
```dart
GetMaterialApp(
enableLog: true,
logWriterCallback: localLogWriter,
);
void localLogWriter(String text, {bool isError = false}) {
// pass the message to your favourite logging package here
// please note that even if enableLog: false log messages will be pushed in this callback
// you get check the flag if you want through GetConfig.isLogEnable
}
```
### Local State Widgets
Các Widget này cho phép bạn quản lý một giá trị duy nhất và giữ trạng thái tạm thời và cục bộ.
Chúng tôi có các hướng đi cho Reactive và Simple.
Ví dụ: bạn có thể sử dụng chúng để chuyển đổi văn bản tối nghĩa trong một `TextField`, có thể tạo một widget
Expandable Panel tùy chỉnh hoặc có thể sửa đổi chỉ mục hiện tại trong `BottomNavigationBar` trong khi thay đổi nội dung
bên trong một `Scaffold`.
#### ValueBuilder
Đơn giản hóa của `StatefulWidget` hoạt động với lệnh gọi lại` .setState` nhận giá trị cập nhật.
```dart
ValueBuilder<bool>(
initialValue: false,
builder: (value, updateFn) => Switch(
value: value,
onChanged: updateFn, // same signature! you could use ( newValue ) => updateFn( newValue )
),
// if you need to call something outside the builder method.
onUpdate: (value) => print("Value updated: $value"),
onDispose: () => print("Widget unmounted"),
),
```
#### ObxValue
Tương tự như [`ValueBuilder`] (# valuebuilder), nhưng đây là phiên bản Reactive, bạn kèm một lệnh Rx (nhớ cái .obs không?) và nó cập nhật tự động ... hay chưa?
```dart
ObxValue((data) => Switch(
value: data.value,
onChanged: data, // Rx has a _callable_ function! You could use (flag) => data.value = flag,
),
false.obs,
),
```
## Mẹo hữu ích
`.obs` observable (variable có thể quan sát được) (còn được gọi là loại _Rx_) có nhiều phương thức và toán tử bên trong.
> Is very common to _believe_ that a property with `.obs` **IS** the actual value... but make no mistake!
> We avoid the Type declaration of the variable, because Dart's compiler is smart enough, and the code
> looks cleaner, but:
```dart
var message = 'Xin Chào'.obs;
print( 'Message "$message" has Type ${message.runtimeType}');
```
Ngay cả khi `message` _prints_ giá trị String, Loại là ** RxString **!
Vì vậy, bạn không thể thực hiện `message.substring (0, 4) '. Bạn phải truy cập vào `value`thực bên trong _observable_: Cách được sử dụng nhiều nhất là`.value`, nhưng, bạn có biết rằng bạn cũng có thể sử dụng ...
```dart
final name = 'GetX'.obs;
// only "updates" the stream, if the value is different from the current one.
name.value = 'Hey';
// All Rx properties are "callable" and returns the new value.
// but this approach does not accepts `null`, the UI will not rebuild.
name('Hello');
// is like a getter, prints 'Hello'.
name() ;
/// numbers:
final count = 0.obs;
// You can use all non mutable operations from num primitives!
count + 1;
// Watch out! this is only valid if `count` is not final, but var
count += 1;
// You can also compare against values:
count > 2;
/// booleans:
final flag = false.obs;
// switches the value between true/false
flag.toggle();
/// all types:
// Sets the `value` to null.
flag.nil();
// All toString(), toJson() operations are passed down to the `value`
print( count ); // calls `toString()` inside for RxInt
final abc = [0,1,2].obs;
// Converts the value to a json Array, prints RxList
// Json is supported by all Rx types!
print('json: ${jsonEncode(abc)}, type: ${abc.runtimeType}');
// RxMap, RxList and RxSet are special Rx types, that extends their native types.
// but you can work with a List as a regular list, although is reactive!
abc.add(12); // pushes 12 to the list, and UPDATES the stream.
abc[3]; // like Lists, reads the index 3.
// equality works with the Rx and the value, but hashCode is always taken from the value
final number = 12.obs;
print( number == 12 ); // prints > true
/// Custom Rx Models:
// toJson(), toString() are deferred to the child, so you can implement override on them, and print() the observable directly.
class User {
String name, last;
int age;
User({this.name, this.last, this.age});
@override
String toString() => '$name $last, $age years old';
}
final user = User(name: 'Khang', last: 'Huỳnh', age: 33).obs;
// `user` is "reactive", but the properties inside ARE NOT!
// So, if we change some variable inside of it...
user.value.name = 'Kaiser';
// The widget will not rebuild!,
// `Rx` don't have any clue when you change something inside user.
// So, for custom classes, we need to manually "notify" the change.
user.refresh();
// or we can use the `update()` method!
user.update((value){
value.name='Kaiser';
});
print( user );
```
## StateMixin
Một cách khác để xử lý trạng thái `UI` của bạn là sử dụng`StateMixin <T>`.
Để triển khai nó, hãy sử dụng dấu `with` để thêm`StateMixin <T>` bộ điều khiển của bạn cho phép tích hợp kèm mô hình T.
```dart
class Controller extends GetController with StateMixin<User>{}
```
Phương thức `change ()` thay đổi trạng thái bất cứ khi nào chúng ta muốn.
Chỉ cần chuyển dữ liệu và trạng thái theo cách này:
```dart
change(data, status: RxStatus.success());
```
RxStatus allow these status:
```dart
RxStatus.loading();
RxStatus.success();
RxStatus.empty();
RxStatus.error('message');
```
To represent it in the UI, use:
```dart
class OtherClass extends GetView<Controller> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: controller.obx(
(state)=>Text(state.name),
// here you can put your custom loading indicator, but
// by default would be Center(child:CircularProgressIndicator())
onLoading: CustomLoadingIndicator(),
onEmpty: Text('No data found'),
// here also you can set your own error widget, but by
// default will be an Center(child:Text(error))
onError: (error)=>Text(error),
),
);
}
```
#### GetView
Widget này là bảo bối của Getx, rất đơn giản, nhưng rất hữu ích!
Là một Widget `const Stateless` có getter` controller` cho một `Controller` đã đăng ký, chỉ vậy thôi người ơi~.
```dart
class AwesomeController extends GetController {
final String title = 'My Awesome View';
}
// ALWAYS remember to pass the `Type` you used to register your controller!
class AwesomeView extends GetView<AwesomeController> {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Text(controller.title), // just call `controller.something`
);
}
}
```
#### GetResponsiveView
Mở rộng tiện ích này để xây dựng chế độ responsive.
Ưidget này chứa thuộc tính `screen` có tất cả
thông tin về kích thước và loại màn hình.
##### Hướng dẫn sử dụng trước khi dùng
Bạn có hai lựa chọn để xây dựng nó.
- với phương thức `builder` bạn trả về tiện ích con để xây dựng.
- với các phương thức `desktop`,` tablet`, `phone`,` watch`. cụ thể, các phương thức này sẽ tạo các loại màn hình khớp với ngữ cảnh khi màn hình là [ScreenType.Tablet] thì phương thức `tablet` sẽ được tạo ra và cứ như vậy.
**Chí ú:** Nếu bạn dùng cái này, nhớ đặt `alwaysUseBuilder` thành `false`
Với `settings` property bạn có thể đặt chiều dài tối thiểu cho các loại màn hình.
![example](https://github.com/SchabanBo/get_page_example/blob/master/docs/Example.gif?raw=true)
Code to this screen
[code](https://github.com/SchabanBo/get_page_example/blob/master/lib/pages/responsive_example/responsive_view.dart)
#### GetWidget
Hầu hết mọi người không biết gì về Widget này, hoặc hoàn toàn nhầm lẫn về cách sử dụng nó.
Trường hợp sử dụng rất hiếm, nhưng rất cụ thể: Nó `caches` một Bộ điều khiển.
Bởi vì _cache_ không thể là một `const Stateless`.
> So, when do you need to "cache" a Controller?
Nếu sử dụng, bạn sẽ dùng cái này **GetX**: `Get.create()`.
`Get.create(()=>Controller())` sẽ tạo một `Controller` với mỗi lần gọi `Get.find<Controller>()`,
Đó là nơi mà `` GetWidget` tỏa sáng ... chẳng hạn như bạn có thể sử dụng nó, để giữ một danh sách các mục Todo. Vì vậy, nếu widget được "xây dựng lại", nó sẽ giữ nguyên phiên bản controller.
#### GetxService
Class này giống như một `GetxController`, nó chia sẻ cùng một vòng đời (`onInit ()`,`onReady ()`,`onClose ()`).
Nhưng không có "logic" bên trong của nó. Nó chỉ thông báo cho ** GetX ** Hệ thống Nạp Dependency rằng class con này ** không thể ** bị xóa khỏi bộ nhớ.
Vì vậy, rất hữu ích để giữ cho "Service" của bạn luôn có thể truy cập và hoạt động với `Get.find ()`. Giống:
`ApiService`,` StorageService`, `CacheService`.
```dart
Future<void> main() async {
await initServices(); /// AWAIT SERVICES INITIALIZATION.
runApp(SomeApp());
}
/// Is a smart move to make your Services intiialize before you run the Flutter app.
/// as you can control the execution flow (maybe you need to load some Theme configuration,
/// apiKey, language defined by the User... so load SettingService before running ApiService.
/// so GetMaterialApp() doesnt have to rebuild, and takes the values directly.
void initServices() async {
print('starting services ...');
/// Here is where you put get_storage, hive, shared_pref initialization.
/// or moor connection, or whatever that's async.
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!');
}
}
```
Cách duy nhất để thực sự xóa một `GetxService`, là với`Get.reset ()`giống như cách thức "Khởi động nóng" ứng dụng của bạn. Vì vậy, hãy nhớ rằng, nếu bạn cần sự tồn tại tuyệt đối của một class trong vòng đời tồn tại của nó trong ứng dụng của bạn, hãy sử dụng `GetxService`.
# Thay đổi đột phá 2.0
1- Rx types:
| Before | After |
| ------- | ---------- |
| StringX | `RxString` |
| IntX | `RxInt` |
| MapX | `RxMap` |
| ListX | `RxList` |
| NumX | `RxNum` |
| DoubleX | `RxDouble` |
RxController và GetBuilder bây giờ đã hợp nhất, bạn không cần phải ghi nhớ bộ điều khiển nào bạn muốn sử dụng, chỉ cần sử dụng GetxController, nó sẽ hoạt động để quản lý trạng thái đơn giản và reactive.
2- NamedRoutes
Before:
```dart
GetMaterialApp(
namedRoutes: {
'/': GetRoute(page: Home()),
}
)
```
Now:
```dart
GetMaterialApp(
getPages: [
GetPage(name: '/', page: () => Home()),
]
)
```
Tại sao lại thay đổi?
Thông thường, có thể cần phải quyết định trang nào sẽ được hiển thị từ một tham số hoặc mã thông báo đăng nhập, cách tiếp cận trước đây không linh hoạt, vì nó không cho phép điều này.
Việc chèn trang vào một hàm đã làm giảm đáng kể mức tiêu thụ RAM, vì các tuyến sẽ không được cấp phát trong bộ nhớ kể từ khi ứng dụng được khởi động và nó cũng cho phép thực hiện kiểu tiếp cận này:
```dart
GetStorage box = GetStorage();
GetMaterialApp(
getPages: [
GetPage(name: '/', page:(){
return box.hasData('token') ? Home() : Login();
})
]
)
```
# Tại sao lại dùng GetX
1- Nhiều lần sau khi cập nhật Flutter, nhiều package của bạn sẽ bị hỏng. Đôi khi lỗi biên dịch code xảy ra, lỗi thường xuất hiện mà vẫn không có câu trả lời; và, chúng ta, nhà phát triển, cần biết lỗi đến từ đâu, theo dõi lỗi, chỉ sau đó cố gắng mở một vấn đề trong kho lưu trữ tương ứng và xem vấn đề của nó đã được giải quyết. Tập trung các tài nguyên chính để phát triển (Quản lý state, dependency và route), cho phép bạn thêm một gói duy nhất vào pubspec của mình và bắt đầu hoạt động. Sau khi cập nhật Flutter, điều duy nhất bạn cần làm là cập nhật Get dependency và bắt đầu làm việc. Get cũng giải quyết các vấn đề tương thích. Có bao nhiêu lần một phiên bản của một gói không tương thích với phiên bản của gói khác, vì một gói sử dụng phần phụ thuộc trong một phiên bản và gói kia trong phiên bản khác? Đây cũng không phải là vấn đề đáng lo ngại khi sử dụng Get, vì mọi thứ đều nằm trong cùng một gói và hoàn toàn tương thích.
2- Flutter dễ học, Flutter siêu phàm, nhưng Flutter vẫn có một số bản soạn sẵn có thể không mong muốn đối với hầu hết các nhà phát triển, chẳng hạn như `Navigator.of (context) .push (context, builder [...] '. Get đơn giản hóa việc phát triển. Thay vì viết 8 dòng mã chỉ để gọi một tuyến đường, bạn chỉ cần làm điều đó: `` Get.to (Home ()) 'và bạn đã hoàn tất, bạn sẽ chuyển sang trang tiếp theo. Các url web động là một điều thực sự khó khăn để làm với Flutter hiện tại và điều đó với GetX đơn giản đến đần độn :). Quản lý trạng thái trong Flutter và quản lý các phần phụ thuộc cũng là điều tạo ra nhiều cuộc thảo luận, vì có hàng trăm mẫu trong pub (Dart package website). Nhưng không có gì dễ dàng bằng việc thêm ".obs" ở cuối biến của bạn, và đặt tiện ích của bạn bên trong Obx, và thế là xong, tất cả các cập nhật cho biến đó sẽ được tự động cập nhật trên màn hình.
3- Dễ dàng mà không phải lo lắng về hiệu suất. Hiệu suất của Flutter đã đáng kinh ngạc rồi, nhưng hãy tưởng tượng rằng bạn sử dụng trình quản lý state và trình định vị để phân phối các Blocs / stores / controllers / v.v. của bạn. Bạn sẽ phải gọi thủ công loại trừ sự phụ thuộc khi bạn không cần đến chúng. Nhưng bạn đã bao giờ nghĩ chỉ cần sử dụng bộ điều khiển của mình và khi nó không còn được ai sử dụng nữa, nó sẽ đơn giản được xóa khỏi bộ nhớ? Đó là những gì GetX làm. Với SmartManagement, mọi thứ không được sử dụng sẽ được xóa khỏi bộ nhớ và bạn không phải lo lắng về bất cứ điều gì ngoài lập trình. Bạn sẽ được đảm bảo rằng bạn đang sử dụng các nguồn tài nguyên cần thiết tối thiểu mà thậm chí không cần tạo ra một logic nào cho việc này.
4- Tách khỏi thực tế. Bạn có thể đã nghe đến khái niệm "tách khung nhìn (view) khỏi business logic". Đây không phải là đặc thù của BLoC, MVC, MVVM và bất kỳ tiêu chuẩn nào khác trên thị trường đều có khái niệm này. Tuy nhiên, khái niệm này thường có thể được giảm thiểu trong Flutter do việc sử dụng ngữ cảnh (context).
Nếu bạn cần ngữ cảnh để tìm một InheritedWidget, bạn cần nó trong dạng xem hoặc chuyển ngữ cảnh theo tham số. Tôi đặc biệt thấy giải pháp này rất chán đời; hơn nữa, để làm việc theo nhóm, chúng tôi sẽ luôn phụ thuộc vào business logic của View. Getx không chính thống với cách tiếp cận tiêu chuẩn và mặc dù nó không cấm hoàn toàn việc sử dụng StatefulWidgets, InitState, v.v., nhưng nó luôn có một cách tiếp cận tương tự có thể rõ ràng hơn. Controller có vòng đời và khi bạn cần thực hiện yêu cầu APIREST chẳng hạn, bạn độc lập với View. Bạn có thể sử dụng onInit để bắt đầu cuộc gọi http và khi dữ liệu đến, các biến sẽ được điền. Vì GetX hoạt động hoàn toàn reactive (đó là sự thực và hoạt động theo luồng), khi các mục được lấp đầy, tất cả tiện ích con sử dụng biến đó sẽ được cập nhật tự động trong View. Điều này cho phép những người có chuyên môn về UI chỉ làm việc với các widget và không phải gửi bất kỳ thứ gì đến logic nghiệp vụ ngoài các sự kiện của người dùng (như nhấp vào nút), trong khi những người làm việc với logic nghiệp vụ sẽ được tự do tạo và kiểm tra logic nghiệp vụ riêng.
Thư viện này sẽ luôn được cập nhật và triển khai các tính năng mới. Hãy thoải mái đưa ra các bài PR và đóng góp cho chúng.
# Cộng đồng
## Kênh Cộng đồng
GetX có một cộng đồng rất tích cực và hữu ích. Nếu bạn có thắc mắc hoặc muốn được hỗ trợ về việc sử dụng khuôn khổ này, vui lòng tham gia các kênh cộng đồng của chúng tôi, câu hỏi của bạn sẽ được trả lời nhanh hơn và đó sẽ là nơi phù hợp nhất. Kho lưu trữ này dành riêng cho các vấn đề mở và yêu cầu tài nguyên, nhưng hãy thoải mái trở thành một phần của Cộng đồng GetX.
| **Slack** | **Discord** | **Telegram** |
| :-------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
| [![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](https://communityinviter.com/apps/getxworkspace/getx) | [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) | [![Telegram](https://img.shields.io/badge/chat-on%20Telegram-blue.svg)](https://t.me/joinchat/PhdbJRmsZNpAqSLJL6bH7g) |
## Cách cống hiến
_Bạn muốn đóng góp cho dự án? Chúng tôi sẽ tự hào giới thiệu bạn với tư cách là một trong những cộng tác viên của chúng tôi. Dưới đây là một số điểm mà bạn có thể đóng góp và làm cho Get (và Flutter) tốt hơn nữa._
- Giúp dịch readme sang các ngôn ngữ khác.
- Thêm tài liệu vào readme (rất nhiều chức năng của Get chưa được tài liệu hóa).
- Viết bài hoặc làm video dạy cách sử dụng Get (chúng sẽ được chèn vào Readme và trong tương lai trong Wiki của chúng tôi).
- Đưa ra các PR cho mã / bài kiểm tra.
- Bao gồm các chức năng mới.
Mọi đóng góp đều được hoan nghênh!
## Các bài báo và video
- [Flutter Getx EcoSystem package for arabic people](https://www.youtube.com/playlist?list=PLV1fXIAyjeuZ6M8m56zajMUwu4uE3-SL0) - Hướng dẫn bởi [Pesa Coder](https://github.com/UsamaElgendy).
- [Dynamic Themes in 3 lines using GetX™](https://medium.com/swlh/flutter-dynamic-themes-in-3-lines-c3b375f292e3) - Hướng dẫn bởi [Rod Brown](https://github.com/RodBr).
- [Complete GetX™ Navigation](https://www.youtube.com/watch?v=RaqPIoJSTtI) - Quản lý route bởi Amateur Coder.
- [Complete GetX State Management](https://www.youtube.com/watch?v=CNpXbeI_slw) - Quản lý State video by Amateur Coder.
- [GetX™ Other Features](https://youtu.be/ttQtlX_Q0eU) - Utils, storage, bindings and other features video by Amateur Coder.
- [Firestore User with GetX | Todo App](https://www.youtube.com/watch?v=BiV0DcXgk58) - Video by Amateur Coder.
- [Firebase Auth with GetX | Todo App](https://www.youtube.com/watch?v=-H-T_BSgfOE) - Video by Amateur Coder.
- [The Flutter GetX™ Ecosystem ~ State Management](https://medium.com/flutter-community/the-flutter-getx-ecosystem-state-management-881c7235511d) - Quản lý State by [Aachman Garg](https://github.com/imaachman).
- [The Flutter GetX™ Ecosystem ~ Dependency Injection](https://medium.com/flutter-community/the-flutter-getx-ecosystem-dependency-injection-8e763d0ec6b9) - Nạp dependency by [Aachman Garg](https://github.com/imaachman).
- [GetX, the all-in-one Flutter package](https://www.youtube.com/watch?v=IYQgtu9TM74) - Giới thiệu sơ lược về quản lý State and Navigation by Thad Carnevalli.
- [Build a To-do List App from scratch using Flutter and GetX](https://www.youtube.com/watch?v=EcnqFasHf18) - UI + State Management + Storage video by Thad Carnevalli.
- [GetX Flutter Firebase Auth Example](https://medium.com/@jeffmcmorris/getx-flutter-firebase-auth-example-b383c1dd1de2) - Article by Jeff McMorris.
- [Flutter State Management with GetX – Complete App](https://www.appwithflutter.com/flutter-state-management-with-getx/) - by App With Flutter.
- [Flutter Routing with Animation using Get Package](https://www.appwithflutter.com/flutter-routing-using-get-package/) - by App With Flutter.
- [A minimal example on dartpad](https://dartpad.dev/2b3d0d6f9d4e312c5fdbefc414c1727e?) - by [Roi Peker](https://github.com/roipeker)
... ...
# Quản lý dependency
- [Quản lý dependency](#dependency-management)
- [Instancing methods](#instancing-methods)
- [Get.put()](#getput)
- [Get.lazyPut](#getlazyput)
- [Get.putAsync](#getputasync)
- [Get.create](#getcreate)
- [Sử dụng các phương thức / class](#using-instantiated-methodsclasses)
- [Khác nhau giữa phương thức (methods)](#differences-between-methods)
- [Bindings](#bindings)
- [Cách sử dụng](#how-to-use)
- [BindingsBuilder](#bindingsbuilder)
- [SmartManagement](#smartmanagement)
- [Cách thay đổi](#How-to-change)
- [SmartManagement.full](#smartmanagementfull)
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
- [Cách bindings làm việc ngầm](#how-bindings-work-under-the-hood)
- [Chí ú](#notes)
Get có một trình quản lý dependency đơn giản và mạnh mẽ cho phép bạn truy xuất cùng một class với Blocs hoặc Controller của bạn chỉ với 1 dòng mã, không có "context", không có InheritedWidget
```dart
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
```
Thay vì khởi tạo class của bạn trong class bạn đang sử dụng, bạn đang khởi tạo nó trong phiên bản Get, điều này sẽ làm cho nó có sẵn trên toàn bộ Ứng dụng của bạn.
Vì vậy, bạn có thể sử dụng controller (hoặc class Blocs) của mình một cách bình thường
- Note: Nếu bạn đang sử dụng Get's State Manager, hãy chú ý hơn đến [Bindings](#bindings) api, điều này sẽ giúp kết nối chế độ xem với controller của bạn dễ dàng hơn.
- Note²: Quản lý state của Get được tách biệt khỏi các phần khác của gói, vì vậy, nếu ví dụ: nếu ứng dụng của bạn đã sử dụng trình quản lý state (bất kỳ cái nào, không quan trọng), bạn không cần phải thay đổi điều đó, bạn có thể sử dụng phần dependency này người quản lý không có vấn đề gì cả
## Instancing methods
Các phương thức và các tham số có thể định cấu hình của nó là:
### Get.put()
Cách phổ biến nhất để chèn một dependency, là một điều tốt cho controller của View của bạn.
```dart
Get.put<SomeClass>(SomeClass());
Get.put<LoginController>(LoginController(), permanent: true);
Get.put<ListItemController>(ListItemController, tag: "some unique string");
```
Đây là tùy chọn mà bạn có thể đặt lệnh:
```dart
Get.put<S>(
// mandatory: the class that you want to get to save, like a controller or anything
// note: "S" means that it can be a class of any type
S dependency
// optional: this is for when you want multiple classess that are of the same type
// since you normally get a class by using Get.find<Controller>(),
// you need to use tag to tell which instance you need
// must be unique string
String tag,
// optional: by default, get will dispose instances after they are not used anymore (example,
// the controller of a view that is closed), but you might need that the instance
// to be kept there throughout the entire app, like an instance of sharedPreferences or something
// so you use this
// defaults to false
bool permanent = false,
// optional: allows you after using an abstract class in a test, replace it with another one and follow the test.
// defaults to false
bool overrideAbstract = false,
// optional: allows you to create the dependency using function instead of the dependency itself.
// this one is not commonly used
InstanceBuilderCallback<S> builder,
)
```
### Get.lazyPut
Có thể lazyLoad một dependecy để nó chỉ được khởi tạo khi được sử dụng. Rất hữu ích cho các class ngốn nhiều tài nguyên hoặc nếu bạn muốn khởi tạo một số class chỉ ở một nơi (như trong class Bindings) và bạn biết rằng mình sẽ không sử dụng class đó tại thời điểm nhất định.
```dart
/// ApiMock will only be called when someone uses Get.find<ApiMock> for the first time
Get.lazyPut<ApiMock>(() => ApiMock());
Get.lazyPut<FirebaseAuth>(
() {
// ... some logic if needed
return FirebaseAuth();
},
tag: Math.random().toString(),
fenix: true
)
Get.lazyPut<Controller>( () => Controller() )
```
Đây là các tùy chọn bạn có thể đặt lệnh:
```dart
Get.lazyPut<S>(
// mandatory: a method that will be executed when your class is called for the first time
InstanceBuilderCallback builder,
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
// must be unique
String tag,
// optional: It is similar to "permanent", the difference is that the instance is discarded when
// is not being used, but when it's use is needed again, Get will recreate the instance
// just the same as "SmartManagement.keepFactory" in the bindings api
// defaults to false
bool fenix = false
)
```
### Get.putAsync
Đây là khi bạn muốn xài asynchronize code `Get.putAsync`:
```dart
Get.putAsync<SharedPreferences>(() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('counter', 12345);
return prefs;
});
Get.putAsync<YourAsyncClass>( () async => await YourAsyncClass() )
```
Đây là tùy chọn bạn có thể đặt lệnh với putAsync:
```dart
Get.putAsync<S>(
// mandatory: an async method that will be executed to instantiate your class
AsyncInstanceBuilderCallback<S> builder,
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
// must be unique
String tag,
// optional: same as in Get.put(), used when you need to maintain that instance alive in the entire app
// defaults to false
bool permanent = false
)
```
### Get.create
Cái này hơi khó giải thích, nhưng sự khác nhau giữa chúng có thể được tìm thấy trên mục [Differences between methods:](#differences-between-methods)
```dart
Get.Create<SomeClass>(() => SomeClass());
Get.Create<LoginController>(() => LoginController());
```
Tùy chọn có thể sử dụng:
```dart
Get.create<S>(
// required: a function that returns a class that will be "fabricated" every
// time `Get.find()` is called
// Example: Get.create<YourClass>(() => YourClass())
FcBuilderFunc<S> builder,
// optional: just like Get.put(), but it is used when you need multiple instances
// of a of a same class
// Useful in case you have a list that each item need it's own controller
// needs to be a unique string. Just change from tag to name
String name,
// optional: just like int`Get.put()`, it is for when you need to keep the
// instance alive thoughout the entire app. The difference is in Get.create
// permanent is true by default
bool permanent = true
```
## Sử dụng các phương thức / class
Hãy tưởng tượng rằng bạn đã điều hướng qua nhiều route và bạn cần một dữ liệu còn sót trong controller của mình, bạn sẽ cần một trình quản lý state kết hợp với Provider hoặc Get_it, phải hem? Với Get, bạn chỉ cần yêu cầu Get to "find" cho controller của mình và thế là xong:
```dart
final controller = Get.find<Controller>();
// OR
Controller controller = Get.find();
// Yes, it looks like Magic, Get will find your controller, and will deliver it to you.
// You can have 1 million controllers instantiated, Get will always give you the right controller.
```
Và sau đó, bạn sẽ có thể khôi phục dữ liệu controller của mình đã lấy được ở đó:
```dart
Text(controller.textFromApi);
```
Vì giá trị trả về là một class bình thường, bạn có thể làm bất cứ điều gì bạn muốn:
```dart
int count = Get.find<SharedPreferences>().getInt('counter');
print(count); // out: 12345
```
Để xóa một controller đang chạy ngầm của Get:
```dart
Get.delete<Controller>(); //thường thì Get tự xóa, bạn không cần phải đặt lệnh này.
```
## Khác nhau giữa phương thức (methods)
Đầu tiên, hãy nói về `fenix` của Get.lazyPut và `permanent`của các phương thức khác.
Sự khác biệt cơ bản giữa `permanent` và `fenix` là cách bạn muốn lưu trữ các cá thể của mình.
Củng cố: theo mặc định, GetX xóa các trường hợp khi chúng không được sử dụng.
Có nghĩa là: Nếu màn hình 1 có controller 1 và màn hình 2 có controller 2 và bạn xóa route đầu tiên khỏi stack, (chẳng hạn như nếu bạn sử dụng `` Get.off () 'hoặc' `Get.offNamed()``) thì controller 1 bị mất việc sử dụng nó vì vậy nó sẽ bị xóa.
Nhưng nếu bạn muốn chọn sử dụng `permanent: true`, thì controller sẽ không bị mất trong quá trình chuyển đổi này - điều này rất hữu ích cho các dịch vụ mà bạn muốn duy trì hoạt động trong toàn bộ ứng dụng.
Mặt khác, `fenix` dành cho các dịch vụ mà bạn không lo bị mất giữa các lần thay đổi màn hình, nhưng khi bạn cần dịch vụ đó, bạn hy vọng rằng nó vẫn tồn tại. Vì vậy, về cơ bản, nó sẽ loại bỏ controller / service / class không sử dụng, nhưng khi bạn cần, nó sẽ "tạo lại từ đống tro tàn" ở một trường hợp (instance) mới.
Tiếp tục với sự khác biệt giữa các phương pháp:
- Get.put và Get.putAsync tuân theo cùng một thứ tự tạo, với sự khác biệt là một cái sử dụng phương thức không đồng bộ: hai phương thức đó đều tạo và khởi tạo các trường hợp. Cái sử dụng không đồng bộ được chèn trực tiếp vào bộ nhớ, bằng cách sử dụng phương thức nội bộ `insert` với các tham số `permanent: false` và` isSingleton: true` (tham số isSingleton này chỉ nhằm mục đích cho biết liệu nó có sử dụng dependency vào `dependency` hay không hoặc nếu nó được sử dụng dependency vào `FcBuilderFunc`). Sau đó, `Get.find ()` được gọi để khởi tạo ngay lập tức các các trường hợp trên bộ nhớ.
- Get.create: Như tên của nó, nó sẽ "tạo ra" sự dependency cho bạn! Tương tự như `Get.put ()`, nó cũng gọi phương thức nội bộ là `insert` để các trường hợp. Nhưng `permanent` trở thành true và` isSingleton` trở thành false (vì chúng ta đang "tạo" dependency của mình, không có cách nào để nó là một instace singleton, đó là lý do tại sao lại là false). Và bởi vì nó có `permanent: true`, chúng tôi mặc định có lợi ích là không bị mất nó giữa các màn hình! Ngoài ra, `` Get.find () 'không được gọi ngay lập tức, nó phải chờ được sử dụng trong màn hình để được gọi. Nó được tạo ra theo cách này để sử dụng tham số `permanent ', vì vậy, đáng chú ý là` Get.create () `được tạo ra với mục tiêu tạo ra các phiên bản không được chia sẻ, nhưng không bị loại bỏ, như ví dụ: trong listView, mà bạn muốn có một phiên bản duy nhất cho danh sách đó - do đó, Get.create phải được sử dụng cùng với GetWidget.
- Get.lazyPut: Như tên của nó, nó là một quy trình lười biếng. Cá thể được tạo, nhưng nó không được gọi để sử dụng ngay lập tức, nó vẫn đang chờ được gọi. Trái ngược với các phương thức khác, `insert` không được gọi ở đây. Thay vào đó, cá thể được chèn vào một phần khác của bộ nhớ, một phần chịu trách nhiệm cho biết liệu cá thể đó có thể được tạo lại hay không, chúng ta hãy gọi nó là "nhà máy". Nếu chúng ta muốn tạo ra thứ gì đó để sử dụng sau này, nó sẽ không bị trộn lẫn với những thứ đã được sử dụng ngay bây giờ. Và đây là nơi phép thuật của `fenix` đi vào: nếu bạn chọn bỏ` fenix: false`, và `smartManagement` của bạn không phải là` keepFactory`, thì khi sử dụng `Get.find`, instance sẽ thay đổi vị trí trong bộ nhớ từ "nhà máy" đến vùng bộ nhớ cá thể chung. Ngay sau đó, theo mặc định, nó được xóa khỏi "nhà máy". Bây giờ, nếu bạn chọn `fenix: true`, cá thể vẫn tiếp tục tồn tại trong phần dành riêng này, thậm chí sẽ chuyển sang vùng chung, sẽ được gọi lại trong tương lai.
## Bindings
Có lẽ, một trong những điểm khác biệt lớn của gói này là khả năng tích hợp đầy đủ các route, trình quản lý state và trình quản lý dependency.
Khi một route bị xóa khỏi stack, tất cả các controller, biến và phiên bản của các đối tượng liên quan đến nó sẽ bị xóa khỏi bộ nhớ. Nếu bạn đang sử dụng luồng hoặc bộ hẹn giờ, chúng sẽ tự động bị đóng và bạn không phải lo lắng về bất kỳ điều gì trong số đó.
Trong phiên bản 2.10 Được triển khai hoàn toàn API bindings.
Bây giờ bạn không cần sử dụng phương thức init nữa. Bạn thậm chí không cần phải nhập controller của mình nếu bạn không muốn. Bạn có thể khởi động controller và dịch vụ của mình ở nơi thích hợp cho việc đó.
Lớp Binding là một class sẽ tách riêng việc tiêm dependency, trong khi "bindings" các route đường tới trình quản lý state và trình quản lý dependency.
Điều này cho phép Nhận biết màn hình nào đang được hiển thị khi một controller cụ thể được sử dụng và biết vị trí và cách vứt bỏ nó.
Ngoài ra, class Binding sẽ cho phép bạn kiểm soát cấu hình SmartManager. Bạn có thể định cấu hình các phần dependency được sắp xếp khi xóa một route khỏi ngăn xếp hoặc khi widget con đã sử dụng nó được bố trí hoặc không. Bạn sẽ có quản lý dependency thông minh làm việc cho bạn, nhưng ngay cả như vậy, bạn có thể định cấu hình nó theo ý muốn.
### Bindings class
- Tạo một class và implements Binding
```dart
class HomeBinding implements Bindings {}
```
IDE của bạn sẽ tự động yêu cầu bạn ghi đè phương thức "dependency" và bạn chỉ cần nhấp vào đèn, ghi đè phương thức và chèn tất cả các class bạn sẽ sử dụng trên route đó:
```dart
class HomeBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut<HomeController>(() => HomeController());
Get.put<Service>(()=> Api());
}
}
class DetailsBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut<DetailsController>(() => DetailsController());
}
}
```
Bây giờ bạn chỉ cần thông báo route của mình, rằng bạn sẽ sử dụng bindings đó để tạo kết nối giữa trình quản lý route, các dependency và state.
- Sử dụng routes có tên:
```dart
getPages: [
GetPage(
name: '/',
page: () => HomeView(),
binding: HomeBinding(),
),
GetPage(
name: '/details',
page: () => DetailsView(),
binding: DetailsBinding(),
),
];
```
- Sử dụng routes thường:
```dart
Get.to(Home(), binding: HomeBinding());
Get.to(DetailsView(), binding: DetailsBinding())
```
Ở đó, bạn không phải lo lắng về việc quản lý bộ nhớ của ứng dụng của mình nữa, Get sẽ thay bạn làm điều đó.
Lớp Binding được gọi khi một route được gọi, bạn có thể tạo một "InitialBinding trong GetMaterialApp của mình để chèn tất cả các dependency sẽ được tạo.
```dart
GetMaterialApp(
initialBinding: SampleBind(),
home: Home(),
);
```
### BindingsBuilder
Cách mặc định để tạo bindings là tạo một class thực hiện các bindings.
Nhưng cách khác, bạn có thể sử dụng lệnh gọi lại `BindingsBuilder` để bạn có thể chỉ cần sử dụng một hàm để khởi tạo bất cứ thứ gì bạn muốn.
Example:
```dart
getPages: [
GetPage(
name: '/',
page: () => HomeView(),
binding: BindingsBuilder(() {
Get.lazyPut<ControllerX>(() => ControllerX());
Get.put<Service>(()=> Api());
}),
),
GetPage(
name: '/details',
page: () => DetailsView(),
binding: BindingsBuilder(() {
Get.lazyPut<DetailsController>(() => DetailsController());
}),
),
];
```
Bằng cách đó, bạn có thể tránh tạo một class Binding cho mỗi routes, làm cho việc này trở nên đơn giản hơn.
Cả hai cách làm việc đều hoàn toàn tốt và chúng tôi muốn bạn sử dụng những gì phù hợp với sở thích của bạn nhất.
### SmartManagement
GetX theo mặc định loại bỏ controller không sử dụng khỏi bộ nhớ, ngay cả khi xảy ra lỗi và widget con sử dụng nó không được xử lý đúng cách.
Đây được gọi là chế độ quản lý dependency `` đầy đủ`.
Nhưng nếu bạn muốn thay đổi cách GetX kiểm soát việc xử lý các class, bạn có class `SmartManagement` để bạn có thể thiết lập các hành vi khác nhau.
#### Cách thay đổi
Nếu bạn muốn thay đổi cấu hình này (mà bạn thường không cần) thì đây là cách:
```dart
void main () {
runApp(
GetMaterialApp(
smartManagement: SmartManagement.onlyBuilders //here
home: Home(),
)
)
}
```
#### SmartManagement.full
Nó là một trong những mặc định. Loại bỏ các class không được sử dụng và không được đặt thành vĩnh viễn. Trong phần lớn các trường hợp, bạn sẽ muốn giữ nguyên cấu hình này. Nếu bạn mới sử dụng GetX thì đừng thay đổi điều này.
#### SmartManagement.onlyBuilders
Với tùy chọn này, chỉ những controller bắt đầu trong `init: 'hoặc được tải vào Binding với` `Get.lazyPut ()` mới được xử lý.
Nếu bạn sử dụng `Get.put () 'hoặc' Get.putAsync ()` hoặc bất kỳ cách tiếp cận nào khác, SmartManagement sẽ không có quyền loại trừ sự dependency này.
Với hành vi mặc định, ngay cả các widget con được khởi tạo bằng "Get.put" sẽ bị xóa, không giống như SmartManagement.onlyBuilders.
#### SmartManagement.keepFactory
Cũng giống như SmartManagement.full, nó sẽ loại bỏ các phần dependency của nó khi nó không được sử dụng nữa. Tuy nhiên, nó sẽ giữ nguyên chế độ factory của họ, có nghĩa là nó sẽ tạo lại phần dependency nếu bạn cần lại phiên bản đó.
### Cách bindings làm việc ngầm
Các liên kết tạo ra các factory tạm thời, được tạo ra ngay khi bạn nhấp để chuyển sang màn hình khác và sẽ bị phá hủy ngay sau khi hoạt ảnh thay đổi màn hình xảy ra.
Điều này xảy ra quá nhanh đến nỗi máy phân tích thậm chí sẽ không thể đăng ký nó.
Khi bạn điều hướng đến màn hình này một lần nữa, một factory tạm thời mới sẽ được gọi, vì vậy điều này thích hợp hơn khi sử dụng SmartManagement.keepFactory, nhưng nếu bạn không muốn tạo Bindings hoặc muốn giữ tất cả các dependency của mình trên cùng một Binding, thì chắc chắn sẽ giúp ích cho bạn.
Các factory chiếm ít bộ nhớ, chúng không chứa các cá thể mà là một chức năng có "hình dạng" của class đó mà bạn muốn.
Điều này có chi phí bộ nhớ rất thấp, nhưng vì mục đích của lib này là để đạt được hiệu suất tối đa có thể bằng cách sử dụng tài nguyên tối thiểu, Get xóa ngay cả các factory theo mặc định.
Sử dụng cái nào thuận tiện nhất cho bạn.
## Chí ú
- KHÔNG SỬ DỤNG SmartManagement.keepFactory nếu bạn đang sử dụng nhiều Binding. Nó được thiết kế để sử dụng mà không có Bindings, hoặc với một Bindings duy nhất được liên kết trong `initialBinding` của GetMaterialApp.
- Việc sử dụng Bindings là hoàn toàn tùy chọn, nếu muốn, bạn có thể sử dụng `Get.put () 'và' Get.find()` trên các class sử dụng controller nhất định mà không gặp bất kỳ vấn đề gì.
Tuy nhiên, nếu bạn làm việc với Service hoặc bất kỳ abstract nào khác, tôi khuyên bạn nên sử dụng Bindings để tổ chức tốt hơn.
... ...
- [Quản lý route](#route-management)
- [Hướng dẫn sử dụng trước khi dùng](#how-to-use)
- [Điều hướng không cần tên](#navigation-without-named-routes)
- [Điều hướng cần tên](#navigation-with-named-routes)
- [Gửi data cho route có tên](#send-data-to-named-routes)
- [Dynamic urls links](#dynamic-urls-links)
- [Middleware](#middleware)
- [Điều hướng không cần context](#navigation-without-context)
- [SnackBars](#snackbars)
- [Dialogs](#dialogs)
- [BottomSheets](#bottomsheets)
- [Điều hướng lồng (Nested Navigation)](#nested-navigation)
# Quản lý route
Đây là lời giải thích đầy đủ về tất cả những gì có cho Getx khi vấn đề là quản lý routes.
## Hướng dẫn sử dụng trước khi dùng
Thêm cái này vào file pubspec.yaml của bạn:
```yaml
dependencies:
get:
```
Nếu bạn định sử dụng các routes / snackbars / dialogs / bottomsheets mà không có "context" hoặc sử dụng các API cấp cao, bạn chỉ cần thêm Get trước MaterialApp của mình, biến nó thành GetMaterialApp và tung hành!
```dart
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
```
## Điều hướng không cần tên
Để điều hướng đến một màn hình mới:
```dart
Get.to(NextScreen());
```
Để đóng snackbars, dialog, bottomsheets hoặc bất cứ thứ gì bạn thường đóng bằng Navigator.pop (context);
```dart
Get.back();
```
Để chuyển đến màn hình tiếp theo và không có tùy chọn nào để quay lại màn hình trước đó (để sử dụng trong SplashScreens, màn hình đăng nhập, v.v.)
```dart
Get.off(NextScreen());
```
Để chuyển đến màn hình tiếp theo và hủy tất cả các lộ trình trước đó (hữu ích trong giỏ hàng, polls và test)
```dart
Get.offAll(NextScreen());
```
Để điều hướng đến routes tiếp theo và nhận hoặc cập nhật dữ liệu ngay sau khi bạn trở về từ routes đó:
```dart
var data = await Get.to(Payment());
```
trên màn hình khác, gửi dữ liệu cho routes trước đó:
```dart
Get.back(result: 'success');
```
And use it:
ex:
```dart
if(data == 'success') madeAnything();
```
Bạn không muốn học cú pháp của chúng tôi?
Chỉ cần thay đổi Navigator (chữ in hoa) thành navigator (chữ thường) và bạn sẽ có tất cả các chức năng của điều hướng tiêu chuẩn mà không cần phải sử dụng "context"
Thí dụ:
```dart
// Default Flutter navigator
Navigator.of(context).push(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return HomePage();
},
),
);
// Get using Flutter syntax without needing context
navigator.push(
MaterialPageRoute(
builder: (_) {
return HomePage();
},
),
);
// Get syntax (It is much better, but you have the right to disagree)
Get.to(HomePage());
```
## Điều hướng cần tên
- Nếu bạn thích điều hướng bằng tên, Get cũng hỗ trợ điều này.
To navigate to nextScreen
```dart
Get.toNamed("/NextScreen");
```
Để điều hướng và xóa màn hình trước đó khỏi cây widget.
```dart
Get.offNamed("/NextScreen");
```
Để điều hướng và xóa tất cả các màn hình trước đó khỏi cây widget.
```dart
Get.offAllNamed("/NextScreen");
```
Để định dạng routes, sử dụng GetMaterialApp:
```dart
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => MyHomePage()),
GetPage(name: '/second', page: () => Second()),
GetPage(
name: '/third',
page: () => Third(),
transition: Transition.zoom
),
],
)
);
}
```
Để xử lý điều hướng đến các routes không được xác định (lỗi 404), bạn có thể xác định trang 'không xác định' trong GetMaterialApp.
```dart
void main() {
runApp(
GetMaterialApp(
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => MyHomePage()),
GetPage(name: '/second', page: () => Second()),
],
)
);
}
```
### Gửi data cho route có tên
Chỉ cần gửi những gì bạn muốn cho các đối số (arguments). Get chấp nhận bất kỳ thứ gì ở đây, cho dù đó là String, Map, List hay thậm chí là một class trường hợp.
```dart
Get.toNamed("/NextScreen", arguments: 'Get is the best');
```
Trong class controller của bạn:
```dart
print(Get.arguments);
//print out: Get is the best
```
### Dynamic urls links
Get hỗ trợ các url động nâng cao giống như trên Web. Các nhà phát triển web có lẽ đã muốn tính năng này trên Flutter và rất có thể đã thấy một gói hứa hẹn tính năng này và cung cấp một cú pháp hoàn toàn khác so với một URL sẽ có trên web, và Get cũng giải quyết được điều này.
```dart
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
```
trong controller/bloc/stateful/stateless của class:
```dart
print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
```
Bạn có thể đặt NamedParameters với Get dễ dàng:
```dart
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(
name: '/',
page: () => MyHomePage(),
),
GetPage(
name: '/profile/',
page: () => MyProfile(),
),
//You can define a different page for routes with arguments, and another without arguments, but for that you must use the slash '/' on the route that will not receive arguments as above.
GetPage(
name: '/profile/:user',
page: () => UserProfile(),
),
GetPage(
name: '/third',
page: () => Third(),
transition: Transition.cupertino
),
],
)
);
}
```
Gửi data bằng tên
```dart
Get.toNamed("/profile/34954");
```
Trên màn hình thứ hai, lấy dữ liệu theo tham số (parameters)
```dart
print(Get.parameters['user']);
// out: 34954
```
hoặc gửi nhiều tham số như thế này
```dart
Get.toNamed("/profile/34954?flag=true&country=italy");
```
or
```dart
var parameters = <String, String>{"flag": "true","country": "italy",};
Get.toNamed("/profile/34954", parameters: parameters);
```
Trên màn hình thứ hai, lấy dữ liệu theo các tham số như thường lệ
```dart
print(Get.parameters['user']);
print(Get.parameters['flag']);
print(Get.parameters['country']);
// out: 34954 true italy
```
Và bây giờ, tất cả những gì bạn cần làm là sử dụng Get.toNamed () để điều hướng các routes đã đặt tên của bạn mà không cần bất kỳ "context" nào (bạn có thể gọi các routes của mình trực tiếp từ BLoC hoặc lớp Bộ điều khiển) và khi ứng dụng của bạn được biên dịch lên web, các routes sẽ xuất hiện trong url <3
### Middleware
Nếu bạn muốn nghe Get events để kích hoạt các hành động, bạn có thể sử dụng routingCallback cho nó
```dart
GetMaterialApp(
routingCallback: (routing) {
if(routing.current == '/second'){
openAds();
}
}
)
```
Nếu bạn không sử dụng GetMaterialApp, bạn có thể sử dụng API thủ công để đính kèm trình quan sát Middleware.
```dart
void main() {
runApp(
MaterialApp(
onGenerateRoute: Router.generateRoute,
initialRoute: "/",
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer), // HERE !!!
],
),
);
}
```
Tạo một MiddleWare class
```dart
class MiddleWare {
static observer(Routing routing) {
/// You can listen in addition to the routes, the snackbars, dialogs and bottomsheets on each screen.
///If you need to enter any of these 3 events directly here,
///you must specify that the event is != Than you are trying to do.
if (routing.current == '/second' && !routing.isSnackbar) {
Get.snackbar("Hi", "You are on second route");
} else if (routing.current =='/third'){
print('last route called');
}
}
}
```
Bây giờ, hãy sử dụng Get trên code của bạn:
```dart
class First extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.add),
onPressed: () {
Get.snackbar("hi", "i am a modern snackbar");
},
),
title: Text('First Route'),
),
body: Center(
child: ElevatedButton(
child: Text('Open route'),
onPressed: () {
Get.toNamed("/second");
},
),
),
);
}
}
class Second extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.add),
onPressed: () {
Get.snackbar("hi", "i am a modern snackbar");
},
),
title: Text('second Route'),
),
body: Center(
child: ElevatedButton(
child: Text('Open route'),
onPressed: () {
Get.toNamed("/third");
},
),
),
);
}
}
class Third extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Third Route"),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Get.back();
},
child: Text('Go back!'),
),
),
);
}
}
```
## Điều hướng không cần context
### SnackBars
Để có một SnackBar đơn giản với Flutter, bạn phải lấy context của Scaffold, hoặc bạn phải sử dụng GlobalKey được gắn vào Scaffold của bạn
```dart
final snackBar = SnackBar(
content: Text('Hi!'),
action: SnackBarAction(
label: 'I am a old and ugly snackbar :(',
onPressed: (){}
),
);
// Find the Scaffold in the widget tree and use
// it to show a SnackBar.
Scaffold.of(context).showSnackBar(snackBar);
```
With Get:
```dart
Get.snackbar('Hi', 'i am a modern snackbar');
```
Với Get, tất cả những gì bạn phải làm là gọi thanh Get.snackbar từ bất kỳ đâu trong code của bạn hoặc tùy chỉnh nó theo cách bạn muốn!
```dart
Get.snackbar(
"Hey i'm a Get SnackBar!", // title
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
icon: Icon(Icons.alarm),
shouldIconPulse: true,
onTap:(){},
barBlur: 20,
isDismissible: true,
duration: Duration(seconds: 3),
);
////////// ALL FEATURES //////////
// Color colorText,
// Duration duration,
// SnackPosition snackPosition,
// Widget titleText,
// Widget messageText,
// bool instantInit,
// Widget icon,
// bool shouldIconPulse,
// double maxWidth,
// EdgeInsets margin,
// EdgeInsets padding,
// double borderRadius,
// Color borderColor,
// double borderWidth,
// Color backgroundColor,
// Color leftBarIndicatorColor,
// List<BoxShadow> boxShadows,
// Gradient backgroundGradient,
// TextButton mainButton,
// OnTap onTap,
// bool isDismissible,
// bool showProgressIndicator,
// AnimationController progressIndicatorController,
// Color progressIndicatorBackgroundColor,
// Animation<Color> progressIndicatorValueColor,
// SnackStyle snackStyle,
// Curve forwardAnimationCurve,
// Curve reverseAnimationCurve,
// Duration animationDuration,
// double barBlur,
// double overlayBlur,
// Color overlayColor,
// Form userInputForm
///////////////////////////////////
```
Nếu bạn thích snackbar truyền thống hoặc muốn tùy chỉnh nó từ đầu, bao gồm chỉ thêm một dòng (Get.snackbar sử dụng tiêu đề và thông báo bắt buộc), bạn có thể sử dụng
`Get.rawSnackbar ()`; 'cung cấp API RAW trên đó Get.
### Dialogs
To open dialog:
```dart
Get.dialog(YourDialogWidget());
```
To open default dialog:
```dart
Get.defaultDialog(
onConfirm: () => print("Ok"),
middleText: "Dialog made in 3 lines of code"
);
```
Bạn cũng có thể sử dụng Get.generalDialog thay vì showGeneralDialog.
Đối với tất cả các tiện ích hộp thoại Flutter khác, bao gồm cả cupertinos, bạn có thể sử dụng Get.overlayContext thay vì context và mở nó ở bất kỳ đâu trong mã của bạn.
Đối với các widget không sử dụng Overlay, bạn có thể sử dụng Get.context.
Hai context này sẽ hoạt động trong 99% trường hợp để thay thế context của UI của bạn, ngoại trừ các trường hợp trong đó inheritWidget được sử dụng mà không có context điều hướng.
### BottomSheets
Get.bottomSheet giống như showModalBottomSheet, nhưng không cần context.
```dart
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: () {},
),
],
),
)
);
```
## Điều hướng lồng (Nested Navigation)
Làm cho điều hướng lồng (nested navigation) của Flutter thậm chí còn dễ dàng hơn.
Bạn không cần context và bạn sẽ tìm thấy stack điều hướng của mình theo Id.
- CHÍ Ú: Việc tạo các stack điều hướng song song có thể gây nguy hiểm. Lý tưởng nhất là không sử dụng NestedNavigators, hoặc sử dụng một cách tối thiểu. Nếu dự án của bạn yêu cầu, hãy tiếp tục, nhưng hãy nhớ rằng việc giữ nhiều stack điều hướng trong bộ nhớ có thể không phải là một ý tưởng hay cho việc tiêu thụ RAM.
Xem nó code đơn giản nè:
```dart
Navigator(
key: Get.nestedKey(1), // create a key by index
initialRoute: '/',
onGenerateRoute: (settings) {
if (settings.name == '/') {
return GetPageRoute(
page: () => Scaffold(
appBar: AppBar(
title: Text("Main"),
),
body: Center(
child: TextButton(
color: Colors.blue,
onPressed: () {
Get.toNamed('/second', id:1); // navigate by your nested route by index
},
child: Text("Go to second"),
),
),
),
);
} else if (settings.name == '/second') {
return GetPageRoute(
page: () => Center(
child: Scaffold(
appBar: AppBar(
title: Text("Main"),
),
body: Center(
child: Text("second")
),
),
),
);
}
}
),
```
... ...
* [Quản lý State](#state-management)
+ [Quản lý Reactive State](#reactive-state-manager)
- [Lợi thế](#advantages)
- [Hiệu suất tối đa:](#maximum-performance)
- [Khai báo một biến phản ứng (reactive variable)](#declaring-a-reactive-variable)
- [Thât dễ khi có reactive state.](#having-a-reactive-state-is-easy)
- [Sử dụng values trong View](#using-the-values-in-the-view)
- [Điều kiện để tái tạo lại](#conditions-to-rebuild)
- [Nơi .obs có thể dùng](#where-obs-can-be-used)
- [Chí ú về Lists](#note-about-lists)
- [Tại sao tôi phải dùng .value](#why-i-have-to-use-value)
- [Obx()](#obx)
- [Workers](#workers)
+ [Quản lý State đơn giản](#simple-state-manager)
- [Lợi thế](#advantages-1)
- [Sử dụng](#usage)
- [Cách GetX sử dụng controllers](#how-it-handles-controllers)
- [Không cần StatefulWidget nữa!](#you-wont-need-statefulwidgets-anymore)
- [Tại sao GetX tồn tại?](#why-it-exists)
- [Cách sử dụng khác](#other-ways-of-using-it)
- [IDs độc nhất](#unique-ids)
+ [Trộn hai trình quản lý state](#mixing-the-two-state-managers)
+ [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
# Quản lý State
GetX không sử dụng Streams hoặc ChangeNotifier như các quản lý state khác. Tại sao? Ngoài việc xây dựng các ứng dụng cho android, iOS, web, linux, macos và linux, với GetX bạn có thể xây dựng các ứng dụng máy chủ với cú pháp tương tự như Flutter / GetX. Để cải thiện thời gian phản hồi và giảm mức tiêu thụ RAM, chúng tôi đã tạo GetValue và GetStream, là các giải pháp có độ trễ thấp mang lại nhiều hiệu suất với chi phí vận hành thấp. Chúng tôi sử dụng cơ sở này để xây dựng tất cả các nguồn lực của mình, bao gồm cả quản lý state.
* _Phức hợp_: Một số quản lý state rất phức tạp và có rất nhiều cơ sở hạ tầng. Với GetX, bạn không phải xác định một class cho mỗi event, code rất rõ ràng và rõ ràng, và bạn làm được nhiều việc hơn bằng cách viết ít hơn. Nhiều người đã từ bỏ Flutter vì chủ đề này, và cuối cùng họ đã có một giải pháp đơn giản đến mức đần độn để quản lý các state.
* _Không trình tạo mã_: Bạn dành một nửa thời gian phát triển để viết logic ứng dụng của mình. Một số quản lý state dựa vào trình tạo mã để có mã có thể đọc được ở mức tối thiểu. Việc thay đổi một biến và phải chạy build_runner có thể gây mất hiệu quả, chuyện này rất ngốn thời gian chờ đợi sau khi quét sạch sẽ rất lâu và bạn phải uống rất nhiều cà phê.
Với GetX, mọi thứ đều hoạt động và độc lập với trình tạo mã, giúp tăng năng suất của bạn trong mọi khía cạnh phát triển của bạn.
* _Không phụ thuộc vào context_: Có thể bạn đã cần gửi context của chế độ xem của mình tới controller, làm cho khả năng kết hợp của View với business logic của bạn cao hơn. Bạn có thể phải sử dụng một dependency cho một nơi không có context và phải chuyển context qua các class và hàm khác nhau. Điều này không tồn tại với GetX. Bạn có quyền truy cập vào controller của mình từ bên trong controller mà không cần bất kỳ context nào. Bạn không cần phải gửi context theo tham số vì không có gì theo nghĩa đen.
* _Kiểm soát hạt_: Hầu hết các quản lý state đều dựa trên ChangeNotifier. ChangeNotifier sẽ thông báo cho tất cả các widget phụ thuộc vào nó khi thông báo cho các widget được gọi. Nếu bạn có 40 widget con trên một màn hình, trong đó có một biến thuộc class ChangeNotifier của bạn, khi bạn cập nhật một widget con, tất cả chúng sẽ được xây dựng lại.
Với GetX, ngay cả các widget lồng nhau cũng được tôn trọng. Nếu bạn có Obx đang xem ListView của bạn và người khác đang xem hộp kiểm bên trong ListView, thì khi thay đổi giá trị CheckBox, chỉ nó mới được cập nhật, khi thay đổi giá trị List, chỉ ListView sẽ được cập nhật.
* _Chỉ tái tạo lại nếu biến CẦN thay đổi_: GetX có tính năng kiểm soát streams, điều đó có nghĩa là nếu bạn hiển thị Text là 'Kaiser', nếu bạn thay đổi lại biến có thể quan sát thành 'Kaiser', widget sẽ không được tạo lại. Đó là bởi vì GetX biết rằng 'Kaiser' đã được hiển thị trong Văn bản và sẽ không thực hiện các thao tác tái tạo không cần thiết.
Hầu hết (nếu không phải tất cả) các trình quản lý state hiện tại sẽ xây dựng lại trên màn hình.
## Quản lý Reactive State
Lập trình phản ứng (Reactive programming) có thể khiến nhiều người xa lánh vì nó được cho là phức tạp. GetX biến lập trình phản ứng thành một thứ khá đơn giản:
* Bạn sẽ không cần tạo StreamControllers.
* Bạn sẽ không cần tạo StreamBuilder cho mỗi biến
* Bạn sẽ không cần phải tạo một class cho mỗi state.
* Bạn sẽ không cần tạo get cho một giá trị ban đầu.
Lập trình phản ứng với Get dễ dàng như sử dụng setState.
Hãy tưởng tượng rằng bạn có một biến tên và muốn rằng mỗi khi bạn thay đổi nó, tất cả các widget sử dụng nó sẽ được tự động thay đổi.
Đây là count variable của bạn:
``` dart
var name = 'Khang Huỳnh';
```
Để làm cho nó có thể quan sát được, bạn chỉ cần thêm ".obs" vào cuối nó:
``` dart
var name = 'Khang Huỳnh'.obs;
```
Chỉ vậy thôi, chỉ *vậy thôi* người ơi~
Từ bây giờ, chúng ta có thể tham chiếu đến các biến reactive - ". Obs" (có thể thay thế) này là _Rx_.
Chúng tôi đã làm gì phía dưới class code? Chúng tôi đã tạo một `Stream` của `String`, được gán giá trị ban đầu `"Khang Huỳnh"`, chúng tôi đã thông báo cho tất cả các widget con sử dụng `"Khang Huỳnh"` rằng chúng hiện "thuộc về" biến này và khi giá trị _Rx_ thay đổi, chúng phải thay đổi theo.
Đây là **phép màu của GetX**, nhờ vào khả năng của Dart.
Tuy nhiên, như chúng ta đã biết, một `Widget` chỉ có thể được thay đổi nếu nó nằm bên trong một hàm, bởi vì các class tĩnh không có quyền" tự động thay đổi ".
Bạn sẽ cần tạo một `StreamBuilder`, đăng ký biến này để lắng nghe các thay đổi và tạo một "stream" các` StreamBuilder` lồng nhau nếu bạn muốn thay đổi một số biến trong cùng một phạm vi, phải không?
Không, bạn không cần `StreamBuilder`, nhưng bạn đã đúng về các class tĩnh.
Theo quan điểm, chúng ta thường có rất nhiều bảng soạn sẵn khi chúng ta muốn thay đổi một Widget cụ thể, đó là cách Flutter.
Với ** GetX **, bạn cũng có thể quên mã soạn sẵn này.
`StreamBuilder (…)`? `initialValue:…`? `builder:…`? Không, bạn chỉ cần đặt biến này bên trong Widget `Obx ()`.
``` dart
Obx (() => Text (controller.name));
```
_Bạn cần nhớ gì?_ Chỉ `Obx(() =>` .
Bạn chỉ đang chuyển Widget đó thông qua một hàm mũi tên vào một `Obx ()` ("Observer" của _Rx_).
`Obx` khá thông minh và sẽ chỉ thay đổi nếu giá trị của `controller.name` thay đổi.
Nếu `name` là` "Kaiser" `và bạn thay đổi nó thành` "Kaiser" `(` name.value = "Kaiser" `), vì nó giống như` giá trị` như trước, sẽ không có gì thay đổi trên màn hình, và `Obx`, để tiết kiệm tài nguyên, sẽ đơn giản bỏ qua giá trị mới và không xây dựng lại Widget. **Tuyệt vời ông mặt trời chứ?**
> So, what if I have 5 _Rx_ (observable) variables within an `Obx` ?
Nó sẽ chỉ cập nhật khi ** bất kỳ ** nào trong số chúng thay đổi.
> And if I have 30 variables in a class, when I update one, will it update **all** the variables that are in that class?
Không, chỉ **Widget cụ thể** sử dụng biến _Rx_ đó.
Vì vậy, **GetX** chỉ cập nhật màn hình, khi biến _Rx_ thay đổi giá trị của nó.
```
final isOpen = false.obs;
// NOTHING will happen... same value.
void onButtonTap() => isOpen.value=false;
```
### Lợi thế
**GetX()** giúp bạn khi bạn cần kiểm soát **chi tiết** đối với những gì đang được cập nhật.
Nếu bạn không cần `ID duy nhất`, vì tất cả các biến của bạn sẽ được sửa đổi khi bạn thực hiện một hành động, thì hãy sử dụng` GetBuilder`,
bởi vì nó là một Trình cập nhật state đơn giản (trong các khối, như `setState ()` '), được tạo chỉ trong một vài dòng mã.
Nó được làm đơn giản, ít ảnh hưởng đến CPU nhất và chỉ để thực hiện một mục đích duy nhất (xây dựng lại _State_) và sử dụng tài nguyên tối thiểu có thể.
Nếu bạn cần một Trình quản lý state **mạnh mẽ**, bạn không thể làm sai với **GetX**.
Nó không hoạt động với các biến, nhưng __flows__, mọi thứ trong đó đều là `Streams`.
Bạn có thể sử dụng _rxDart_ kết hợp với nó, vì mọi thứ đều là `Luồng`,
bạn có thể nghe `event` của từng" biến _Rx_ ",
bởi vì mọi thứ trong đó đều là `Streams`.
Nó thực sự là một cách tiếp cận _BLoC_, dễ dàng hơn _MobX_ và không có trình tạo code hoặc decorations.
Bạn có thể biến **mọi thứ** thành một _"Observable" _ chỉ với một `.obs`.
### Hiệu suất tối đa:
Ngoài việc có một thuật toán thông minh để xây dựng lại tối thiểu, **GetX** sử dụng trình so sánh để đảm bảo rằng Bang đã thay đổi.
Nếu bạn gặp bất kỳ lỗi nào trong ứng dụng của mình và gửi một bản thay đổi state, **GetX** sẽ đảm bảo rằng nó sẽ không gặp sự cố.
Với **GetX** State chỉ thay đổi nếu `giá trị` thay đổi.
Đó là sự khác biệt chính giữa **GetX** và việc sử dụng _ `computed` từ MobX_.
Khi kết hợp hai __observables__, và một thay đổi; trình nghe của _observable_ đó cũng sẽ thay đổi.
Với **GetX**, nếu bạn nối hai biến, `GetX ()` (tương tự như `Observer ()`) sẽ chỉ xây dựng lại nếu nó ngụ ý thay đổi state thực sự.
### Khai báo một biến phản ứng (reactive variable)
Bạn có 3 cách để thay đổi variable thành "observable".
1 - Sử dụng **`Rx{Type}`**.
``` dart
// initial value is recommended, but not mandatory
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 - Sử dụng **`Rx`** và dùng Darts Generics, `Rx<Type>`
``` dart
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>>({});
// Custom classes - it can be any class, literally
final user = Rx<User>();
```
3 - Cách tối ưu nhất, thêm **`.obs`**`value` :
``` dart
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;
// Custom classes - it can be any class, literally
final user = User().obs;
```
##### Thât dễ khi có reactive state.
Như chúng ta biết, _Dart_ đang hướng tới _null safety_.
Để chuẩn bị, từ bây giờ, bạn phải luôn bắt đầu các biến _Rx_ của mình bằng một **initial value**.
> Transforming a variable into an _observable_ + _initial value_ with **GetX** is the simplest, and most practical approach.
Theo đúng nghĩa đen, bạn sẽ thêm một "` .obs` "vào cuối biến của mình và **vậy thôi người ơi~**, bạn đã làm cho nó có thể quan sát được, và `.value` của nó sẽ là _initial value_).
### Sử dụng values trong View
``` dart
// controller file
final count1 = 0.obs;
final count2 = 0.obs;
int get sum => count1.value + count2.value;
```
``` dart
// view file
GetX<Controller>(
builder: (controller) {
print("count 1 rebuild");
return Text('${controller.count1.value}');
},
),
GetX<Controller>(
builder: (controller) {
print("count 2 rebuild");
return Text('${controller.count2.value}');
},
),
GetX<Controller>(
builder: (controller) {
print("count 3 rebuild");
return Text('${controller.sum}');
},
),
```
Nếu chúng ta cộng `count1.value++` , nó sẽ in:
* `count 1 rebuild`
* `count 3 rebuild`
bởi vì `count1` có giá trị là` 1` và `1 + 0 = 1`, thay đổi giá trị getter` sum`.
Nếu ta thay đổi `count2.value++` , nó sẽ in:
* `count 2 rebuild`
* `count 3 rebuild`
bởi vì `count2.value` đã thay đổi, và kết quả của` sum` bây giờ là `2`.
* LƯU Ý: Theo mặc định, event đầu tiên sẽ xây dựng lại widget con, ngay cả khi nó là cùng một `giá trị`.
Hành vi này tồn tại do các biến Boolean.
Ví dụ, bạn code thế này:
``` dart
var isLogged = false.obs;
```
Và sau đó, bạn đã kiểm tra xem người dùng có "đăng nhập" để kích hoạt event trong `ever` không.
``` dart
@override
onInit(){
ever(isLogged, fireRoute);
isLogged.value = await Preferences.hasToken();
}
fireRoute(logged) {
if (logged) {
Get.off(Home());
} else {
Get.off(Login());
}
}
```
nếu `hasToken` là` false`, sẽ không có thay đổi thành `isLogged`, vì vậy `ever ()` sẽ không bao giờ được gọi.
Để tránh loại hành vi này, thay đổi đầu tiên đối với _observable_ sẽ luôn kích hoạt một event,
ngay cả khi nó chứa cùng một `.value`.
Bạn có thể xóa hành vi này nếu muốn, bằng cách sử dụng:
`isLogged.firstRebuild = false;`
### Điều kiện để tái tạo lại
Ngoài ra, Get cung cấp khả năng kiểm soát state đã được tinh chỉnh. Bạn có thể điều kiện một event (chẳng hạn như thêm một đối tượng vào danh sách), với một điều kiện nhất định.
``` dart
// First parameter: condition, must return true or false.
// Second parameter: the new value to apply if the condition is true.
list.addIf(item < limit, item);
```
Không có decoration, không có trình tạo mã, không có phức tạp hóa vấn đề: smile:
Bạn có biết ứng dụng counter của Flutter không? Class controller của bạn có thể trông giống như sau:
``` dart
class CountController extends GetxController {
final count = 0.obs;
}
```
Đơn giản hơn:
``` dart
controller.count.value++
```
Bạn có thể cập nhật counter trong UI của mình, bất kể nó được lưu trữ ở đâu.
### Nơi .obs có thể dùng
Bạn có thể biến đổi bất cứ thứ gì trên obs. Đây là hai cách để làm điều đó:
* Bạn có thể chuyển đổi các giá trị class của mình thành obs
``` dart
class RxUser {
final name = "Camila".obs;
final age = 18.obs;
}
```
* hoặc bạn có thể biến cả 1 class thành observable
``` dart
class User {
User({String name, int age});
var name;
var age;
}
// when instantianting:
final user = User(name: "Camila", age: 18).obs;
```
### Chí ú về Lists
List hoàn toàn có thể quan sát được cũng như các đối tượng bên trong nó. Bằng cách đó, nếu bạn thêm một giá trị vào danh sách, nó sẽ tự động xây dựng lại các widget con sử dụng nó.
Bạn cũng không cần phải sử dụng ".value" với các danh sách, api phi tiêu tuyệt vời đã cho phép chúng tôi loại bỏ điều đó.
Tiếc thay, các kiểu nguyên thủy như String và int không thể được mở rộng, khiến việc sử dụng .value là bắt buộc, nhưng điều đó sẽ không thành vấn đề nếu bạn làm việc với getters và setters cho những thứ này.
``` dart
// On the controller
final String title = 'User Info:'.obs
final list = List<User>().obs;
// on the view
Text(controller.title.value), // String need to have .value in front of it
ListView.builder (
itemCount: controller.list.length // lists don't need it
)
```
Khi bạn đang làm cho các class của riêng mình có thể quan sát được, có một cách khác để cập nhật chúng:
``` dart
// on the model file
// we are going to make the entire class observable instead of each attribute
class User() {
User({this.name = '', this.age = 0});
String name;
int age;
}
// on the controller file
final user = User().obs;
// when you need to update the user variable:
user.update( (user) { // this parameter is the class itself that you want to update
user.name = 'Jonny';
user.age = 18;
});
// an alternative way of update the user variable:
user(User(name: 'João', age: 35));
// on view:
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
// you can also access the model values without the .value:
user().name; // notice that is the user variable, not the class (variable has lowercase u)
```
Bạn không cần phải làm việc với các bộ nếu bạn không muốn. bạn có thể sử dụng api "assign" và "assignAll".
Api "assign" sẽ xóa danh sách của bạn và thêm một đối tượng duy nhất mà bạn muốn bắt đầu ở đó.
Api "allowAll" sẽ xóa danh sách hiện có và thêm bất kỳ đối tượng có thể lặp lại nào mà bạn đưa vào đó.
### Tại sao tôi phải dùng .value
Chúng ta có thể loại bỏ việc sử dụng 'value' đối với `String` và` int` bằng một trình tạo mã và decoration đơn giản, nhưng mục đích của thư viện này chính là tránh các dependency bên ngoài. Chúng tôi muốn cung cấp một môi trường sẵn sàng cho việc lập trình, liên quan đến các yếu tố cần thiết (quản lý các route, dependency và state), theo cách đơn giản, nhẹ và hiệu quả mà không cần gói bên ngoài.
Theo nghĩa đen, bạn có thể thêm 3 chữ cái vào pubspec (get) của mình và dấu hai chấm và bắt đầu lập trình. Tất cả các giải pháp được bao gồm theo mặc định, từ quản lý route đến quản lý state, nhằm mục đích dễ dàng, năng suất và hiệu suất.
Tổng trọng lượng của thư viện này ít hơn của một trình quản lý state duy nhất, mặc dù nó là một giải pháp hoàn chỉnh và đó là những gì bạn phải hiểu.
Nếu bạn bị làm phiền bởi `.value`, và giống như một trình tạo mã, MobX là một giải pháp thay thế tuyệt vời và bạn có thể sử dụng nó cùng với Get. Đối với những người muốn thêm một gói dependency duy nhất vào pubspec và bắt đầu lập trình mà không cần lo lắng về phiên bản của gói không tương thích với gói khác hoặc nếu lỗi cập nhật state đến từ trình quản lý state hoặc dependency, hoặc vẫn không muốn lo lắng về sự sẵn có của controller, cho dù theo nghĩa đen là "chỉ là lập trình", GetX là lựa chọn hoàn hảo.
Nếu bạn không gặp vấn đề gì với trình tạo mã MobX hoặc không gặp vấn đề gì với bảng soạn sẵn BLoC, bạn có thể chỉ cần sử dụng Get cho các route và quên rằng nó có trình quản lý state. Get SEM và RSM ra đời không cần thiết, công ty của tôi có một dự án với hơn 90 controller và trình tạo mã chỉ mất hơn 30 phút để hoàn thành nhiệm vụ của nó sau khi Flutter Clean trên một máy khá tốt, nếu dự án của bạn có 5, 10, 15 controller, bất kỳ nhà quản lý state sẽ cung cấp cho bạn tốt. Nếu bạn có một dự án lớn đến mức ngớ ngẩn và trình tạo mã là một vấn đề đối với bạn, thì bạn đã được trao giải pháp này.
Rõ ràng, nếu ai đó muốn đóng góp vào dự án và tạo trình tạo mã, hoặc thứ gì đó tương tự, tôi sẽ liên kết trong readme này như một giải pháp thay thế, nhu cầu của tôi không phải là nhu cầu của tất cả các nhà phát triển, nhưng ý tôi là thế, đã có những giải pháp tốt đã làm được điều đó, như MobX.
### Obx()
Nhập vào Get bằng cách sử dụng Bindings là không cần thiết. bạn có thể sử dụng widget Obx thay vì GetX, widget chỉ nhận được chức năng ẩn danh tạo widget.
Rõ ràng, nếu bạn không sử dụng một kiểu, bạn sẽ cần phải có một phiên bản của controller để sử dụng các biến hoặc sử dụng `Get.find <Controller> ()` .value hoặc Controller.to.value để truy xuất giá trị .
### Workers
Workers sẽ hỗ trợ bạn, kích hoạt các lệnh gọi lại cụ thể khi một event xảy ra.
``` dart
/// Called every time `count1` changes.
ever(count1, (_) => print("$_ has been changed"));
/// Called only first time the variable $_ is changed
once(count1, (_) => print("$_ was changed once"));
/// Anti DDos - Called every time the user stops typing for 1 second, for example.
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
/// Ignore all changes within 1 second.
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
```
Tất cả các workers (except `debounce` ) có `condition` tham số được đặt tên, mà có thể là loại `bool` hoặc lệnh gọi lại trả về một `bool` .
`condition` này mô tả khi `callback` kích hoạt.
Tất cả các workers đều trả về trường hợp `Worker`, mà bạn có thể đóng ( thông qua `dispose()` ) của worker.
* **`ever`**
được gọi mỗi khi biến _Rx_ tạo ra một giá trị mới.
* **`everAll`**
Giống như `ever`, nhưng nó có một` List` gồm các giá trị _Rx_ Được gọi mỗi khi biến của nó bị thay đổi. Chỉ vậy thôi người ơi~ 😊
* **`once`**
'once' chỉ được gọi lần đầu tiên biến được thay đổi.
* **`debounce`**
'debounce' rất hữu ích trong các hàm tìm kiếm, nơi bạn chỉ muốn API được gọi khi người dùng nhập xong. Nếu người dùng nhập "Kaiser", bạn sẽ có 6 tìm kiếm trong các API, theo ký tự K, a, i, s, e và r. Với Get, điều này không xảy ra, bởi vì bạn sẽ có một Worker "debounce" sẽ chỉ được kích hoạt khi kết thúc nhập.
* **`interval`**
'interval' khác với debounce. Debounce xảy ra nếu người dùng thực hiện 1000 thay đổi đối với một biến trong vòng 1 giây, y sẽ chỉ gửi biến cuối cùng sau bộ hẹn giờ quy định (mặc định là 800 mili giây). Thay vào đó, interval sẽ bỏ qua tất cả các hành động của người dùng trong interval quy định. Nếu bạn gửi event trong 1 phút, 1000 mỗi giây, tính năng gỡ lỗi sẽ chỉ gửi cho bạn event cuối cùng, khi người dùng ngừng phân chia event. interval sẽ phân phối các event mỗi giây và nếu được đặt thành 3 giây, nó sẽ phân phối 20 event trong phút đó. Điều này được khuyến nghị để tránh lạm dụng, trong các chức năng mà người dùng có thể nhanh chóng nhấp vào một thứ gì đó và có được một số lợi thế (hãy tưởng tượng rằng người dùng có thể kiếm được xu bằng cách nhấp vào thứ gì đó, nếu y nhấp 300 lần trong cùng một phút, y sẽ có 300 xu, bằng cách sử dụng interval, bạn có thể đặt khung thời gian trong 3 giây, và thậm chí sau đó nhấp vào 300 hoặc một nghìn lần, tối đa y sẽ nhận được trong 1 phút sẽ là 20 xu, nhấp 300 hoặc 1 triệu lần). Việc gỡ lỗi này phù hợp cho việc chống DDos, cho các chức năng như tìm kiếm trong đó mỗi thay đổi đối với onChange sẽ gây ra một truy vấn tới api của bạn. Debounce sẽ đợi người dùng ngừng nhập tên để thực hiện yêu cầu. Nếu nó được sử dụng trong kịch bản đồng xu được đề cập ở trên, người dùng sẽ chỉ giành được 1 đồng xu, bởi vì nó chỉ được thực thi, khi người dùng "tạm dừng" trong thời gian đã thiết lập.
* CHÍ Ú: Workers phải luôn được sử dụng khi khởi động Controller hoặc Class, vì vậy nó phải luôn ở trên onInit (được khuyến nghị), phương thức khởi tạo Class hoặc initState của StatefulWidget (phương pháp này không được khuyến khích trong hầu hết các trường hợp, nhưng nó không nên có hiệu ứng phụ nào khác).
## Quản lý State đơn giản
Get có một trình quản lý state cực kỳ nhẹ và dễ dàng, không sử dụng ChangeNotifier, sẽ đáp ứng nhu cầu đặc biệt cho những người mới sử dụng Flutter và sẽ không gây ra sự cố cho các ứng dụng lớn.
GetBuilder nhắm chính xác vào việc kiểm soát nhiều state. Hãy tưởng tượng rằng bạn đã thêm 30 sản phẩm vào giỏ hàng, bạn nhấp vào xóa một sản phẩm, đồng thời danh sách được cập nhật, giá được cập nhật và huy hiệu trong giỏ hàng được cập nhật thành số lượng nhỏ hơn. Kiểu tiếp cận này khiến GetBuilder trở thành kẻ giết người, bởi vì nó nhóm các state và thay đổi tất cả chúng cùng một lúc mà không có bất kỳ "logic tính toán" nào cho điều đó. GetBuilder được tạo ra với loại tình huống này, vì để thay đổi state tạm thời, bạn có thể sử dụng setState và bạn sẽ không cần trình quản lý state cho việc này.
Bằng cách đó, nếu bạn muốn một controller riêng lẻ, bạn có thể gán ID cho controller đó hoặc sử dụng GetX. Điều này là tùy thuộc vào bạn, hãy nhớ rằng bạn càng có nhiều widget "riêng lẻ" thì hiệu suất của GetX càng nổi bật, trong khi hiệu suất của GetBuilder phải vượt trội hơn khi có nhiều thay đổi state.
### Lợi thế
1. Chỉ cập nhật các widget được yêu cầu.
2. Không sử dụng changeNotifier, đó là trình quản lý state sử dụng ít bộ nhớ hơn (gần như bằng 0mb).
3. Quên StatefulWidget! Với Get, bạn sẽ không bao giờ cần đến nó. Với các trình quản lý state khác, bạn có thể sẽ phải sử dụng StatefulWidget để lấy phiên bản của Provider, BLoC, MobX, v.v. Nhưng bạn đã bao giờ nghĩ rằng AppBar, Scaffole và hầu hết các widget trong class của bạn là stateless? Vậy tại sao phải lưu state của toàn bộ class, nếu bạn chỉ có thể lưu state của Widget là stateful? Get giải quyết được điều đó bằng cách tạo một class Stateless, làm cho mọi thứ trở nên vô trạng. Nếu bạn cần cập nhật một thành phần riêng lẻ, hãy bọc nó bằng GetBuilder và state của nó sẽ được duy trì.
4. Tái cơ cấu cho dự án của bạn xanh, sạch và đẹp! Controller không được nằm trong UI của bạn, hãy đặt TextEditController của bạn hoặc bất kỳ controller nào bạn sử dụng trong class Controller của mình.
5. Bạn có cần kích hoạt event để cập nhật widget con ngay khi nó được hiển thị không? GetBuilder có thuộc tính "initState", giống như StatefulWidget và bạn có thể gọi các event từ controller của mình, trực tiếp từ nó, không có thêm event nào được đặt trong initState của bạn.
6. Bạn có cần phải kích hoạt một hành động như đóng streams, hẹn giờ, v.v. không? GetBuilder cũng có dispose property, nơi bạn có thể gọi các event ngay khi widget đó bị phá hủy.
7. Chỉ sử dụng các streams nếu cần thiết. Bạn có thể sử dụng StreamControllers bên trong controller của mình một cách bình thường và sử dụng StreamBuilder cũng bình thường, nhưng hãy nhớ rằng, một streams tiêu thụ bộ nhớ một kha khá, lập trình phản ứng rất đẹp, nhưng bạn không nên lạm dụng nó. 30 streams mở cùng lúc có thể tệ hơn changeNotifier (và changeNotifier đã rất là tệ).
8. Cập nhật các widgets mà không tốn ram. Chỉ lưu trữ ID người tạo GetBuilder và cập nhật GetBuilder đó khi cần thiết. Mức tiêu thụ bộ nhớ của get ID trong bộ nhớ là rất thấp ngay cả đối với hàng nghìn GetBuilders. Khi bạn tạo GetBuilder mới, bạn thực sự đang chia sẻ state GetBuilder có ID người tạo. Một state mới không được tạo cho mỗi GetBuilder, giúp tiết kiệm RẤT NHIỀU ram cho các ứng dụng lớn. Về cơ bản, ứng dụng của bạn sẽ hoàn toàn là Không state và một số ít Tiện ích sẽ có state (trong GetBuilder) sẽ có một state duy nhất, và do đó cập nhật một sẽ cập nhật tất cả. Nhà nước chỉ là một.
9. Get là toàn trí và trong hầu hết các trường hợp, nó biết chính xác thời gian để lấy controller ra khỏi bộ nhớ. Bạn không nên lo lắng về việc khi nào nên vứt bỏ controller, Get biết thời điểm tốt nhất để thực hiện việc này.
### Sử dụng
``` dart
// Create controller class and extends GetxController
class Controller extends GetxController {
int counter = 0;
void increment() {
counter++;
update(); // use update() to update counter variable on UI when increment be called
}
}
// On your Stateless/Stateful class, use GetBuilder to update Text when increment be called
GetBuilder<Controller>(
init: Controller(), // INIT IT ONLY THE FIRST TIME
builder: (_) => Text(
'${_.counter}',
),
)
//Initialize your controller only the first time. The second time you are using ReBuilder for the same controller, do not use it again. Your controller will be automatically removed from memory as soon as the widget that marked it as 'init' is deployed. You don't have to worry about that, Get will do it automatically, just make sure you don't start the same controller twice.
```
**OK, giải thích xong rồi!**
* Bạn đã học cách quản lý state với Get.
* Lưu ý: Bạn có thể muốn một tổ chức lớn hơn và không sử dụng thuộc tính init. Vì vậy, bạn có thể tạo một class và mở rộng class Bindings và trong đó đề cập đến các controller sẽ được tạo trong route đó. Khi đó các Controllers sẽ không được tạo, ngược lại, đây chỉ là một câu lệnh, để lần đầu sử dụng Controller, Get sẽ biết cần tìm ở đâu. Get sẽ vẫn là lazyLoad và sẽ tiếp tục loại bỏ Controller khi chúng không còn cần thiết nữa. Hãy xem ví dụ pub.dev để xem nó hoạt động như thế nào.
Nếu bạn điều hướng nhiều route và cần dữ liệu trong controller đã sử dụng trước đó, bạn chỉ cần sử dụng GetBuilder Again (không có init):
``` dart
class OtherClass extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GetBuilder<Controller>(
builder: (s) => Text('${s.counter}'),
),
),
);
}
```
Nếu bạn cần sử dụng controller của mình ở nhiều nơi khác và bên ngoài GetBuilder, chỉ cần tạo quyền truy cập vào controller của bạn và có nó một cách dễ dàng. (hoặc sử dụng `Get.find <Controller> ()`)
``` dart
class Controller extends GetxController {
/// You do not need that. I recommend using it just for ease of syntax.
/// with static method: Controller.to.increment();
/// with no static method: Get.find<Controller>().increment();
/// There is no difference in performance, nor any side effect of using either syntax. Only one does not need the type, and the other the IDE will autocomplete it.
static Controller get to => Get.find(); // add this line
int counter = 0;
void increment() {
counter++;
update();
}
}
```
Sau đó, truy cập thẳng vào controller của bạn:
``` dart
FloatingActionButton(
onPressed: () {
Controller.to.increment(),
} // This is incredibly simple!
child: Text("${Controller.to.counter}"),
),
```
Khi bạn nhấn FloatingActionButton, tất cả các widget đang lắng nghe biến 'counter' sẽ được cập nhật tự động.
### Cách GetX sử dụng controllers
Ví dụ:
`Class a => Class B (has controller X) => Class C (has controller X)`
Trong class A, controller chưa có trong bộ nhớ, vì bạn chưa sử dụng nó (Get là lazyLoad). Trong class B, bạn đã sử dụng controller và nó đã vào bộ nhớ. Trong class C, bạn đã sử dụng cùng một controller như trong class B, Get sẽ chia sẻ state của controller B với controller C, và controller tương tự vẫn còn trong bộ nhớ. Nếu bạn đóng màn hình C và màn hình B, Get sẽ tự động lấy controller X ra khỏi bộ nhớ và giải phóng tài nguyên, vì Class A không sử dụng controller. Nếu bạn điều hướng đến B một lần nữa, controller X sẽ nhập lại bộ nhớ, nếu thay vì đi đến class C, bạn quay lại class A một lần nữa, Get sẽ đưa controller ra khỏi bộ nhớ theo cách tương tự. Nếu class C không sử dụng controller và bạn đã lấy class B ra khỏi bộ nhớ, thì sẽ không có class nào sử dụng controller X và tương tự như vậy, nó sẽ bị loại bỏ. Ngoại lệ duy nhất có thể gây rắc rối với Get là nếu bạn xóa B khỏi route một cách bất ngờ và cố gắng sử dụng controller trong C. Trong trường hợp này, ID người tạo của controller ở B đã bị xóa và Get được lập trình để xóa nó khỏi bộ nhớ mọi controller không có ID người tạo. Nếu bạn dự định làm điều này, hãy thêm flag "autoRemove: false" vào GetBuilder của class B và sử dụng adoptID = true trong GetBuilder của class C.
### Không cần StatefulWidget nữa!
Sử dụng StatefulWidgets có nghĩa là lưu trữ state của toàn bộ màn hình một cách không cần thiết, ngay cả khi bạn cần xây dựng lại một cách tối thiểu widget, bạn sẽ nhúng nó vào Consumer / Observer / BlocProvider / GetBuilder / GetX / Obx, đây sẽ là một StatefulWidget khác.
Class StatefulWidget là một class lớn hơn StatelessWidget, class này sẽ phân bổ nhiều RAM hơn và điều này có thể không tạo ra sự khác biệt đáng kể giữa một hoặc hai class, nhưng chắc chắn nó sẽ làm được khi bạn có 100 class trong số chúng!
Trừ khi bạn cần sử dụng một mixin, như TickerProviderStateMixin, thì việc sử dụng StatefulWidget với Get là hoàn toàn không cần thiết.
Bạn có thể gọi trực tiếp tất cả các phương thức của StatefulWidget từ GetBuilder.
Nếu bạn cần gọi phương thức initState () hoặc dispose () chẳng hạn, bạn có thể gọi chúng trực tiếp;
``` dart
GetBuilder<Controller>(
initState: (_) => Controller.to.fetchApi(),
dispose: (_) => Controller.to.closeStreams(),
builder: (s) => Text('${s.username}'),
),
```
Một cách tiếp cận tốt hơn nhiều so với cách này là sử dụng phương thức onInit () và onClose () trực tiếp từ controller của bạn.
``` dart
@override
void onInit() {
fetchApi();
super.onInit();
}
```
* CHÍ Ú: Nếu bạn muốn bắt đầu một phương thức tại thời điểm controller được gọi lần đầu tiên, bạn KHÔNG CẦN sử dụng các hàm tạo cho việc này, trên thực tế, bằng cách sử dụng gói hướng hiệu suất như Get, điều này không phù hợp với thực tiễn xấu, bởi vì nó lệch khỏi logic trong đó controller được tạo hoặc chỉ định (nếu bạn tạo một phiên bản của controller này, hàm tạo sẽ được gọi ngay lập tức, bạn sẽ điền controller trước khi nó được sử dụng, bạn đang cấp phát bộ nhớ mà không sử dụng nó , điều này chắc chắn làm hỏng các nguyên tắc của thư viện này). Các phương thức onInit(); và onClose(); được tạo ra cho mục đích này, chúng sẽ được gọi khi Controller được tạo hoặc được sử dụng lần đầu tiên, tùy thuộc vào việc bạn có đang sử dụng Get.lazyPut hay không. Ví dụ: nếu bạn muốn thực hiện lệnh gọi tới API của mình để điền dữ liệu, bạn có thể quên phương thức cũ của initState / dispose, chỉ cần bắt đầu lệnh gọi tới api trong onInit. Nếu bạn cần thực thi bất kỳ lệnh nào, như đóng streams, hãy sử dụng onClose() cho việc đó.
### Tại sao GetX tồn tại?
Mục đích của gói này chính xác là cung cấp cho bạn một giải pháp hoàn chỉnh để điều hướng các route, quản lý các dependency và state, sử dụng các dependency ít nhất có thể, với mức độ tách biệt cao. Nhận tất cả các API Flutter cấp cao và cấp thấp trong chính nó, để đảm bảo rằng bạn làm việc với ít khớp nối nhất có thể. Chúng tôi tập trung mọi thứ trong một gói duy nhất, để đảm bảo rằng bạn không có bất kỳ loại khớp nối nào trong dự án của mình. Bằng cách đó, bạn có thể chỉ đặt các widget trong chế độ xem của mình và để phần của nhóm làm việc với logic nghiệp vụ tự do làm việc với logic nghiệp vụ độc lập với View. Điều này cung cấp một môi trường làm việc sạch hơn nhiều, để một phần nhóm của bạn chỉ hoạt động với các widget mà không phải lo lắng về việc gửi dữ liệu đến controller của bạn và một phần nhóm của bạn chỉ làm việc với logic nghiệp vụ trong phạm vi bề rộng của nó mà không dependency vào bất kỳ yếu tố View.
Vì vậy, để đơn giản hóa điều này:
Bạn không cần gọi các phương thức trong initState và gửi chúng theo tham số đến controller của mình, cũng như không sử dụng phương thức khởi tạo controller cho việc đó, bạn có phương thức onInit() được gọi vào đúng thời điểm để bạn khởi động các dịch vụ của mình.
Bạn không cần phải gọi thiết bị, bạn có phương thức onClose() sẽ được gọi vào thời điểm chính xác khi controller của bạn không còn cần thiết nữa và sẽ bị xóa khỏi bộ nhớ. Bằng cách đó, chỉ để lại chế độ xem cho các widget, tránh bất kỳ loại logic nghiệp vụ nào từ nó.
Đừng gọi một phương thức vứt bỏ bên trong GetxController, nó sẽ không làm được gì cả, hãy nhớ rằng controller không phải là một Widget, bạn không nên "vứt bỏ" nó, và nó sẽ được Get tự động và thông minh xóa khỏi bộ nhớ. Nếu bạn đã sử dụng bất kỳ streams nào trên đó và muốn đóng streams đó, chỉ cần chèn streams đó vào phương thức đóng. Thí dụ:
``` dart
class Controller extends GetxController {
StreamController<User> user = StreamController<User>();
StreamController<String> name = StreamController<String>();
/// close stream = onClose method, not dispose.
@override
void onClose() {
user.close();
name.close();
super.onClose();
}
}
```
Vòng đời của controller:
* onInit() nơi nó được tạo.
* onClose() nơi nó được đóng để thực hiện bất kỳ thay đổi nào nhằm chuẩn bị cho phương thức xóa
* deleted: bạn không có quyền truy cập vào API này vì nó sẽ xóa controller khỏi bộ nhớ theo đúng nghĩa đen. Nó được xóa theo đúng nghĩa đen, mà không để lại bất kỳ dấu vết nào.
### Cách sử dụng khác
Bạn có thể sử dụng phiên bản Controller trực tiếp trên giá trị GetBuilder:
``` dart
GetBuilder<Controller>(
init: Controller(),
builder: (value) => Text(
'${value.counter}', //here
),
),
```
Bạn cũng có thể cần một phiên bản của controller bên ngoài GetBuilder và bạn có thể sử dụng các phương pháp này để đạt được điều này:
``` dart
class Controller extends GetxController {
static Controller get to => Get.find();
[...]
}
// on you view:
GetBuilder<Controller>(
init: Controller(), // use it only first time on each controller
builder: (_) => Text(
'${Controller.to.counter}', //here
)
),
```
or
``` dart
class Controller extends GetxController {
// static Controller get to => Get.find(); // with no static get
[...]
}
// on stateful/stateless class
GetBuilder<Controller>(
init: Controller(), // use it only first time on each controller
builder: (_) => Text(
'${Get.find<Controller>().counter}', //here
),
),
```
* Bạn có thể sử dụng các phương pháp tiếp cận "không chuẩn" để thực hiện việc này. Nếu bạn đang sử dụng một số trình quản lý dependency khác, như get_it, modular, v.v. và chỉ muốn cung cấp phiên bản controller, bạn có thể thực hiện điều này:
``` dart
Controller controller = Controller();
[...]
GetBuilder<Controller>(
init: controller, //here
builder: (_) => Text(
'${controller.counter}', // here
),
),
```
### IDs độc nhất
Nếu bạn muốn tinh chỉnh kiểm soát cập nhật của widget con với GetBuilder, bạn có thể gán cho chúng các ID độc:
``` dart
GetBuilder<Controller>(
id: 'text'
init: Controller(), // use it only first time on each controller
builder: (_) => Text(
'${Get.find<Controller>().counter}', //here
),
),
```
Và cập nhật nó vào biểu mẫu này:
``` dart
update(['text']);
```
Bạn cũng có thể áp đặt các điều kiện cho bản cập nhật:
``` dart
update(['text'], counter < 10);
```
GetX thực hiện điều này tự động và chỉ cấu trúc lại widget con sử dụng biến chính xác đã được thay đổi, nếu bạn thay đổi một biến thành giống với biến trước đó và điều đó không ngụ ý thay đổi state, GetX sẽ không xây dựng lại widget con để tiết kiệm bộ nhớ và Chu kỳ CPU ( 3 đang được hiển thị trên màn hình và bạn lại thay đổi biến thành 3. Trong hầu hết các trình quản lý state, điều này sẽ gây ra việc xây dựng lại mới, nhưng với GetX, widget sẽ chỉ được xây dựng lại, nếu trên thực tế state của nó đã thay đổi ).
## Trộn hai trình quản lý state
Một số người đã mở một yêu cầu tính năng, vì họ chỉ muốn sử dụng một loại biến phản ứng và cơ chế khác và cần chèn Obx vào GetBuilder cho việc này. Suy nghĩ về nó MixinBuilder đã được tạo ra. Nó cho phép cả những thay đổi phản ứng bằng cách thay đổi các biến ".obs" và cập nhật thủ công thông qua update(). Tuy nhiên, trong số 4 widget, nó là widget tiêu tốn nhiều tài nguyên nhất, vì ngoài việc có Subscription để nhận các event thay đổi từ con mình, nó còn đăng ký phương thức cập nhật của controller của mình.
Việc mở rộng GetxController rất quan trọng, vì chúng có vòng đời và có thể "bắt đầu" và "kết thúc" các event trong các phương thức onInit() và onClose() của chúng. Bạn có thể sử dụng bất kỳ lớp nào cho việc này, nhưng tôi thực sự khuyên bạn nên sử dụng lớp GetxController để đặt các biến của bạn, cho dù chúng có thể quan sát được hay không.
## StateMixin
Một cách khác để xử lý state `UI` của bạn là sử dụng` StateMixin <T> `.
Để triển khai nó, hãy sử dụng dấu `với` để thêm` StateMixin <T> ` bộ điều khiển của bạn cho phép một mô hình T.
``` dart
class Controller extends GetController with StateMixin<User>{}
```
Phương thức `change()` thay đổi state bất cứ khi nào chúng ta muốn.
Chỉ cần truyền dữ liệu và state theo cách này:
```dart
change(data, status: RxStatus.success());
```
RxStatus cho phép các state này:
``` dart
RxStatus.loading();
RxStatus.success();
RxStatus.empty();
RxStatus.error('message');
```
Để diễn tả nó trên UI, sử dụng:
```dart
class OtherClass extends GetView<Controller> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: controller.obx(
(state)=>Text(state.name),
// here you can put your custom loading indicator, but
// by default would be Center(child:CircularProgressIndicator())
onLoading: CustomLoadingIndicator(),
onEmpty: Text('No data found'),
// here also you can set your own error widget, but by
// default will be an Center(child:Text(error))
onError: (error)=>Text(error),
),
);
}
```
## GetBuilder vs GetX vs Obx vs MixinBuilder
Trong một thập kỷ làm việc với lập trình, tôi đã có thể học được một số bài học quý giá.
Lần đầu tiên tôi tiếp xúc với lập trình phản ứng là rất "Trời thần ơi, tuyệt vời ông mặt trời!" và trên thực tế, lập trình phản ứng là không thể tin được.
Tuy nhiên, nó không phải là thích hợp cho tất cả các trường hợp. Thông thường, tất cả những gì bạn cần là thay đổi state của 2 hoặc 3 widget cùng lúc, hoặc thay đổi state trong thời gian ngắn, trong trường hợp này, lập trình phản ứng không phải là xấu, nhưng nó không phù hợp.
Lập trình phản ứng có mức tiêu thụ RAM cao hơn có thể được bù đắp bởi quy trình làm việc riêng lẻ, điều này sẽ đảm bảo rằng chỉ một widget con được xây dựng lại và khi cần thiết, nhưng tạo danh sách với 80 đối tượng, mỗi đối tượng có nhiều streams không phải là một ý kiến hay . Mở thanh kiểm tra phi tiêu và kiểm tra xem StreamBuilder tiêu thụ bao nhiêu và bạn sẽ hiểu những gì tôi đang cố gắng nói với bạn.
Với ý nghĩ đó, tôi đã tạo trình quản lý state đơn giản. Nó đơn giản, và đó chính xác là những gì bạn cần ở nó: cập nhật state trong các khối theo cách đơn giản và tiết kiệm nhất.
GetBuilder rất tiết kiệm RAM và khó có cách tiếp cận nào tiết kiệm hơn nó (ít nhất là tôi không thể tưởng tượng được, nếu đã tồn tại cách khác, vui lòng cho chúng tôi biết).
Tuy nhiên, GetBuilder vẫn là một trình quản lý state cơ học, bạn cần phải gọi update () giống như bạn sẽ cần gọi tới Provider's InformListaries ().
Có những tình huống khác mà lập trình phản ứng thực sự thú vị và nếu không dùng nó đồng nghĩa như đang phát minh lại cái bánh xe. Với suy nghĩ đó, GetX được tạo ra để cung cấp mọi thứ hiện đại và tiên tiến nhất trong một trình quản lý state. Nó chỉ cập nhật những gì cần thiết và khi cần thiết, nếu bạn gặp lỗi và gửi 300 state thay đổi đồng thời, GetX sẽ lọc và cập nhật màn hình chỉ khi state thực sự thay đổi.
GetX vẫn tiết kiệm hơn bất kỳ trình quản lý state phản ứng nào khác, nhưng nó tiêu tốn nhiều RAM hơn GetBuilder một chút. Suy nghĩ về điều đó và hướng tới việc tiêu thụ tối đa tài nguyên mà Obx đã tạo ra. Không giống như GetX và GetBuilder, bạn sẽ không thể khởi tạo controller bên trong Obx, nó chỉ là một Widget với StreamSubscription nhận các event thay đổi từ con bạn, vậy thôi. Nó tiết kiệm hơn GetX, nhưng thua GetBuilder, điều được mong đợi, vì nó có tính phản ứng và GetBuilder có cách tiếp cận đơn giản nhất tồn tại, đó là lưu trữ hashCode của widget con và StateSetter của nó. Với Obx, bạn không cần phải viết loại controller của mình và bạn có thể nghe thấy sự thay đổi từ nhiều controller khác nhau, nhưng nó cần được khởi tạo trước đó, sử dụng phương pháp ví dụ ở đầu readme này hoặc sử dụng class Bindings.
... ...