Brightcode
Brightcode

Reputation: 768

Data inside GetxController always returns null or Empty when used inside a GetBuilder

I am trying to get data from a List inside a GetxController but it keeps returning null/Empty whenever I try to access it inside a GetBuilder even when I already assigned a value to it. Inside the GetxController I can print out the value but when I want to use it it returns null/Empty

class BankController extends GetxController implements GetxService {
  BankRepo bankRepo = BankRepo();
  List<Bank> banks = [];

 
  Future<void> getBanks() async {
    ResponseModel response = await bankRepo.banks();
    if (response.statusCode == 200) {
      print(banks);    // => []
      banks = (response.data as List).map((e) => Bank.fromJson(e)).toList();
      print(banks);   // => [Instance of 'Bank', Instance of 'Bank', Instance of 'Bank', Instance of 'Bank', Instance of 'Bank', .....
      update();
    }
  }

}

To Reproduce:

Steps to reproduce the behavior:

Expected behavior: After calling the getBanks function from BankController, a data is been assigned to List<Bank> banks = [];, then I create a GetBuilder inside add_bank.dart to get the data inside List<Bank> banks = []; and add them to my Dropdown. The dropdown will then display the list of all the banks.

Flutter Version:

Flutter Channel stable, v2.10.3

Getx Version:

get: ^4.6.5

Minimal reproduce code: add_bank.dart:

class AddBankView extends StatefulWidget {
  const AddBankView({Key? key}) : super(key: key);

  @override
  State<AddBankView> createState() => _AddBankViewState();
}

class _AddBankViewState extends State<AddBankView> {
  Bank selectedBank = Bank(name: "Select Bank", code: "");

  @override
  void initState() {
    getBanks();
    super.initState();
  }

  getBanks() async {
    await BankController().getBanks();
    BankController ctn = Get.find<BankController>();
    print(ctn.banks); // => []
  }

  @override
  Widget build(BuildContext context) {
  
    return SafeArea(
      child: Scaffold(
        appBar: customAppbar("New Bank"),
        body: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(20.0),


            child: GetBuilder<BankController>(
                init: BankController(),
                builder: (bankController) {
                  print(bankController.banks); // => []
                  return Column(
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        
                        Container(
                          height: 50,
                          width: MediaQuery.of(context).size.width,
                          margin: const EdgeInsets.symmetric(vertical: 20),
                          child: DropdownButtonHideUnderline(
                            child: GFDropdown(
                              padding: const EdgeInsets.symmetric(vertical: 15),
                              borderRadius: BorderRadius.circular(5),
                              border: const BorderSide(
                                  color: Colors.transparent,
                                  width: 0,
                                  style: BorderStyle.none),
                              dropdownButtonColor: Colors.transparent,
                              dropdownColor:
                                  Theme.of(context).colorScheme.surface,
                              value: selectedBank,
                              icon: Icon(
                                IconlyBold.arrowDownCircle,
                                color: Theme.of(context).colorScheme.onSurface,
                                size: 22,
                              ),
                              onChanged: (value) {
                                setState(() {
                                  selectedBank = value as Bank;
                                });
                              },
                              items: bankController.banks
                                  .map((Bank value) => DropdownMenuItem<Bank>(
                                        value: value,
                                        child: CustomText(value.name,
                                            color: theme.secondary
                                                .withOpacity(0.85)),
                                      ))
                                  .toList(),
                            ),
                          ),
                        ),
                      ]);
                }),
          ),
        ),
      ),
    );
  }
}

Bind Controllers in get_init.dart:

initControllers() {
  //Repositories
  Get.lazyPut(() => BankRepo());

  //Controllers
  Get.lazyPut(() => BankController());
}

main.dart:

void main() async {
  initControllers();
  runApp(const MyApp());
}

I ask you guys, how can I solve it. If you have any questions you can ask me.

Upvotes: 1

Views: 1031

Answers (3)

ShinMyth
ShinMyth

Reputation: 644

There are two ways on doing this either you make it a service or a controller.

Service approach:

class BankService extends GetxService {}

main.dart file

await Get.putAsync<BankService>(() async => BankService());

add_bank_view.dart file

getBanks() async {
   final ctn = Get.find<BankService>();
   await ctn.getBanks(); 
   print(ctn.banks); // => []
}

Controller approach:

class BankController extends GetxController {}

add_bank_view.dart file

final controller = Get.put(BankController());

getBanks() async {
   await controller.getBanks(); 
   print(controller.banks); // => []
}

Upvotes: 1

Hayi Nukman
Hayi Nukman

Reputation: 1201

Its a bit tricky when using Get.lazyPut or Get.put or instead Get.create and depending on the case that you want it to act. Like Get.lazyPut will not create the instance at the time, and will create it when you call Get.find.

And in your case, this is the code from your question.

  getBanks() async {
    await BankController().getBanks();
    BankController ctn = Get.find<BankController>();
    print(ctn.banks); // => []
  }

when you call await BankController().getBanks(); you literally create a new instance of BankController using its constructor. and then on the line below, you create another instance of BankController but using the GetX dependency injector (lazyPut factory). So the instance ctn and the instance created in this BankController().getBanks() is completely different instance. and obviously will produce empty banks.

Get.lazyPut, or Get.put didn't simply make all of the class become singleton, you have to call Get.find and use the instance from it to make it act as a single instance.

So, in conclusion, your code should be like this:

    BankController ctn = Get.find<BankController>();
    await ctn.getBanks();
    print(ctn.banks);

More detail about the difference between put, lazyPut, and create can be found here:

https://github.com/jonataslaw/getx/blob/master/documentation/en_US/dependency_management.md#differences-between-methods

Upvotes: 2

Rohit Krishna
Rohit Krishna

Reputation: 202

Have you used BankController in any other page?

If not, use Get.put instead of Get.find :-

final BankController bankControl = Get.put(BankController());

Upvotes: 1

Related Questions