Jonny Borges
Committed by GitHub

Merge pull request #779 from RenatFakhrutdinov/master

Translation of documentation into Russian
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
_Языки: Русский (этот файл), [Английский](README.md), [Китайский](README.zh-cn.md), [Бразильский Португальский](README.pt-br.md), [Испанский](README-es.md), [Польский](README.pl.md)._
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![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)
- [Про Get](#про-get)
- [Установка](#установка)
- [Приложение "Счётчик" с GetX](#приложение-счётчик-с-getx)
- [Три столпа](#три-столпа)
- [Управление состоянием](#управление-состоянием)
- [Реактивное управление состоянием](#реактивное-управление-состоянием)
- [Подробнее об управлении состоянием](#подробнее-об-управлении-состоянием)
- [Управление маршрутами](#управление-маршрутами)
- [Подробнее об управлении маршрутами](#подробнее-об-управлении-маршрутами)
- [Внедрение зависимостей](#внедрение-зависимостей)
- [Подробнее о внедрении зависимостей](#подробнее-о-внедрении-зависимостей)
- [Утилиты](#утилиты)
- [Интернационализация](#интернационализация)
- [Переводы](#переводы)
- [Применение переводов](#применение-переводов)
- [Локализация](#локализация)
- [Изменение локализации](#изменение-локализации)
- [Системная локализация](#системная-локализации)
- [Изменение темы](#изменение-темы)
- [Другие API](#другие-api)
- [Дополнительные глобальные настройки и ручные настройки](#дополнительные-глобальные-настройки-и-ручные-настройки)
- [Локальные виджеты состояния](#локальные-виджеты-состояний)
- [ValueBuilder](#valuebuilder)
- [ObxValue](#obxvalue)
- [Полезные советы](#полезные-советы)
- [GetView](#getview)
- [GetWidget](#getwidget)
- [GetxService](#getxservice)
- [Критические изменения по сравнению с версией 2.0](#критические-изменения-по-сравнению-с-версией-20)
- [Почему Getx?](#почему-getx)
- [Сообщество](#сообщество)
- [Каналы сообщества](#каналы-сообщества)
- [Как внести свой вклад](#как-внести-свой-вклад)
- [Статьи и видео](#статьи-и-видео)
# Про Get
- GetX - это сверхлегкое и мощное решение для Flutter. Оно совмещает в себе высокопроизводительное управление состоянием, интеллектуальное внедрение зависимостей, управление маршрутами быстрым и практичным способом.
- GetX имеет 3 базовых принципа, являющихся приоритетом для всех ресурсов в библиотеке
- **Производительность:** GetX сфокусирован на производительности и минимальном потреблении ресурсов. Бенчмарки почти всегда не имеют значения в реальном мире, но, если Вам угодно, здесь ([бенчмарки](https://github.com/jonataslaw/benchmarks)) есть индикаторы потребления, где GetX работает лучше, чем другие подходы к управлению состоянием. Разница небольшая, но демонстрирует нашу заботу о ресурсах.
- **Продуктивность:** GetX использует простой и приятный синтаксис. Не имеет значения, что вы хотите сделать, всегда есть более легкий способ с GetX. Это сэкономит часы разработки и обеспечит максимальную производительность, которую может обеспечить ваше приложение.
- **Организация:** GetX позволяет полностью разделить представление, логику представления, бизнес-логику, внедрение зависимостей и навигацию. Вам не нужен контекст для навигации между маршрутами, поэтому вы не зависите от дерева виджетов. Вам не нужен контекст для доступа к вашим контроллерам / блокам через наследуемый виджет, поэтому вы полностью отделяете логику представления и бизнес-логику от уровня визуализации. Вам не нужно внедрять классы Controllers / Models / Blocs в дерево виджетов через мультипровайдеры, поскольку GetX использует собственную функцию внедрения зависимостей, полностью отделяя DI от его представления.
С GetX вы знаете, где найти каждую функцию вашего приложения, имея чистый код по умолчанию. Это, помимо упрощения обслуживания, делает возможным совместное использование модулей, что до того момента во Flutter было немыслимо.
BLoC был отправной точкой для организации кода во Flutter, он отделяет бизнес-логику от визуализации. Getx является естественным развитием этого, разделяя не только бизнес-логику, но и логику представления. Дополнительное внедрение зависимостей и маршрутов также разделено, и уровень данных не учитывается. Вы знаете, где все находится, и это проще, чем написать "Hello World".
GetX - это самый простой, практичный и масштабируемый способ создания высокопроизводительных приложений с помощью Flutter SDK с большой экосистемой вокруг него, которая отлично работает, прост для новичков и точен для экспертов. Он безопасен, стабилен, актуален и предлагает огромный набор встроенных API, которых нет в Flutter SDK по умолчанию.
- GetX не раздут. Он имеет множество функций, которые позволяют вам начать программировать, ни о чем не беспокоясь, но каждая из этих функций находится в отдельных контейнерах и запускается только после использования. Если вы используете только управление состоянием, то будет скомпилировано только управление состоянием. Если вы используете маршрутизацию, то ничего из управления состоянием не будет скомпилировано. Вы можете воспользоваться репозиторием бенчмарка, и вы увидите, что используя только управление состоянием Get, приложение, которое скомпилировано с помощью Get, имеет меньший размер, чем приложения использующие другие пакеты для управления состоянием, потому что всё, что не используется, не будет скомпилировано в Ваш код. Таким образом каждое решение GetX было спроектировано, чтобы быть сверхлёгким. Также в этом есть и заслуга Flutter, который умеет устранять неиспользуемые ресурсы, как ни один другой фреймворк.
- Getx имеет огромную экосистему, способную работать с одним и тем же кодом на Android, iOS, в Интернете, Mac, Linux, Windows и на вашем сервере.
**С помощью [Get Server](https://github.com/jonataslaw/get_server) ваш код, созданный на веб-интерфейсе, можно повторно использовать на вашем сервере.**
**Кроме того, весь процесс разработки может быть полностью автоматизирован как на сервере, так и во внешнем интерфейсе с помощью [Get CLI](https://github.com/jonataslaw/get_cli)**.
**Кроме того, для дальнейшего повышения вашей продуктивности у нас есть [расширение для VSCode](https://marketplace.visualstudio.com/items?itemName=get-snippets.get-snippets) и [расширение для Android Studio / Intellij](https://plugins.jetbrains.com/plugin/14975-getx-snippets).**
# Установка
Добавьте Get в файл pubspec.yaml:
```yaml
dependencies:
get:
```
Импортируйте Get в файлы, в которых планируете его использовать:
```dart
import 'package:get/get.dart';
```
# Приложение "Счётчик" с GetX
Проект "Счётчик", созданный по умолчанию для нового проекта на Flutter, имеет более 100 строк (с комментариями). Чтобы показать возможности Get, я продемонстрирую, как сделать "Счётчик", изменяющий состояние при каждом клике, переключении между страницами и передаче состояния между экранами. Всё это вместе с разделением бизнес логики от представления занимает ВСЕГО ЛИШЬ 26 СТРОК КОДА, ВКЛЮЧАЯ КОММЕНТАРИИ.
- Шаг 1:
Добавьте "Get" перед вашим MaterialApp, превращая его в GetMaterialApp
```dart
void main() => runApp(GetMaterialApp(home: Home()));
```
- Примечание: это не изменяет MaterialApp, GetMaterialApp не является модифицированным MaterialApp, это просто предварительно настроенный виджет, у которого в качестве дочернего по умолчанию используется MaterialApp. Вы можете настроить это вручную, но это не обязательно. GetMaterialApp будет создавать маршруты, вводить их, вводить переводы, вводить всё, что вам нужно для навигации. Если вы используете Get только для управления состоянием или зависимостями, нет необходимости использовать GetMaterialApp. GetMaterialApp необходим для навигации, снекбаров, интернационализации, bottomSheets, диалогов и API, связанных с маршрутами и отсутствием контекста.
- Примечание²: Этот шаг необходим только в том случае, если вы собираетесь использовать управление маршрутами (`Get.to()`, `Get.back()` и так далее). Если вы не собираетесь его использовать, то шаг 1 выполнять необязательно.
- Шаг 2:
Создайте свой класс бизнес-логики и поместите в него все переменные, методы и контроллеры.
Вы можете сделать любую переменную наблюдаемой, используя простой ".obs".
```dart
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
}
```
- Шаг 3:
Создайте свой View, используйте StatelessWidget и сэкономьте немного оперативной памяти, с Get вам больше не нужно использовать StatefulWidget.
```dart
class Home extends StatelessWidget {
// Instantiate your class using Get.put() to make it available for all "child" routes there.
final Controller c = Get.put(Controller());
@override
Widget build(context) => 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: RaisedButton(
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}")));
}
}
```
Результат:
![](counter-app-gif.gif)
Это простой проект, но он уже дает понять, насколько мощным является Get. По мере роста вашего проекта эта разница будет становиться все более значительной.
Get был разработан для работы с командами, но он упрощает работу отдельного разработчика.
Оптимизируйте ваши сроки, доставляйте всё вовремя без потери производительности. Get не для всех, но, если вы идентифицировали себя с предыщим предложением, Get для вас!
# Три столпа
## Управление состоянием
В настоящее время для Flutter есть несколько менеджеров состояний. Однако большинство из них связано с использованием ChangeNotifier для обновления виджетов, и это плохой и очень плохой подход к производительности для средних или больших приложений. Вы можете проверить в официальной документации Flutter, что [ChangeNotifier следует использовать с 1 или максимум 2 слушателями](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html), что делает его практически непригодным для любого приложения среднего или большого размера.
Get не лучше и не хуже, чем любой другой менеджер состояний, но вам следует проанализировать его, а также пункты ниже, чтобы выбрать между использованием Get в чистой форме (Vanilla), либо совместно с другим менеджером состояний.
Определенно, Get не враг любого другого менеджера состояний, потому что Get - это микрофреймворк, а не просто менеджер состояний, и его можно использовать отдельно или вместе с ними.
Get имеет два разных менеджера состояний: простой менеджер состояний (мы назовем его GetBuilder) и реактивный менеджер состояний (который называется GetX).
### Реактивное управление состоянием
Реактивное программирование может оттолкнуть многих людей, потому что считается сложным. GetX превращает реактивное программирование в нечто довольно простое:
- Вам не нужно создавать StreamControllers.
- Вам не нужно создавать StreamBuilder для каждой переменной.
- Вам не нужно создавать класс для каждого состояния.
- Вам не нужно создавать геттер начального значения.
Реактивное программирование с Get так же просто, как использование setState.
Представим, что у вас есть переменная name и вы хотите, чтобы каждый раз, когда вы её изменяете, все виджеты, которые её используют, менялись автоматически.
Это ваша переменная:
```dart
var name = 'Jonatas Borges';
```
Чтобы сделать его наблюдаемым, вам просто нужно добавить в конец ".obs":
```dart
var name = 'Jonatas Borges'.obs;
```
А в пользовательском интерфейсе, если вы хотите отображать это значение и обновлять экран при изменении значений, просто сделайте следующее:
```dart
Obx(() => Text("${controller.name}"));
```
Вот и всё. Это _так_ просто.
### Подробнее об управлении состоянием
**Более подробное объяснение управления состоянием [здесь](./documentation/ru_RU/state_management.md). Там вы увидите больше примеров, а также разницу между простым менеджером состояния и реактивным менеджером состояния.**
Вы получите хорошее представление о мощности GetX.
## Управление маршрутами
Если вы собираетесь использовать маршруты / снекбары / диалоги / bottomsheets без контекста, GetX отлично подойдёт вам, просто посмотрите:
Добавьте "Get" перед MaterialApp, превратив его в GetMaterialApp.
```dart
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
```
Перейдите на новый экран:
```dart
Get.to(NextScreen());
```
Перейдите на новый экран с именем. Более подробную информацию об именованных маршрутах смотрите [здесь](./documentation/ru_RU/route_management.md#navigation-with-named-routes)
```dart
Get.toNamed('/details');
```
Закрыть снекбар, диалог, bottomsheets, или что-то иное, что вы обычно закрывали с помощью Navigator.pop(context);
```dart
Get.back();
```
Для перехода к следующему экрану без возможности вернуться к предыдущему экрану (для использования в SplashScreens, экранах входа и т. д.)
```dart
Get.off(NextScreen());
```
Для перехода к следующему экрану и отмены всех предыдущих маршрутов (полезно в корзинах для покупок, опросах и тестах)
```dart
Get.offAll(NextScreen());
```
Заметили, что вам не нужно было использовать контекст, чтобы делать что-либо из этого? Это одно из самых больших преимуществ использования Get. Благодаря этому вы можете без проблем выполнять все эти методы из класса контроллера.
### Подробнее об управлении маршрутами
**Get работает с именованными маршрутами, а также предлагает более низкий уровень контроля над вашими маршрутами! [Здесь](./documentation/ru_RU/route_management.md) есть подробная документация.**
## Внедрение зависимостей
Get имеет простой и мощный менеджер зависимостей, который позволяет вам получить тот же класс, что и ваш BLoC или контроллер, всего одной строкой кода, без Provider context, без InheritedWidget:
```dart
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
```
- Примечание: Если вы используете Get State Manager, обратите больше внимания на API привязок, который упростит подключение вашего представления к контроллеру.
Вместо того, чтобы создавать экземпляр вашего класса внутри класса, который вы используете, вы создаете его в экземпляре Get, что сделает его доступным во всем приложении. Таким образом, вы можете использовать свой контроллер (или BLoC) в обычном режиме.
**Совет:** Управление зависимостями Get не связано с другими частями пакета, поэтому, если, например, ваше приложение уже использует менеджер состояний (любой, не имеет значения), вам не нужно все это переписывать, вы можете использовать это внедрение зависимостей без проблем.
```dart
controller.fetchApi();
```
Представьте, что вы прошли через множество маршрутов и вам нужны данные, которые остались в вашем контроллере, вам понадобится менеджер состояний в сочетании с Provider или Get_it, верно? Только не с Get. Вам просто нужно попросить Get «найти» ваш контроллер, никаких дополнительных зависимостей вам не потребуется:
```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.
```
И тогда вы сможете восстановить данные вашего контроллера, которые были там получены:
```dart
Text(controller.textFromApi);
```
### Подробнее о внедрении зависимостей
**Более подробное объяснение управления зависимостями [здесь](./documentation/ru_RU/dependency_management.md)**
# Утилиты
## Интернационализация
### Переводы
Переводы хранятся в виде карты пар "ключ-значение". Чтобы добавить собственные переводы, создайте класс и расширьте `Translations`.
```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',
}
};
}
```
#### Использование переводов
Просто добавьте `.tr` к указанному ключу, и он будет переведен с использованием текущего значения `Get.locale` и `Get.fallbackLocale`.
```dart
Text('title'.tr);
```
### Локализация
Передайте параметры в `GetMaterialApp`, чтобы определить языковой стандарт и переводы.
```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.
);
```
#### Изменение локализации
Вызовите `Get.updateLocale(locale)`, чтобы обновить локализацию. Затем переводы автоматически используют новый языковой стандарт.
```dart
var locale = Locale('en', 'US');
Get.updateLocale(locale);
```
#### Системная локализация
Чтобы узнать системную локализацию, вам следует использовать `window.locale`.
```dart
import 'dart:ui' as ui;
return GetMaterialApp(
locale: ui.window.locale,
);
```
## Изменение темы
Пожалуйста, не используйте виджет более высокого уровня, чем `GetMaterialApp`, для его обновления. Это может вызвать повторяющиеся ключи. Многие люди привыкли к старому подходу к созданию виджета «ThemeProvider» только для того, чтобы изменить тему вашего приложения, а это НЕ требуется с GetX ™.
Вы можете создать свою собственную тему и просто добавить ее в `Get.changeTheme` без повторяющегося кода:
```dart
Get.changeTheme(ThemeData.light());
```
Если вы хотите создать что-то вроде кнопки, которая изменяет тему, вы можете объединить для этого два API **GetX ™**:
- API, который проверяет, используется ли темная тема.
- И API смены темы.
Вы можете просто поместить это в `onPressed`:
```dart
Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
```
Когда `.darkmode` активен, он переключится на _light theme_, и когда _light theme_ станет активной, он изменится на _dark theme_.
## Другие API
```dart
// give the current args from currentScreen
Get.arguments
// give arguments of previous route
Get.previousArguments
// give name of previous route
Get.previousRoute
// give the raw route to access for example, rawRoute.isFirst()
Get.rawRoute
// give access to Rounting 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
// Note: the following methods are extensions on context. Since 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>()
```
### Дополнительные глобальные настройки и ручные настройки
GetMaterialApp настраивает все за вас, но если вы хотите настроить Get вручную.
```dart
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [GetObserver()],
);
```
Вы также сможете использовать собственное промежуточное ПО в `GetObserver`, это ни на что не повлияет.
```dart
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer) // Here
],
);
```
Вы можете создать _Глобальные Настройки_ Для `Get`. Просто добавьте `Get.config` в ваш код прежде чем нажимать на любой из маршрутов.
Или сделайте это прямо в `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
)
```
При желании, вы сможете перенаправить все сообщения из `Get`.
Если вы хотите использовать свой любимый пакет для логирования и собирать логи там:
```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
}
```
### Локальные виджеты состояния
Эти виджеты позволяют управлять одним значением, сохраняя состояние эфемерным и локальным.
У нас есть варианты для Reactive и Simple.
Например, вы можете использовать их для переключения obscureText в `TextField`, возможно, для создания кастомного ExpandablePanel или, возможно, для изменения текущего индекса в `BottomNavigationBar` при изменении содержимого body в `Scaffold`.
#### ValueBuilder
Упрощение `StatefulWidget` который работает с вызовом `.setState` принимающим обновленное значение.
```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
Похож на [`ValueBuilder`](#valuebuilder), но это реактивная версия, вы передаёте Rx экземпляр (помните волшебный .obs?) и
автоматически обновляетесь... разве это не великолепно?
```dart
ObxValue((data) => Switch(
value: data.value,
onChanged: data, // Rx has a _callable_ function! You could use (flag) => data.value = flag,
),
false.obs,
),
```
## Полезные советы
`.obs`ervables (наблюдатели) (также известные как Rx-типы) имеют широкий спектр внутренних методов и операторов
> Очень распространено _мнение_, что свойство с `.obs` **ЯВЛЯЕТСЯ** действительным значением... но не ошибайтесь!
> Мы избегаем объявления типа переменной, потому что компилятор Dart достаточно умен, и
> код выглядит чище, но:
```dart
var message = 'Hello world'.obs;
print( 'Message "$message" has Type ${message.runtimeType}');
```
Даже если `message` _выводит_ значение String, его тип - **RxString**!
Итак, вы не сможете сделать `message.substring( 0, 4 )`.
Вы должны получить доступ к реальному `value` внутри _observable_:
Самый "используемый способ" это `.value`, но, знаете ли вы, что вы также можете использовать ...
```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: 'John', last: 'Doe', age: 33).obs;
// `user` is "reactive", but the properties inside ARE NOT!
// So, if we change some variable inside of it...
user.value.name = 'Roi';
// 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='Roi';
});
print( user );
```
#### GetView
Я люблю этот виджет, он такой простой, но такой полезный!
Это`const Stateless` виджет, который имеет геттер `controller` для зарегистрированного `Controller`, вот и всё.
```dart
class AwesomeController extends GetxController {
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`
);
}
}
```
#### GetWidget
Большинство людей понятия не имеют об этом виджете или путаются при его применении.
Вариант его использования редок, но конкретен: он кэширует Controller.
Поэтому из-за _cache_, он не может быть `const Stateless`.
> Итак, когда вам нужно «кэшировать» контроллер?
В случаях использования другой "не распространённой" фичи **GetX**: `Get.create()`.
`Get.create(()=>Controller())` будет создавать новый `Controller` каждый раз при вызове
`Get.find<Controller>()`,
Это тот самый случай, когда `GetWidget` блистает... поскольку вы можете использовать его, например, для хранения списка элементов Todo. Итак, если виджет будет «перестроен», он сохранит тот же экземпляр контроллера.
#### GetxService
Этот класс похож на `GetxController`, у него такой же жизненный цикл ( `onInit()`, `onReady()`, `onClose()`).
Но внутри нет никакой «логики». Он просто уведомляет систему **GetX** Dependency Injection о том, что этот подкласс **нельзя** удалить из памяти.
Так что очень полезно держать ваши «Сервисы» всегда доступными и активными с помощью `Get.find()`. Например:
`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!');
}
}
```
Единственный способ удалить `GetxService` - использовать `Get.reset()`, который похож на «горячую перезагрузку» вашего приложения. Так что помните, если вам нужен постоянный экземпляр класса в течение всего жизненного цикла вашего приложения, используйте `GetxService`.
# Критические изменения по сравнению с версией 2.0
1- Rx типы:
| До | После |
| ------- | ---------- |
| StringX | `RxString` |
| IntX | `RxInt` |
| MapX | `RxMap` |
| ListX | `RxList` |
| NumX | `RxNum` |
| DoubleX | `RxDouble` |
RxController и GetBuilder теперь объединены, вам больше не нужно запоминать, какой контроллер вы хотите использовать, просто используйте GetxController, он будет работать как для простого управления состоянием, так и для реактивного.
2- NamedRoutes
До:
```dart
GetMaterialApp(
namedRoutes: {
'/': GetRoute(page: Home()),
}
)
```
Сейчас:
```dart
GetMaterialApp(
getPages: [
GetPage(name: '/', page: () => Home()),
]
)
```
Для чего это изменение?
Часто может потребоваться решить, какая страница будет отображаться с помощью параметра или токена входа, предыдущий подход был негибким, так как он не позволял этого.
Вставка страницы в функцию значительно снизила потребление оперативной памяти, поскольку маршруты не будут выделяться в памяти с момента запуска приложения, а также позволил использовать такой подход:
```dart
GetStorage box = GetStorage();
GetMaterialApp(
getPages: [
GetPage(name: '/', page:(){
return box.hasData('token') ? Home() : Login();
})
]
)
```
# Почему Getx?
1- Много раз после обновления Flutter многие из ваших пакетов ломались. Иногда случаются ошибки компиляции, часто возникают ошибки, на которые до сих пор нет ответов, и разработчику необходимо знать, откуда возникла ошибка, отслеживать ошибку, только затем попытаться открыть проблему в соответствующем репозитории и увидеть, как проблема решена. Get централизует основные ресурсы для разработки (управление состоянием, зависимостями и маршрутами), позволяя вам добавить один пакет в свой pubspec и начать работу. После обновления Flutter единственное, что вам нужно сделать, это обновить зависимость Get и приступить к работе. Get также решает проблемы совместимости. Как часто бывало, что одна версия пакета несовместима с другой, потому что одна использует зависимость в одной версии, а другая - в другой? Это не проблема при использовании Get, поскольку все находится в одном пакете и полностью совместимо.
2- Flutter - это просто, Flutter - это невероятно, но у Flutter все еще некоторый шаблонный код, который может быть нежелательным для большинства разработчиков, например `Navigator.of(context).push (context, builder [...]`. Get упрощает разработку. Вместо того, чтобы писать 8 строк кода для вызова маршрута, вы можете просто сделать это: `Get.to(Home())` и всё готово, вы перейдёте на следующую страницу. Динамические URL-адреса - это действительно болезненная вещь, которую нужно решать во Flutter в настоящее время, а с GetX это элементарно. Управление состояниями во Flutter и управление зависимостями также вызывает много споров, поскольку в pub есть сотни паттернов. Но нет ничего проще, чем добавить «.obs» в конец вашей переменной и поместить ваш виджет внутри Obx, и всё, все обновления этой переменной будут автоматически обновляться на экране.
3- Лёгкость, не беспокоясь о производительности. Производительность Flutter уже потрясающая, но представьте, что вы используете диспетчер состояний и локатор для распределения классов блоков / хранилищ / контроллеров / и других классов. Вам придётся вручную вызывать исключение этой зависимости, когда она вам не нужна. Но вы когда-нибудь думали о том, чтобы просто использовать свой контроллер, и когда он больше никем не использовался, он просто был бы удален из памяти? Это то, что делает GetX. Благодаря SmartManagement всё, что не используется, удаляется из памяти, и вам не нужно беспокоиться ни о чем, кроме программирования. Вы будете уверены, что потребляете минимум необходимых ресурсов, даже не создав для этого логики.
4- Действительное разделение. Вы могли слышать о концепции разделения представления от бизнес логики. Это не исключительная особенность BLoC, MVC, MVVM и тд, любой стандарт реализует эту концепцию. Однако во Flutter возможно ослабление этой концепции из-за необходимости использования контекста.
Если вам нужен контекст для поиска InheritedWidget, он вам нужен в представлении, либо нужно передать контекст как параметр. Мы считаем это решение очень уродливым, и для работы в команде мы всегда будем зависеть от логики представления (View). Getx - необычный подход со стандартным доступом, который хоть и не запрещает использование StatefulWidgets, InitState, и т.д., всегда имеет более чистый аналог. У контроллеров есть жизненные циклы, и когда вам нужно сделать, например, запрос APIREST, вы не зависите ни от чего в представлении. Вы можете использовать onInit для инициирования http-вызова, и когда данные поступят, переменные будут заполнены. Поскольку GetX полностью реактивен (действительно реактивен и работает с потоками), после заполнения элементов все виджеты, использующие эту переменную, будут автоматически обновлены в представлении. Это позволяет людям с опытом работы с пользовательским интерфейсом работать только с виджетами и не отправлять в бизнес-логику ничего, кроме пользовательских событий (например, нажатия кнопки), в то время как люди, работающие с бизнес-логикой, смогут создавать и тестировать бизнес-логику отдельно.
Эта библиотека всегда будет обновляться и реализовывать новые функции. Не стесняйтесь предлагать PR.
# Сообщества
## Каналы сообщества
У GetX очень активное и готовое к взаимовыручке сообщество. Если у вас есть вопросы или вы хотите получить какую-либо помощь относительно использования этого фреймворка, присоединяйтесь к нашим каналам сообщества, на ваш вопрос ответят быстро. Этот репозиторий предназначен исключительно для открытия проблем и запроса ресурсов, но не стесняйтесь быть частью сообщества 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) |
## Как внести свой вклад
_Хотите внести свой вклад в проект? Вы будем рады отметить вас как одного из наших соавторов. Вот несколько направлений, где вы можете сделать Get (и Flutter) лучше._
- Помощь в переводе readme на другие языки.
- Добавление документации в readme (многие функции Get еще не задокументированы).
- Напишите статью или сделайте видео, обучающие использованию Get (они будут вставлены в Readme и в будущем в нашу Wiki).
- Предложите PRs для кода/тестов.
- Новые фичи.
Приветствуется любой вклад!
## Статьи и видео
- [Dynamic Themes in 3 lines using GetX™](https://medium.com/swlh/flutter-dynamic-themes-in-3-lines-c3b375f292e3) - Tutorial by [Rod Brown](https://github.com/RodBr).
- [Complete GetX™ Navigation](https://www.youtube.com/watch?v=RaqPIoJSTtI) - Route management video by Amateur Coder.
- [Complete GetX State Management](https://www.youtube.com/watch?v=CNpXbeI_slw) - State management 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) - State management by [Aachman Garg](https://github.com/imaachman).
- [GetX, the all-in-one Flutter package](https://www.youtube.com/watch?v=IYQgtu9TM74) - A brief tutorial covering State Management 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.
... ...
# Управление зависимостями
- [Управление зависимостями](#управление-зависимостями)
- [Методы создания экземпляров](#методы-создания-экземпляров)
- [Get.put()](#getput)
- [Get.lazyPut](#getlazyput)
- [Get.putAsync](#getputasync)
- [Get.create](#getcreate)
- [Применение методов/классов создания экземпляров](#применение-методовклассов-создания-экземпляров)
- [Различия между методами](#различия-между-методами)
- [Подвязки](#подвязки)
- [Класс Bindings](#класс-bindings)
- [BindingsBuilder](#bindingsbuilder)
- [SmartManagement](#smartmanagement)
- [Как поменять](#как-поменять)
- [SmartManagement.full](#smartmanagementfull)
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
- [Как подвязки работают под капотом](#как-подвязки-работают-под-капотом)
- [Примечания](#примечания)
Get имеет простой и мощный менеджер зависимостей, позволяющий вам получить тот же класс, что и ваш Bloc или Controller, с помощью 1 строки кода, без Provider и inheritedWidget:
```dart
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
```
Вместо того, чтобы создавать экземпляр вашего класса в классе, который вы используете, вы создаёте его в экземпляре Get, что сделает его доступным во всем приложении.
Таким образом Вы можете использовать свой контроллер (или Bloc)
- Примечание: Если вы применяете менеджер состояний Get, обратите внимание на [Bindings](#bindings) api, упрощающего подключение вашего предсталения к контроллеру.
- Примечаие²: Управление зависимостями Get отделено от других частей пакета, поэтому, если, например, ваше приложение уже использует другой менеджер состояний, вам не нужно его менять, вы можете использовать этот менеджер внедрения зависимостей без каких-либо проблем.
## Методы создания экземпляров
Методы и настраиваемые параметры:
### Get.put()
Самый распространенный способ внедрения зависимости. Например, хорош для контроллеров ваших представлений.
```dart
Get.put<SomeClass>(SomeClass());
Get.put<LoginController>(LoginController(), permanent: true);
Get.put<ListItemController>(ListItemController, tag: "some unique string");
```
Это все параметры, которые вы можете установить при использовании put:
```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
Можно отложить загрузку зависимости, чтобы она создавалась только тогда, когда она используется. Очень полезно для вычислительных ресурсоёмких классов или если вы хотите создать экземпляры нескольких классов в одном месте (например, в классе Bindings), и вы знаете, что не собираетесь использовать этот класс в то время.
```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() )
```
Это все параметры, которые вы можете установить при использовании lazyPut:
```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
Если вы хотите зарегистрировать асинхронный экземпляр, вы можете использовать `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() )
```
Это все параметры, которые вы можете установить при использовании 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
Это хитрый метод. Подробное объяснение того, что это такое, и различий между ними можно найти в разделе [Различия между методами](#различия-между-методами):
```dart
Get.Create<SomeClass>(() => SomeClass());
Get.Create<LoginController>(() => LoginController());
```
Это все параметры, которые вы можете установить при использовании create:
```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
```
## Применение методов/классов создания экземпляров
Представьте, что вы прошли через множество маршрутов и вам нужны данные, которые остались в вашем контроллере. Вам понадобится менеджер состояний в сочетании с Provider или Get_it, верно? Только не с Get. Вам просто нужно попросить Get «найти» ваш контроллер, никаких дополнительных зависимостей вам не потребуется:
```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.
```
И тогда вы сможете восстановить данные вашего контроллера, которые были там получены:
```dart
Text(controller.textFromApi);
```
Поскольку возвращаемое значение является обычным классом, вы можете делать все, что захотите:
```dart
int count = Get.find<SharedPreferences>().getInt('counter');
print(count); // out: 12345
```
Чтобы удалить экземпляр Get:
```dart
Get.delete<Controller>(); //usually you don't need to do this because GetX already delete unused controllers
```
## Различия между методами
Сперва давайте рассмотрим параметр `fenix` метода Get.lazyPut и `permanent` других методов.
Фундаментальное различие между `permanent` и `fenix` заключается в том, как вы хотите хранить свои экземпляры.
Закрепляя это: по умолчанию GetX удаляет экземпляры, когда они не используются.
Это означает, что: если на экране 1 есть контроллер 1, а на экране 2 есть контроллер 2, и вы удаляете первый маршрут из стека (например, если вы используете `Get.off()` или `Get.offNamed()`), контроллер 1 теперь не используется, поэтому он будет стёрт.
Но если вы используете `permanent: true`, тогда контроллер не будет потерян при этом переходе - что очень полезно для служб, которые вы хотите поддерживать на протяжении всего приложения.
`fenix` предназначен для сервисов, о потере которых вы не беспокоитесь между сменами экрана, но когда вам нужен этот сервис, вы ожидаете, что он будет активен. По сути, он избавится от неиспользуемого контроллера/службы/класса, но когда он вам понадобится, он «воссоздает из пепла» новый экземпляр.
Исходя из различий между методами:
- Get.put и Get.putAsync следует одному и тому же порядку создания, с той разницей, что второй использует асинхронный метод: эти два метода создают и инициализируют экземпляр. Они вставляются непосредственно в память с использованием внутреннего метода `insert` с параметрами `permanent: false` и `isSingleton: true` (параметр isSingleton предназначен только для того, чтобы указать, следует ли использовать зависимость от `dependency` или она должна использовать зависимость от `FcBuilderFunc`). После этого вызывается `Get.find()`, который немедленно инициализирует экземпляры, находящиеся в памяти.
- Get.create: Как следует из названия, он «создаст» вашу зависимость! Подобно `Get.put()`, он также вызывает внутренний метод `insert` для создания экземпляра. Но `permanent` становится true и `isSingleton` становится false (поскольку мы «создаем» нашу зависимость, она не может быть синглтоном, поэтому false). И поскольку `permanent: true`, мы по умолчанию не теряем его между экранами! Также, `Get.find()` не вызывается немедленно, он ожидает использования на экране для вызова. Он создан таким образом, чтобы использовать параметр `permanent`, `Get.create()` был создан с целью создания не общих экземпляров, но не удаляемых, как, например, кнопка в listView, где вам нужен уникальный экземпляр для этого списка - по этой причине Get.create необходимо использовать вместе с GetWidget.
- Get.lazyPut: Как следует из названия, это ленивый процесс. Экземпляр создается, но он не вызывается для немедленного использования, он остается в ожидании вызова. В отличие от других методов, `insert` не вызывается здесь. Вместо этого экземпляр вставляется в другую часть памяти, часть, отвечающую за определение возможности воссоздания экземпляра, назовем это «фабрикой». Если мы хотим создать что-то, что будет использоваться позже, это не будет смешиваться с вещами, которые использовались сейчас. И здесь вступает в силу магия `fenix`: если вы решаете оставить `fenix: false`, и ваш `smartManagement` не является `keepFactory`, то, при использовании `Get.find`, экземпляр изменит место в памяти с «фабрики» на область памяти общего экземпляра. Сразу после этого по умолчанию удаляется с «фабрики». Теперь, если вы выберете `fenix: true`, экземпляр продолжит существовать в этой выделенной части, даже перейдя в общую область, для повторного вызова в будущем.
## Подвязки
Возможно, одной из главных особенностей этого пакета является возможность полной интеграции маршрутов, менеджера состояний и менеджера зависимостей.
Когда маршрут удаляется из стека, все контроллеры, переменные и экземпляры связанных с ним объектов удаляются из памяти. Если вы используете потоки или таймеры, они закроются автоматически, и вам не о чем беспокоиться.
В версии 2.10 полностью реализован API привязок.
Теперь вам больше не нужно использовать метод инициализации. Вам даже не нужно вводить контроллеры, если вы этого не хотите. Вы можете запустить свои контроллеры и серисы в соответствующем для этого месте.
Класс Binding - это класс, который будет разделять внедрение зависимостей, при этом «привязывая» маршруты к диспетчеру состояний и диспетчеру зависимостей.
Этот класс позволяет Get узнать, какой экран отображается при использовании конкретного контроллера, а также узнать, где и как его удалить.
Кроме того, класс Binding позволит вам контролировать конфигурацию SmartManager. Вы можете настроить зависимости, которые будут упорядочены при удалении маршрута из стека, или когда виджет, который его использовал, выкладывается, или ни то, ни другое. На вас будет работать интеллектуальное управление зависимостями, но даже в этом случае вы можете настроить его по своему усмотрению.
### Класс Bindings
- Создайте класс и реализуйте Binding
```dart
class HomeBinding implements Bindings {}
```
Ваша IDE автоматически попросит вас переопределить метод «зависимостей», и вам просто нужно последовать этой просьбе, переопределить метод и вставить все классы, которые вы собираетесь использовать на этом маршруте:
```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());
}
}
```
Теперь вам просто нужно сообщить своему маршруту, что вы будете использовать эту привязку для установления связи между диспетчером маршрутов, зависимостями и состояниями.
- Используя именованные маршруты:
```dart
getPages: [
GetPage(
name: '/',
page: () => HomeView(),
binding: HomeBinding(),
),
GetPage(
name: '/details',
page: () => DetailsView(),
binding: DetailsBinding(),
),
];
```
- Используя обычные маршруты:
```dart
Get.to(Home(), binding: HomeBinding());
Get.to(DetailsView(), binding: DetailsBinding())
```
Вам больше не нужно беспокоиться об управлении памятью вашего приложения, Get сделает это за вас.
Класс Binding вызывается при вызове маршрута, вы можете создать "initialBinding" в GetMaterialApp, чтобы вставить все зависимости, которые будут созданы.
```dart
GetMaterialApp(
initialBinding: SampleBind(),
home: Home(),
);
```
### BindingsBuilder
По умолчанию привязка создается путем создания класса, реализующего привязки.
Но в качестве альтернативы вы можете использовать обратный вызов `BindingsBuilder`, чтобы просто использовать функцию для создания всего, что вы хотите.
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());
}),
),
];
```
Таким образом, вы можете избежать создания одного класса привязки для каждого маршрута, что сделает это еще проще.
Оба способа работают идеально, и мы хотим, чтобы вы использовали то, что больше всего соответствует вашим вкусам.
### SmartManagement
GetX по умолчанию удаляет неиспользуемые контроллеры из памяти, даже если происходит сбой и виджет, который их использует, не удаляется должным образом.
Это то, что называется `полным` режимом управления зависимостями.
Но если вы хотите поменять способ, которым GetX управляет удалением классов, у вас есть класс `SmartManagement`, в котором вы можете задавать другое поведение.
#### Как поменять
Если вы хотите поменять эту конфигурацию (что обычно не требуется), вот способ:
```dart
void main () {
runApp(
GetMaterialApp(
smartManagement: SmartManagement.onlyBuilders //here
home: Home(),
)
)
}
```
#### SmartManagement.full
Это значение по умолчанию. Удаляет классы, которые не используются и не были постоянными. В большинстве случаев вы захотите оставить эту конфигурацию нетронутой. Если вы новичок в GetX, не меняйте это.
#### SmartManagement.onlyBuilders
С этой опцией будут удалены только контроллеры, запущенные в `init:` или загруженные в Binding с помощью `Get.lazyPut()`.
Если вы используете `Get.put()` или `Get.putAsync()` или любой другой подход, SmartManagement не будет иметь разрешений на исключение этой зависимости.
При поведении по умолчанию даже виджеты, созданные с помощью Get.put, будут удалены, в отличие от SmartManagement.onlyBuilders.
#### SmartManagement.keepFactory
Как и SmartManagement.full, он удаляет зависимости, когда он больше не используется. Однако он сохранит свою фабрику, что означает, что он воссоздает зависимость, если вам снова понадобится этот экземпляр.
### Как подвязки работают под капотом
Привязки создают временные фабрики, которые создаются в тот момент, когда вы кликаете для перехода на другой экран, и будут уничтожены, как только произойдет анимация смены экрана.
Это происходит так быстро, что анализатор даже не сможет это зарегистрировать.
Когда вы снова перейдете на этот экран, будет вызвана новая временная фабрика, поэтому это предпочтительнее, чем использование SmartManagement.keepFactory, но если вы не хотите создавать привязки или хотите сохранить все свои зависимости в одной привязке, это вам поможет.
Фабрики занимают мало памяти, они содержат не экземпляры, а функцию с «формой» того класса, который вам нужен.
Это имеет очень низкую стоимость памяти, но поскольку цель этой библиотеки - получить максимально возможную производительность с использованием минимальных ресурсов, Get по умолчанию удаляет даже фабрики. Используйте то, что вам удобнее.
## Примечания
- НЕ ИСПОЛЬЗУЙТЕ SmartManagement.keepFactory, если вы используете несколько привязок. Он был разработан для использования без привязок или с одной привязкой, связанной в initialBinding GetMaterialApp.
- Использование привязок совершенно необязательно, если вы хотите, вы можете без проблем использовать `Get.put()` и `Get.find()` для классов, которые используют данный контроллер.
Однако, если вы работаете со службами или любой другой абстракцией, я рекомендую использовать привязки для лучшей организации.
... ...
- [Управление маршрутами](#управление-маршрутами)
- [Как использовать](#как-использовать)
- [Навигация без именованных маршрутов](#навигация-без-именованных-маршрутов)
- [Навигация по именованным маршрутам](#навигация-по-именованным-маршрутам)
- [Отправить данные по именованному маршруту](#отправить-данные-по-именованному-маршруту)
- [Динамические url-ссылки](#динамические-url-ссылки)
- [Middleware](#middleware)
- [Навигация без контекста](#навигация-без-контекста)
- [SnackBars](#snackbars)
- [Dialogs](#dialogs)
- [BottomSheets](#bottomsheets)
- [Вложенная навигация](#вложенная-навигация)
# Управление маршрутами
Это полное объяснение всего, что нужно Getx, когда речь идет об управлении маршрутами.
## Как использовать
Добавьте к вашему pubspec.yaml следующее:
```yaml
dependencies:
get:
```
Если вы собираетесь использовать маршруты/snackbars/dialogs/bottomSheets без контекста или использовать высокоуровневые API Get, вам нужно просто добавить «Get» перед вашим MaterialApp, превратив его в GetMaterialApp!
```dart
GetMaterialApp( // Before: MaterialApp(
home: MyHome(),
)
```
## Навигация без именованных маршрутов
Для навигации на новый экран:
```dart
Get.to(NextScreen());
```
Чтобы закрыть snackbars, диалог, bottomsheets, или все, что вы обычно закрывали с помощью Navigator.pop(context);
```dart
Get.back();
```
Для перехода к следующему экрану и отсутствия возможности вернуться к предыдущему экрану (для использования в SplashScreens, экранах входа и т.д.)
```dart
Get.off(NextScreen());
```
Для перехода к следующему экрану и отмены всех предыдущих маршрутов (полезно в корзинах для покупок, опросах и тестах)
```dart
Get.offAll(NextScreen());
```
Чтобы перейти к следующему маршруту и ​​получить или обновить данные, как только вы вернетесь с него:
```dart
var data = await Get.to(Payment());
```
отправить данные на предыдущий экран:
```dart
Get.back(result: 'success');
```
И использовать их:
пример:
```dart
if(data == 'success') madeAnything();
```
Нет желания учить наш синтаксис?
Просто измените Navigator(верхний регистр) на navigator (нижний регистр), и вы получите все функции стандартной навигации без использования контекста.
Пример:
```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());
```
## Навигация по именованным маршрутам
- Если вы предпочитаете перемещаться по именованным маршрутам, Get также поддерживает это.
Для навигации на следующий экран
```dart
Get.toNamed("/NextScreen");
```
Для навигации и удаления предыдущего экрана из дерева.
```dart
Get.offNamed("/NextScreen");
```
Для навигации и удаления всех предыдущих экранов из дерева.
```dart
Get.offAllNamed("/NextScreen");
```
Для определения маршрутов используйте 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
),
],
)
);
}
```
Для обработки навигации по неопределенным маршрутам (ошибка 404) вы можете определить страницу unknownRoute в GetMaterialApp.
```dart
void main() {
runApp(
GetMaterialApp(
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => MyHomePage()),
GetPage(name: '/second', page: () => Second()),
],
)
);
}
```
### Отправить данные по именованному маршруту
Просто отправьте то, что хотите в качестве аргументов. Get принимает здесь всё, что угодно, будь то String, Map, List или даже экземпляр класса.
```dart
Get.toNamed("/NextScreen", arguments: 'Get is the best');
```
в вашем классе или контроллере:
```dart
print(Get.arguments);
//print out: Get is the best
```
### Динамические url-ссылки
Получите расширенные динамические url-адреса, как в Интернете. Веб-разработчики, вероятно, уже хотели эту функцию на Flutter, и, скорее всего, видели, что пакет обещает эту функцию и предоставляет совершенно другой синтаксис, чем url-адрес в Интернете, но Get также решает эту проблему.
```dart
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
```
в вашем controller/bloc/stateful/stateless классе:
```dart
print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
```
Вы также можете легко получить именованные параметры с помощью Get:
```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
),
],
)
);
}
```
Отправьте данные по именованному маршруту
```dart
Get.toNamed("/profile/34954");
```
На втором экране возьмите данные по параметрам
```dart
print(Get.parameters['user']);
// out: 34954
```
И теперь все, что вам нужно сделать, это использовать Get.toNamed() для навигации по именованным маршрутам без какого-либо контекста (вы можете вызывать свои маршруты непосредственно из класса BLoC или контроллера), а когда ваше приложение будет скомпилировано в Интернете, ваше маршруты появятся в url <3
### Middleware
Если вы хотите прослушивать события Get для запуска действий, вы можете использовать для этого routingCallback
```dart
GetMaterialApp(
routingCallback: (routing) {
if(routing.current == '/second'){
openAds();
}
}
)
```
Если вы не используете GetMaterialApp, вы можете использовать ручной API для подключения наблюдателя.
```dart
void main() {
runApp(
MaterialApp(
onGenerateRoute: Router.generateRoute,
initialRoute: "/",
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer), // HERE !!!
],
),
);
}
```
Создайте класс MiddleWare
```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');
}
}
}
```
Теперь используйте Get в своем коде:
```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: RaisedButton(
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: RaisedButton(
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: RaisedButton(
onPressed: () {
Get.back();
},
child: Text('Go back!'),
),
),
);
}
}
```
## Навигация без контекста
### SnackBars
Чтобы получить простой SnackBar с Flutter, вы должны получить контекст Scaffold, или вы должны использовать GlobalKey, прикрепленный к вашему Scaffold
```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);
```
Реализация в Get:
```dart
Get.snackbar('Hi', 'i am a modern snackbar');
```
С Get всё, что вам нужно сделать, это вызвать Get.snackbar из любого места кода или настроить его так, как вы хотите!
```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,
// FlatButton 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
///////////////////////////////////
```
Если вы предпочитаете традиционный snackbar, или хотите настроить его с нуля, вы можете использовать
`Get.rawSnackbar();` который предоставляет RAW API, на котором был построен Get.snackbar.
### Dialogs
Чтобы открыть:
```dart
Get.dialog(YourDialogWidget());
```
Чтобы открыть диалог по умолчанию:
```dart
Get.defaultDialog(
onConfirm: () => print("Ok"),
middleText: "Dialog made in 3 lines of code"
);
```
Вы также можете использовать Get.generalDialog вместо showGeneralDialog.
Для всех других виджетов диалога Flutter, включая cupertino, вы можете использовать Get.overlayContext вместо контекста и открывать его в любом месте вашего кода.
Для виджетов, которые не используют Overlay, вы можете использовать Get.context.
Эти два контекста будут работать в 99% случаев для замены контекста вашего пользовательского интерфейса, за исключением случаев, когда наследуемый виджет используется без контекста навигации.
### BottomSheets
Get.bottomSheet похож на showModalBottomSheet, но не требует контекста.
```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: () => {},
),
],
),
)
);
```
## Вложенная навигация
Get сделал вложенную навигацию Flutter еще проще.
Вам не нужен контекст, и вы найдёте свой стек навигации по Id.
- ПРИМЕЧАНИЕ: Создание параллельных стеков навигации может быть опасным. В идеале не используйте NestedNavigators или используйте их редко. Если этого требует ваш проект, продолжайте, но имейте в виду, что хранение нескольких стеков навигации в памяти может быть не лучшим решением для потребления оперативной памяти.
Смотрите как это просто:
```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: FlatButton(
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")
),
),
),
);
}
}
),
```
... ...
- [Управление состоянием](#управление-состоянием)
- [Реактивное управление состоянием](#реактивное-управление-состоянием)
- [Преимущества](#преимущества)
- [Объявление реактивной переменной](#объявление-реактивной-переменной)
- [Использование значений в представлении](#использование-значений-в-представлении)
- [Условия для перестраивания](#условия-для-перестраивания)
- [Где .obs может быть использован](#где-obs-может-быть-использован)
- [Примечание о списках](#примечание-о-списках)
- [Почему мне нужно использовать .value](#почему-мне-нужно-использовать-value)
- [Obx()](#obx)
- [Workers](#workers)
- [Обычное управление состоянием](#обычное-управление-состоянием)
- [Преимущества](#преимущества-1)
- [Использование](#использование)
- [Как обрабатываются контроллеры](#как-обрабатываются-контроллеры)
- [Вам больше не понадобятся StatefulWidgets](#вам-больше-не-понадобятся-statefulwidgets)
- [Почему это существует](#почему-это-существует)
- [Другие способы использования](#другие-способы-использования)
- [Уникальные идентификаторы](#уникальные-идентификаторы)
- [Смешивание двух менеджеров состояний](#смешивание-двух-менеджеров-состояний)
- [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
# Управление состоянием
В настоящее время для Flutter есть несколько менеджеров состояний. Однако большинство из них связано с использованием ChangeNotifier для обновления виджетов, и это плохой и очень плохой подход к производительности средних или больших приложений. Вы можете проверить в официальной документации Flutter, что [ChangeNotifier следует использовать с 1 или максимум 2 слушателями](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html), что делает его практически непригодным для любого приложения среднего или большого размера.
Остальные менеджеры состояний хороши, но есть свои нюансы:
- BLoC безопасен и эффективен, но сложен для новичков, что удерживает людей от разработки с Flutter.
- MobX проще, чем BLoC, реактивен, и я бы сказал, почти идеален, но вам нужно использовать генератор кода, который для больших приложений снижает производительность, таким образом вам нужно будет пить много кофе, прежде чем ваш код снова не будет готов после flutter clean (И это не вина MobX, а вина кодогенерации, которая очень медленная!).
- Provider использует InheritedWidget для доставки слушателя в качестве способа решения проблемы, описанной выше, с помощью ChangeNotifier, что подразумевает, что любой доступ к классу ChangeNotifier должен находиться в дереве виджетов из-за контекста для доступа к Inherited.
Get не лучше и не хуже, чем любой другой менеджер состояний, но вам следует проанализировать эти моменты, а также приведенные ниже пункты, чтобы выбрать между использованием Get в чистом виде (Vanilla) или его вместе с другим менеджером состояний. Определенно, Get - не враг любого другого менеджера состояний, потому что Get - это микрофреймворк, а не просто менеджер состояний, и его можно использовать отдельно или вместе с ними.
## Реактивное управление состоянием
Реактивное программирование может оттолкнуть многих людей, потому что считается сложным. GetX превращает реактивное программирование в нечто довольно простое:
- Вам не нужно создавать StreamControllers.
- Вам не нужно создавать StreamBuilder для каждой переменной.
- Вам не нужно создавать класс для каждого состояния.
- Вам не нужно создавать получение начального значения.
Реактивное программирование с помощью Get так же просто, как использование setState.
Представим, что у вас есть переменная `name` и вы хотите, чтобы каждый раз, когда вы её изменяете, все виджеты, которые её используют, менялись автоматически.
Это ваша переменная:
```dart
var name = 'Jonatas Borges';
```
Чтобы сделать его наблюдаемым, вам просто нужно добавить в конец «.obs»:
```dart
var name = 'Jonatas Borges'.obs;
```
Вот и всё. Это *так* просто.
С этого момента мы могли бы называть эти - ".obs" (ervables) переменные как _Rx_.
Что мы делали под капотом? Мы создали `Stream` из `String`ов, которому было присвоено начальное значение `"Jonatas Borges"`, мы уведомили все виджеты, которые используют `"Jonatas Borges"`, что они теперь «принадлежат» этой переменной, и когда значение _Rx_ изменится, они также должны будут измениться.
Это **волшебство GetX** возможно, благодаря возможностям Dart.
Но, как мы знаем, виджет можно изменить только в том случае, если он находится внутри функции, потому что статические классы не имеют права «автоматически изменяться».
Вам нужно будет создать `StreamBuilder`, подписаться на эту переменную, чтобы отслеживать изменения, и создать «каскад» вложенных `StreamBuilder`, если вы хотите изменить несколько переменных в одной области, верно?
Нет, вам не нужен `StreamBuilder`, но насчёт статических классов вы правы.
Что ж, в представлении во Flutter, когда мы хотим изменить конкретный виджет, приходится писать много шаблоного кода.
C **GetX** вы можете забыть о шаблонном коде.
`StreamBuilder( … )`? `initialValue: …`? `builder: …`? Нет, вам просто нужно поместить эту переменную в виджет `Obx()`.
```dart
Obx (() => Text (controller.name));
```
_Что нужно запомнить?_ Только `Obx(() =>`.
Вы просто передаёте этот виджет через стрелочную функцию в `Obx()` ("Observer" в _Rx_).
`Obx` довольно умён и изменится только при изменении значения `controller.name`.
Если `name` == `"John"`, и вы измените его на `"John"` (`name.value = "John"`), на экране ничего не изменится, так как это то же значение, что и раньше. `Obx` для экономии ресурсов просто проигнорирует новое значение, а не будет перестраивать виджет. **Разве это не потрясающе?**
> Итак, что, если у меня есть 5 переменных _Rx_ (observable) в `Obx`?
Он просто обновится, когда **любой** из них изменится.
> И если у меня есть 30 переменных в классе, когда я обновлю одну, обновятся ли **все** переменные этого класса?
Нет, только **конкретный виджет**, который использует эту переменную _Rx_.
Итак, **GetX** обновляет экран только тогда, когда переменная _Rx_ меняет свое значение.
```
final isOpen = false.obs;
// NOTHING will happen... same value.
void onButtonTap() => isOpen.value=false;
```
### Преимущества
**GetX()** поможет вам, когда вам нужен **детальный** контроль над тем, что обновляется.
Если вам не нужны уникальные идентификаторы, из-за того что все ваши переменные будут изменены при выполнении, используйте GetBuilder, потому что это простой модуль обновления состояния (как `setState()`), написанный всего в несколько строк кода.
Он был сделан простым, чтобы иметь наименьшее влияние на CPU и просто выполнять единственную цель (восстановление состояния), тратя минимально возможные ресурсы.
Если вам нужен **мощный** менеджер состояний, то вашим выбором будет **GetX**.
Он не работает с переменными, а работает с потоками, все в нем - это `Streams` под капотом.
Вы можете использовать _rxDart_ вместе с ним, потому что все это `Streams`,
вы можете прослушивать событие каждой «переменной _Rx_», потому что всё в нём - это `Streams`.
Это буквально подход _BLoC_, который проще, чем _MobX_, и без генераторов кода и тд.
Вы можете превратить что угодно в _"Observable"_ с помощью `.obs`.
### Максимальная производительность:
В дополнение к интеллектуальному алгоритму минимальных перестроек, **GetX** использует компараторы, чтобы убедиться, что состояние изменилось.
Если вы столкнетесь с ошибками в своем приложении и отправите дублирующее изменение состояния,
**GetX** гарантирует, что оно не выйдет из строя.
С **GetX** состояние изменяется только при изменении значения.
В этом основное отличие между **GetX** и применением _`computed` из MobX_.
При объединении двух __observables__, когда один из них изменяется; слушатель этого _observable_ также изменится.
В **GetX**, iесли вы объедините две переменные, `GetX()` (аналогично `Observer()`) будет перестраиваться только в том случае, если это подразумевает реальное изменение состояния.
### Объявление реактивной переменной
У вас есть 3 способа превратить переменную в "observable".
1 - Первый использует **`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 - Второй - использовать **`Rx`** и дженерики `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 - Третий, более практичный, простой и предпочтительный подход, просто добавьте **`.obs`** в качестве свойства вашего значения:
```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;
```
##### Реактивные состояния - это просто.
Как мы знаем, _Dart_ сейчас движется в сторону _null safety_.
Чтобы быть готовым, с этого момента вы всегда должны начинать свои переменные _Rx_ с **начальным значением**.
> Преобразование переменной в _observable_ + _начальное значение_ c **GetX** - самый простой и практичный подход.
Вы буквально добавите "`.obs`" в конец своей переменной и **всё**, вы сделали её observable,
и её `.value`, будет начальным значением.
### Использование значений в представлении
```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}');
},
),
```
Если мы увеличим `count1.value++`, он выведет:
- `count 1 rebuild`
- `count 3 rebuild`
поскольку `count1` имеет значение `1`, а `1 + 0 = 1`, изменяет геттер `sum`.
Если мы изменим `count2.value++`, он выведет:
- `count 2 rebuild`
- `count 3 rebuild`
так как `count2.value` изменился, и теперь `sum` равен `2`.
- Примечание: По умолчанию самое первое событие перестраивает виджет, даже если это то же значение.
Такое поведение существует из-за Boolean переменных.
Представьте, что вы сделали это:
```dart
var isLogged = false.obs;
```
А затем вы проверили, вошел ли пользователь в систему, чтобы вызвать событие в `ever`.
```dart
@override
onInit(){
ever(isLogged, fireRoute);
isLogged.value = await Preferences.hasToken();
}
fireRoute(logged) {
if (logged) {
Get.off(Home());
} else {
Get.off(Login());
}
}
```
Если `hasToken` был `false`, `isLogged` не изменится, поэтому `ever()` никогде не будет вызван.
Чтобы избежать такого поведения, первое изменение _observable_ всегда будет запускать событие, даже если оно содержит то же самое `.value`.
Вы можете убран данное поведение, если хотите, используя:
`isLogged.firstRebuild = false;`
### Условия для перестраивания
Кроме того, Get обеспечивает усовершенствованный контроль состояния. Вы можете обусловить событие (например, добавление объекта в список) определенным условием.
```dart
// First parameter: condition, must return true of false
// Second parameter: the new value to aplly if the condition is true
list.addIf(item < limit, item);
```
Без украшений, без генератора кода, без сложностей :smile:
Вы ведь знаете счётчик Flutter? Ваш класс контроллера может выглядеть так:
```dart
class CountController extends GetxController {
final count = 0.obs;
}
```
С простым:
```dart
controller.count.value++
```
Вы можете обновить переменную счетчика в своем пользовательском интерфейсе, независимо от того, где она хранится.
### Где .obs может быть использован
Вы можете преобразовать что угодно в obs. Вот два способа сделать это:
* Вы можете преобразовать значения вашего класса в obs
```dart
class RxUser {
final name = "Camila".obs;
final age = 18.obs;
}
```
* или вы можете преобразовать весь класс в observable
```dart
class User {
User({String name, int age});
var name;
var age;
}
// when instantianting:
final user = User(name: "Camila", age: 18).obs;
```
### Примечание о списках
Списки полностью наблюдаемы, как и объекты внутри них. Таким образом, если вы добавите значение в список, он автоматически перестроит виджеты, которые его используют.
Вам также не нужно использовать ".value" со списками, замечательный API-интерфейс Dart позволяет нам избежать этого.
К сожалению, примитивные типы, такие как String и int, не могут быть расширены, что делает использование .value обязательным, но это не будет проблемой, если вы работаете с геттерами и сеттерами для них.
```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
)
```
Когда вы делаете свои собственные классы наблюдаемыми, есть другой способ их обновить:
```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)
```
Вам не нужно работать с наборами, если вы этого не хотите, вы можете использовать API "assign" и "assignAll".
API "assign" очистит ваш список и добавит один объект, который вы хотите начать там.
API "assignAll" очистит существующий список и добавит любые повторяемые объекты, которые вы в него вставляете.
### Почему мне нужно использовать .value
Мы могли бы убрать обязательство использовать 'value' для `String` и `int` с помощью простого оформления и генератора кода, но цель этой библиотеки как раз и состоит в том, чтобы избежать внешних зависимостей. Мы хотим предложить среду, готовую для программирования, включающую в себя самое необходимое (управление маршрутами, зависимостями и состояниями) простым, легким и производительным способом без необходимости во внешнем пакете.
Вы можете буквально добавить 3 буквы в свой pubspec (get), двоеточие и начать программировать. Все решения, включенные по умолчанию, от управления маршрутами до управления состоянием, нацелены на простоту, продуктивность и производительность.
Общий вес этой библиотеки меньше, чем у одного менеджера состояний, хотя это полное решение, и это то, что вы должны понимать.
Если вас беспокоит `.value`, и вам нравится генератор кода, MobX - отличная альтернатива, и вы можете использовать его вместе с Get. Для тех, кто хочет добавить одну зависимость в pubspec и начать программировать, не беспокоясь ни о совместимости версий пакетов, ни об ошибках обновления состояния исходящих от менеджеров состояний или зависимостей и не хочет беспокоиться о доступности контроллеров, а «просто программирование», Get идеален.
Если у вас нет проблем с MobX или BLoC, вы можете просто использовать Get для маршрутов и забыть о том, что у него есть менеджер состояний. Простой и реактивный менеджеры состояний Get появились из-за то, что в моей компании был проект с более чем 90 контроллерами, а генератору кода после flutter clean требовалось более 30 минут для выполнения своих задач на достаточно хорошей машине. Если у вас 5, 10, 15 контроллеров, то вам подойдёт любой менеджер состояний. Если у вас абсурдно большой проект и генератор кода является для вас проблемой, то решение прямо перед вами.
Очевидно, что если кто-то хочет внести свой вклад в проект и создать генератор кода или что-то подобное, я укажу об этом в readme в качестве альтернативы. Моя потребность не в востребованности для всех разработчиков, я лишь говорю, что есть хорошие решения, которые уже делают это, например, MobX.
### Obx()
Bindings в Get необязательны. Вы можете использовать виджет Obx вместо GetX, который получает только анонимную функцию, создающую виджет.
Очевидно, что если вы не используете тип, вам потребуется экземпляр вашего контроллера для использования переменных или использовать `Get.find<Controller>()`.value или Controller.to.value для получения значения.
### Workers
Workers помогут вам, инициируя определенные обратные вызовы при возникновении события.
```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));
```
У всех workers (кроме `debounce`) есть именованный параметр `condition`, которое может быть `bool` или обратным вызовом возвращающим `bool`.
Этот `condition` определяет, когда выполняется функция обратного вызова.
Все workers возвращают экземпляр `Worker`, который можно использовать для отмены (с помощью метода `dispose()`) worker.
- **`ever`**
вызывается каждый раз, когда переменная _Rx_ выдает новое значение.
- **`everAll`**
как `ever`, но он принимает `List` значений _Rx_ вызываемый каждый раз, когда его переменная изменяется. Вот и всё.
- **`once`**
'once' вызывается только при первом изменении переменной.
- **`debounce`**
'debounce' очень полезен в функциях поиска, где вы хотите, чтобы API вызывался только тогда, когда пользователь заканчивает ввод. Если пользователь вводит "Jonny", у вас будет 5 поисковых запросов в API по буквам J, o, n, n и y. С Get этого не происходит, потому что у вас будет "debounce" Worker, который будет запускаться только в конце набора.
- **`interval`**
'interval' отличается от `debouce`. В `debouce`, если пользователь внесёт 1000 изменений в переменную в течение 1 секунды, он отправит только последнее после установленного таймера (по умолчанию 800 миллисекунд). Вместо этого Interval будет игнорировать все действия пользователя в течение указанного периода. Если вы отправляете события в течение 1 минуты, 1000 в секунду, debounce отправит вам только последнее, когда пользователь прекратит стрелять событиями. Interval будет доставлять события каждую секунду, а если установлен на 3 секунды, то он будет доставлять 20 событий в эту минуту. Это рекомендуется во избежание злоупотреблений в функциях, где пользователь может быстро щелкнуть что-либо и получить некоторое преимущество (представьте, что пользователь может зарабатывать монеты, щелкая что-либо, если он щелкнет 300 раз за ту же минуту, у него будет 300 монет, используя интервал, вы можете установить временной интервал на 3 секунды, и даже если щелкнуть 300 или тысячу раз, максимум, который он получит за 1 минуту, составит 20 монет, щелкнув 300 или 1 миллион раз). Debounce подходит для защиты от DDos, для таких функций, как поиск, где каждое изменение onChange будет вызывать запрос к вашему API. Debounce будет ждать, пока пользователь перестанет вводить имя, чтобы сделать запрос. Если бы он использовался в вышеупомянутом сценарии с монетами, пользователь накликал бы только 1 монету, потому что он выполняется только тогда, когда пользователь "делает паузу" на установленное время.
- ПРИМЕЧАНИЕ: должны всегда использоваться при запуске контроллера или класса, поэтому он всегда должен быть в onInit (рекомендуется), в конструкторе класса или в initState StatefulWidget (в большинстве случаев эта практика не рекомендуется, но не должна иметь побочных эффектов).
## Обычное управление состоянием
Get имеет чрезвычайно легкий и простой менеджер состояний, который не использует ChangeNotifier, удовлетворит потребности, особенно для тех, кто плохо знаком с Flutter, и не вызовет проблем для больших приложений.
GetBuilder нацелен именно на контроль нескольких состояний. Представьте, что вы добавили 30 продуктов в корзину, вы нажимаете удалить один, одновременно с этим обновляется список, обновляется цена, а значок в корзине покупок обновляется до меньшего числа. Такой подход делает GetBuilder убийственным, потому что он группирует состояния и изменяет их все сразу без какой-либо "вычислительной логики" для этого. GetBuilder был создан с учётом такого рода ситуаций, поскольку для временного изменения состояния вы можете использовать setState, и для этого вам не понадобится менеджер состояний.
Таким образом, если вам нужен отдельный контроллер, вы можете назначить для него идентификаторы или использовать GetX. Это зависит от вас, помните, что чем больше у вас «индивидуальных» виджетов, тем больше ресурсов будет забирать GetX, в то время как производительность GetBuilder должна быть выше при многократном изменении состояния.
### Преимущества
1. Обновляйте только необходимые виджеты.
2. Не используйте changeNotifier, это менеджер состояний, который использует меньше памяти (около 0 МБ).
3. Забудьте о StatefulWidget! С Get он больше не понадобится. С другими менеджерами состояний вам, вероятно, придется использовать StatefulWidget, чтобы получить экземпляр вашего Provider, BLoC, MobX Controller и т.д. Но задумывались ли вы когда-нибудь о том, что ваш AppBar, Scaffold и большинство виджетов в вашем классе не имеют состояния и по сути являются Stateless? Так зачем хранить состояние всего класса, если можно хранить только состояние виджета, которые истинно Stateful? Get решает и эту проблему. Создавайте классы Stateless, всё делайте stateless. Если вам нужно обновить один компонент, просто оберните его GetBuilder.
4. Организуйте свой проект по-настоящему! Контроллеры не должны быть в вашем пользовательском интерфейсе, поместите ваш TextEditController или любой контроллер, который вы используете, в свой класс Controller.
5. Вам нужно инициировать событие для обновления виджета, как только он будет отрисован? GetBuilder имеет свойство initState, как и StatefulWidget, и вы можете вызывать события вашего контроллера прямо из него.
6. Вам необходимо инициировать такие действия как закрытия потоков, таймеров и т.д.? GetBuilder также имеет свойство dispose, с помощью которого вы можете вызывать события, как только этот виджет будет уничтожен.
7. Используйте потоки только при необходимости. Вы можете использовать свои StreamControllers внутри своего контроллера в обычном режиме, а также использовать StreamBuilder как обычно, но помните, что поток разумно потребляет память и реактивное программирование - это прекрасно, но вы не должны злоупотреблять этим. 30 потоков, открытых одновременно, может быть хуже, чем changeNotifier (а changeNotifier - очень плохо).
8. Обновляйте виджеты, не тратя на это оперативную память. Get сохраняет только идентификатор создателя GetBuilder и обновляет этот GetBuilder при необходимости. Потребление памяти для хранения идентификатора get в памяти очень низкое даже для тысяч GetBuilders. Когда вы создаете новый GetBuilder, вы фактически передаёте состояние GetBuilder, у которого есть идентификатор создателя. Новое состояние не создается для каждого GetBuilder, что экономит МНОГО ОЗУ для больших приложений. В основном ваше приложение будет полностью Stateless, и несколько виджетов, которые Stateful (при помощи GetBuilder), будут иметь общее состояние, и поэтому обновление обного обновит их всех. Состояние всего одно.
9. Get - всеведущий и в большинстве случаев точно знает, в какое время нужно извлечь контроллер из памяти. Вам не следует беспокоиться о том, когда утилизировать контроллер, Get знает, когда это сделать.
### Использование
```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.
```
**Готово!**
- Вы уже узнали, как управлять состояниями с помощью Get.
- Примечание: Возможно, вам нужна более крупная организация и не использовать свойство init. Для этого вы можете создать класс и расширить класс Bindings, указав в нем контроллеры, которые будут созданы в рамках этого маршрута. Контроллеры не будут создаваться в это время, наоборот, это просто инструкция, так что при первом использовании контроллера Get будет знать, где его искать. Get останется lazyLoad и продолжит удалять контроллеры, когда они больше не нужны. Смотрите пример в pub.dev, чтобы увидеть, как это работает.
Если вы перемещаетесь по многим маршрутам и вам нужны данные, которые были в вашем ранее используемом контроллере, вам просто нужно использовать снова GetBuilder (без инициализации):
```dart
class OtherClass extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GetBuilder<Controller>(
builder: (s) => Text('${s.counter}'),
),
),
);
}
```
Если вам нужно использовать свой контроллер во многих других местах и ​​за пределами GetBuilder, просто создайте get в своем контроллере и легко его получите. (или используйте `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.counter();
/// with no static method: Get.find<Controller>().counter();
/// 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();
}
}
```
И тогда вы можете напрямую получить доступ к своему контроллеру:
```dart
FloatingActionButton(
onPressed: () {
Controller.to.increment(),
} // This is incredibly simple!
child: Text("${Controller.to.counter}"),
),
```
Когда вы нажимаете FloatingActionButton, все виджеты, которые прослушивают переменную 'counter', будут обновлены автоматически.
### Как обрабатываются контроллеры
Допустим, у нас есть это:
`Class a => Class B (has controller X) => Class C (has controller X)`
В классе A контроллер ещё не находится в памяти, потому что вы его ещё не использовали (Get is lazyLoad). В классе B вы использовали контроллер, и он вошёл в память. В классе C вы использовали тот же контроллер, что и в классе B, Get будет разделять состояние контроллера B с контроллером C, и тот же контроллер всё ещё находится в памяти. Если вы закроете экран C и экран B, Get автоматически извлечёт контроллер X из памяти и освободит ресурсы, поскольку класс A не использует контроллер. Если вы снова перейдете к B, контроллер X снова войдет в память, если вместо перехода к классу C вы снова вернётесь к классу A, Get таким же образом выведет контроллер из памяти. Если класс C не использовал контроллер, а вы вынули класс B из памяти, ни один класс не будет использовать контроллер X, и, соответственно, он будет удален. Единственное исключение, которое может случиться с Get, - это если вы неожиданно удалите B из маршрута и попытаетесь использовать контроллер в C. В этом случае идентификатор создателя контроллера, который был в B, был удален, и Get был запрограммирован на удаление его из памяти каждого контроллера, у которого нет идентификатора создателя. Если вы намереваетесь сделать это, добавьте флаг "autoRemove: false" в GetBuilder класса B GetBuilder и используйте adoptID = true в GetBuilder класса C.
### Вам больше не понадобятся StatefulWidgets
Использование StatefulWidgets означает ненужное сохранение состояния всех экранов, даже если вам нужно минимально перестроить виджет, вы встроите его в Consumer/Observer/BlocProvider/GetBuilder/GetX/Obx, которые будет ещё одним StatefulWidget.
Класс StatefulWidget - это класс большего размера, чем StatelessWidget, который будет выделять больше оперативной памяти, и это может не иметь существенного значения между одним или двумя классами, но, безусловно, будет иметь место, когда у вас их 100!
Если вам не нужно использовать миксин, например TickerProviderStateMixin, использовать StatefulWidget с Get совершенно не нужно.
Вы можете вызывать все методы StatefulWidget прямо из GetBuilder.
Например, если вам нужно вызвать метод initState() или dispose(), вы можете вызвать их напрямую;
```dart
GetBuilder<Controller>(
initState: (_) => Controller.to.fetchApi(),
dispose: (_) => Controller.to.closeStreams(),
builder: (s) => Text('${s.username}'),
),
```
Гораздо лучший подход, чем этот, - использовать методы onInit() и onClose() непосредственно из вашего контроллера.
```dart
@override
void onInit() {
fetchApi();
super.onInit();
}
```
- ПРИМЕЧАНИЕ: Если вы хотите запустить метод в момент первого вызова контроллера, вам НЕ НУЖНО использовать для этого конструкторы, на самом деле, используя ориентированный на производительность пакет, такой как Get, это граничит с плохой практикой, потому что отклоняется от логики, в которой контроллеры создаются или выделяются (если вы создаете экземпляр этого контроллера, конструктор будет вызываться немедленно, вы будете заполнять контроллер ещё до того, как он будет использован, вы выделяете память, не используя её , это определенно вредит принципам этой библиотеки).
Методы onInit(); и onClose(); были созданы для этого, они будут вызываться при создании Контроллера или использоваться в первый раз, в зависимости от того, используете вы Get.lazyPut или нет.
Если вы хотите, например, вызвать ваш API для заполнения данных, вы можете забыть о старомодном методе initState/dispose, просто начните свой вызов api в onInit, и если вам нужно выполнить любую команду как закрытие потоков, используйте для этого onClose().
### Почему это существует
Цель этого пакета - предоставить вам законченное решение для навигации по маршрутам, управления зависимостями и состояниями с использованием минимально возможных зависимостей с высокой степенью разделения.
Get включает в себя все высокоуровневые и низкоуровневые API-интерфейсы Flutter, чтобы гарантировать, что вы работаете с наименьшими взаимозависимостями.
Мы централизуем всё в одном пакете, чтобы гарантировать, что у вас нет никакой взаимозависимости в вашем проекте.
Таким образом, вы можете поместить в свое представление только виджеты и оставить часть своей команды, которая работает с бизнес-логикой, свободной, чтобы работать с бизнес-логикой независимо от какого-либо элемента представления.
Это обеспечивает гораздо более чистую рабочую среду, так что часть вашей команды работает только с виджетами, не беспокоясь об отправке данных на ваш контроллер, а часть вашей команды работает только с бизнес-логикой, независимо от какого-либо элемента представления.
Итак, чтобы упростить это: вам не нужно вызывать методы в initState и отправлять их по параметрам на ваш контроллер или использовать для этого конструктор вашего контроллера, у вас есть метод onInit (), который вызывается в нужное время, чтобы вы могли начать ваши сервисы.
Вам не нужно вызывать устройство, у вас есть метод onClose(), который будет вызываться именно в тот момент, когда ваш контроллер больше не нужен и будет удален из памяти. Таким образом, оставьте представления только для виджетов, воздерживаясь от какой-либо бизнес-логики.
Не вызывайте метод удаления внутри GetxController, он ничего не сделает, помните, что контроллер не является виджетом, его не следует "удалять", и он будет автоматически и разумно удалён из памяти с помощью Get.
Если вы использовали какой-либо поток и хотите закрыть его, просто вставьте его в метод close. Пример:
```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();
}
}
```
Жизненный цикл контроллера:
- onInit() когда он создается.
- onClose() когда он закрывается для внесения каких-либо изменений при подготовке к удалению.
- удалено: у вас нет доступа к этому API, потому что он буквально удаляет контроллер из памяти. Он буквально удаляется, не оставляя следов.
### Другие способы использования
Вы можете использовать экземпляр контроллера непосредственно со значением GetBuilder:
```dart
GetBuilder<Controller>(
init: Controller(),
builder: (value) => Text(
'${value.counter}', //here
),
),
```
Вам также может понадобиться экземпляр вашего контроллера вне GetBuilder, и вы можете использовать эти подходы для достижения этой цели:
```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
)
),
```
или
```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
),
),
```
- Для этого можно использовать "неканоничные" подходы. Если вы используете какой-либо другой менеджер зависимостей, например get_it, modular и т.д., и просто хотите доставить экземпляр контроллера, вы можете сделать это:
```dart
Controller controller = Controller();
[...]
GetBuilder<Controller>(
init: controller, //here
builder: (_) => Text(
'${controller.counter}', // here
),
),
```
### Уникальные идентификаторы
Если вы хотите уточнить элемент управления обновлением виджета с помощью GetBuilder, вы можете назначить им уникальные идентификаторы:
```dart
GetBuilder<Controller>(
id: 'text'
init: Controller(), // use it only first time on each controller
builder: (_) => Text(
'${Get.find<Controller>().counter}', //here
),
),
```
И обновите это следующим образом:
```dart
update(['text']);
```
Также можно наложить условия на обновление:
```dart
update(['text'], counter < 10);
```
GetX делает это автоматически и восстанавливает только виджет, который использует точную переменную, которая была изменена, если вы измените переменную на ту же самую, что и предыдущая, и это не означает изменения состояния, GetX не будет перестраивать виджет для экономии памяти и CPU (На экране отображается 3, и вы снова меняете переменную на 3. В большинстве менеджеров состояний это вызовет новую перестройку, но с GetX виджет будет перестраиваться снова только в том случае, если на самом деле его состояние изменилось).
## Смешивание двух менеджеров состояний
Некоторые открыли запрос, так как они хотели использовать только один тип реактивной переменной и другой механизм, и для этого нужно было вставить Obx в GetBuilder.
Подумав об этом, был создан MixinBuilder.
Он позволяет как реактивные изменения путем изменения переменных ".obs", так и механические обновления через update().
Однако из 4 виджетов он - тот, который потребляет больше всего ресурсов, поскольку помимо подписки на получение событий изменений от своих дочерних элементов, он подписывается на метод обновления своего контроллера.
Расширение GetxController важно, поскольку у них есть жизненные циклы, и они могут "запускать" и "завершать" события в своих методах onInit() и onClose().
Вы можете использовать для этого любой класс, но я настоятельно рекомендую вам использовать класс GetxController для размещения ваших переменных, независимо от того, наблюдаемы они или нет.
## GetBuilder vs GetX vs Obx vs MixinBuilder
За десять лет работы с программированием я смог извлечь несколько ценных уроков.
Мой первый контакт с реактивным программированием был таким: "Вау, это невероятно", и на самом деле реактивное программирование невероятно.
Однако он подходит не для всех ситуаций. Часто всё, что вам нужно, - это изменить состояние 2 или 3 виджетов одновременно или кратковременное изменение состояния, и в этом случае реактивный подход неплох, но не подходит.
Реактивное программирование требует более высокого потребления оперативной памяти, что может быть компенсировано отдельным рабочим процессом, который гарантирует, что только один виджет будет перестроен и при необходимости, но создание списка из 80 объектов, каждый с несколькими потоками, не является хорошей идеей.
Откройте dart inspect и проверьте, сколько потребляет StreamBuilder, и вы поймёте, что я пытаюсь вам сказать.
Имея это в виду, я создал простой менеджер состояний. Это просто, и это именно то, что вы должны от него требовать: обновление состояния в блоках простым и наиболее экономичным способом.
GetBuilder очень экономичен в оперативной памяти, и вряд ли существует более экономичный подход, чем он (по крайней мере, я не могу представить его, если он существует, сообщите нам).
Однако GetBuilder по-прежнему является механическим менеджером состояний, вам нужно вызвать update() так же, как вам нужно было бы вызвать notifyListeners() провайдера.
Бывают и другие ситуации, когда реактивное программирование действительно интересно, и не работать с ним - все равно, что изобретать колесо.
Имея это в виду, GetX был создан, чтобы предоставить всё самое современное и продвинутое в менеджере состояний.
Он обновляет только то, что необходимо, и при необходимости, если у вас есть ошибка и вы отправляете 300 изменений состояния одновременно, GetX будет фильтровать и обновлять экран только в том случае, если состояние действительно изменяется.
GetX по-прежнему более экономичен, чем любой другой менеджер реактивного состояния, но он потребляет немного больше оперативной памяти, чем GetBuilder.
Думая об этом и стремясь максимизировать потребление ресурсов, Obx был создан.
В отличие от GetX и GetBuilder, вы не сможете инициализировать контроллер внутри Obx, это просто виджет с StreamSubscription, который получает события изменения от ваших детей, вот и всё.
Он более экономичен, чем GetX, но проигрывает GetBuilder, что и следовало ожидать, поскольку он является реактивным, а GetBuilder имеет самый упрощенный подход к хранению хэш-кода виджета и его StateSetter.
С Obx вам не нужно писать свой тип контроллера, и вы можете услышать изменение от нескольких разных контроллеров, но его необходимо инициализировать перед этим, используя примерный подход в начале этого файла readme или используя класс Bindings.
... ...