Home > Software design >  how can I get the other controller's variable inside one controller in flutter using getx
how can I get the other controller's variable inside one controller in flutter using getx

Time:09-17

This is an issue related to the getx in flutter. I have 2 controllers. ContractsController and NotificationController. In ContractsController I have put the value into observer variable by calling the Api request. What I want now is to get that variable's data in another controller - NotificationController. How to get that value using getx functions?

ContractsController

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

The expiringContracts is updated successfully with data after the api request. Now, I want to get that value in NotificationController

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

}

CodePudding user response:

Overview

A couple solutions come to mind:

  • pass the expiringContracts list as a constructor argument to NotificationsController if you only need this done once at instantiation, or
  • use a GetX worker to update NotificationsController every time expiringContracts is updated

The first solution isn't related to GetX, rather it's just async coordination between ContractsController and NotificationsController, so lets focus on the 2nd solution: GetX Workers.

Details

In NotificationsController, create a method that will receive expiringContracts.

Something like:

class NotificationsController extends GetxController {

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

}

Please note: none of this code is tested. I'm writing this purely in StackOverflow, so consider this pseudo-code.

In ContractsController we'll supply the above callback method as a constructor arg:

In ContractsController, something like:

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
  }
}

Here the GetX ever worker takes the observable as first argument, and a function as 2nd argument. That function must take an argument of type that matches the observed variable, i.e. List<ExpiringContract>, hence the Type of refreshContractsCallback was defined as Function(List<ExpiringContract>).

Now whenever the observable expiringContracts is updated in ContractsController, refreshContractsCallback(contracts) will be called, which supplies the list of expiring contracts to NotificationsController via refreshContracts.

Finally, when instantiating the two controllers inside the build() method of your route/page:

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

Timeline of Events

  • NotificationsController gets created as nx.
  • nx.onInit() runs, slow call of refreshContracts() starts
  • ContractsController gets created, with nx.refreshContracts callback
  • your page paints
    • nx has no contracts data at this point, so you'll prob. need a FutureBuilder or an Obx/ GetX StatelessWidget that'll rebuild when data eventually arrives
  • when refreshContracts() finishes, ever worker runs, sending contracts to nx
  • nx.refreshContracts(contracts) is run, doing something with contracts

Notes

  • async/await was removed from nx.onInit
  • ever worker will run when refreshContract finishes

CodePudding user response:

There were some powerful approaches in GetX. I solved this issue with Get.put and Get.find

Here is the code that I added.

ContractsController

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

NotificationController

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

Finally, I have found that GetX is simple but powerful for state management in flutter. Thanks.

  • Related