как я могу получить переменную другого контроллера внутри одного контроллера в flutter с помощью getx

#flutter #oop #dart #flutter-getx

Вопрос:

Это проблема, связанная с getx в flutter. У меня есть 2 контроллера. Контролер контрактов и контролер уведомлений. В ContractsController я поместил значение в переменную наблюдателя, вызвав запрос Api. Теперь я хочу получить данные этой переменной в другом контроллере — NotificationController. Как получить это значение с помощью функций getx?

Контролер контрактов

 class ContractsController extends GetxController {
  ExpiringContractRepository _expiringContractRepository;

  final expiringContracts = <ExpiringContract>[].obs; // This is the value what I want in another controller

  ContractsController() {
    _expiringContractRepository = new ExpiringContractRepository();
  }

  @override
  Future<void> onInit() async {
    await refreshContracts();
    super.onInit();
  }

  Future refreshContracts({bool showMessage}) async {
    await getExpiringContracts();
    if (showMessage == true) {
      Get.showSnackbar(Ui.SuccessSnackBar(message: "List of expiring contracts refreshed successfully".tr));
    }
  }

  Future getExpiringContracts() async {
    try {
      expiringContracts.value = await _expiringContractRepository.getAll(); // put the value from the api
    } catch (e) {
      Get.showSnackbar(Ui.ErrorSnackBar(message: e.toString()));
    }
  }
}
 

Истекающие контракты успешно обновляются данными после запроса api.
Теперь я хочу получить это значение в NotificationController

Контроллер уведомлений

 class NotificationsController extends GetxController {
  final notifications = <Notification>[].obs;
  ContractsController contractsController;

  NotificationsController() {
  }

  @override
  void onInit() async {
    contractsController = Get.find<ContractsController>();
    print(contractsController.expiringContracts);  // This shows an empty list ?????  
    super.onInit();
  }

}
 

Ответ №1:

Обзор

На ум приходит пара решений:

  • передайте expiringContracts список в качестве аргумента конструктора NotificationsController , если вам нужно сделать это только один раз при создании экземпляра, или
  • используйте работника getX для обновления NotificationsController при каждом expiringContracts обновлении

Первое решение не связано с getX, скорее это просто асинхронная координация между ContractsController и NotificationsController , поэтому давайте сосредоточимся на 2-м решении: работники getX.

Подробные сведения

В NotificationsController , создайте метод, который будет получать expiringContracts .

Что-то вроде:

 class NotificationsController extends GetxController {

  void refreshContracts(List<ExpiringContract> contracts) {
    // do something
  }

}
 

Пожалуйста, обратите внимание: ни один из этих кодов не тестируется. Я пишу это исключительно в StackOverflow, поэтому рассмотрите этот псевдокод.

В ContractsController мы предоставим приведенный выше метод обратного вызова в качестве конструктора arg:

В ContractsController , что-то вроде:

 class ContractsController {

  final expiringContracts = <ExpiringContract>[].obs

  final Function(List<ExpiringContract>) refreshContractsCallback;

  ContractsController(this.refreshContractsCallback);

  @override
  void onInit() {
    super.onInit();
    refreshContracts();  // do your stuff after super.onInit
    ever(expiringContracts, refreshContractsCallback);
    // ↑ contracts → refreshContractsCallback(contracts)
    // when expiringContracts updates, run callback with them
  }
}
 

Здесь ever работник getX принимает наблюдаемое в качестве первого аргумента, а функцию в качестве 2-го аргумента. Эта функция должна принимать аргумент типа, соответствующего наблюдаемой переменной , т. е. List<ExpiringContract> , следовательно, Тип refreshContractsCallback был определен как Function(List<ExpiringContract>) .

Теперь всякий раз , когда наблюдаемое expiringContracts обновляется ContractsController , refreshContractsCallback(contracts) будет вызываться, который предоставляет список истекающих контрактов в NotificationsController via refreshContracts .

Наконец, при создании экземпляров двух контроллеров внутри build() метода вашего маршрута/страницы:

 NotificationsController nx = Get.put(NotificationsController());
ContractsController cx = Get.put(ContractsController(nx.refreshContracts));
 

Хронология событий

  • NotificationsController создается как nx .
  • nx.onInit() работает, медленный вызов refreshContracts() стартов
  • ContractsController создается с nx.refreshContracts обратным вызовом
  • ваша страница рисует
    • nx на данный момент у вас нет данных о контрактах, так что вы, вероятно, будете. нужен a FutureBuilder или Obx / GetX StatelessWidget , который восстановится, когда данные в конечном итоге поступят
  • когда refreshContracts() заканчивается, ever работник запускается, отправляя контракты на nx
  • nx.refreshContracts(contracts) бежит, что-то делает с контрактами

Примечания

  • асинхронное/ожидание было удалено из nx.onInit
  • ever работник будет работать, когда refreshContract закончит

Комментарии:

1. Привет. @Бейкер. Спасибо за ваш любезный ответ. Такой подход является замечательным. Спасибо. Это так полезно. В любом случае, я решаю эту проблему другим способом. Я опубликую свой ответ здесь, так что у вас есть немного времени, чтобы проверить это?

Ответ №2:

В getX было несколько мощных подходов. Я решил эту проблему с помощью Get.put и Get.find

Вот код, который я добавил.

Контролер контрактов

 class ContractsController extends GetxController {
  ExpiringContractRepository _expiringContractRepository;

  final expiringContracts = <ExpiringContract>[].obs; // This is the value what I want in another controller

  ContractsController() {
    _expiringContractRepository = new ExpiringContractRepository();
  }

  @override
  Future<void> onInit() async {
    await refreshContracts();
    super.onInit();
  }

  Future refreshContracts({bool showMessage}) async {
    await getExpiringContracts();
    if (showMessage == true) {
      Get.showSnackbar(Ui.SuccessSnackBar(message: "List of expiring contracts refreshed successfully".tr));
    }
  }

  Future getExpiringContracts() async {
    try {
      expiringContracts.value = await _expiringContractRepository.getAll(); // put the value from the API
      // ******************************** //
      Get.put(ContractsController()); // Added here
    } catch (e) {
      Get.showSnackbar(Ui.ErrorSnackBar(message: e.toString()));
    }
  }
}
 

Контроллер уведомлений

 class NotificationsController extends GetxController {
  final notifications = <Notification>[].obs;
  ContractsController contractsController;

  NotificationsController() {
  }

  @override
  void onInit() async {
    // ******************************** //
    contractsController = Get.find<ContractsController>(); // Added here.
    print(contractsController.expiringContracts);  // This shows the updated value
    super.onInit();
  }
}
 

Наконец, я обнаружил, что getX прост, но мощен для управления состоянием в flutter.
Спасибо.

Комментарии:

1. Привет @Денис, если это сработает для тебя, отлично. Одна вещь, которую нужно проверить: ContractsController удаляется ли она из памяти, когда вы покидаете/открываете вызывающую страницу getExpiringContracts() ? (Если это так, вы увидите сообщение «ContractsController удален из памяти» в консоли отладки или запуска.) Я спрашиваю , так как контроллер регистрируется внутри ContractsController , что является техникой/идиомой, которую я раньше не видел.

2. Привет. @Бейкер. Да, я только что проверил это в своем приложении. Поскольку представление уведомлений будет перенаправляться из представления контракта, это работает хорошо, и нет сообщения о том, что contractsController был удален. Но когда я выхожу из представления уведомлений, оно показывает удаленное сообщение контроллера. Теперь я понимаю, что Get.put и find доступны в одном и том же маршруте стека. Спасибо за ваш любезный ответ.