Flutter Getx - "Xxx" not found. You need to call "Get.put(Xxx())" - But I have called Get.put(Xxx())
Asked Answered
L

7

22

I have this global bindings class to initialise some services and I need it to be initialised straight away:

import 'package:get/get.dart';
import 'package:vepo/data/data_provider/local_data_provider.dart';
import 'package:vepo/data/data_source/local_data_source.dart';

import 'services/authentication_service.dart';

class GlobalBindings extends Bindings {
  final LocalDataProvider _localDataProvider = LocalDataProvider();
  @override
  void dependencies() {
    Get.put<AuthenticationService>(AuthenticationService(), permanent: true);
    Get.put<LocalDataProvider>(_localDataProvider, permanent: true);
    Get.put<LocalDataSource>(LocalDataSource(_localDataProvider),
        permanent: true);
  }
}

Which is in my initialBindings:

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Vepo',
      initialRoute: AppPages.INITIAL,
      initialBinding: GlobalBindings(),
      transitionDuration: const Duration(milliseconds: 500),
      defaultTransition: Transition.rightToLeft,
      getPages: AppPages.routes,
      home: Root(),
      theme: homeTheme,
    );
  }
}

Then in a class constructor I try to "find" it:

class UserLocalRepository extends VpService implements IUserLocalRepository {
  UserLocalRepository() {
    localDataSource = Get.find<LocalDataSource>();
  }

  LocalDataSource localDataSource;

And I get this error:

══════ Exception caught by widgets library ═══════════════════════════════════
The following message was thrown building App(dirty):
"LocalDataSource" not found. You need to call "Get.put(LocalDataSource())" or "Get.lazyPut(()=>LocalDataSource())"

The relevant error-causing widget was
App
lib/main.dart:17
When the exception was thrown, this was the stack
#0      GetInstance.find
package:get/…/src/get_instance.dart:272
#1      Inst.find
package:get/…/src/extension_instance.dart:66
#2      new UserLocalRepository
package:vepo/…/user/user_local_repository.dart:10
#3      new LoggedOutNickNameBinding
package:vepo/…/logged_out_nickname/logged_out_nick_name_binding.dart:11
#4      AppPages.routes
package:vepo/…/routes/app_pages.dart:29
...
════════════════════════════════════════════════════════════════════════════════

This is the binding mentioned in the error message:

class LoggedOutNickNameBinding extends Bindings {
  LoggedOutNickNameBinding() {
    _repository = Get.put(UserLocalRepository());
  }

  IUserLocalRepository _repository;

  @override
  void dependencies() {
    Get.lazyPut<LoggedOutNickNameController>(
      () => LoggedOutNickNameController(_repository),
    );
  }
}

Why are the "initialBindings" not initialised, so that my app can "find" them when the app starts up?

Labrum answered 15/11, 2020 at 0:41 Comment(0)
B
28

I'm guessing that there's a timing / ordering mismatch with when your GlobalBindings.dependencies() method gets called and when you need those resources.

You could try initializing your Bindings class prior to GetMaterialApp instead of passing your Bindings class to GetMaterialApp.

void main() async {
  //WidgetsFlutterBinding.ensureInitialized(); // uncomment if needed for resource initialization
  GlobalBindings().dependencies();
  runApp(MyApp());
}

Tangent

Just guessing here, but are some of the classes you're initializing via Get.put are slow-startup (i.e. async) before they are ready to use?

If so you could use

Get.putAsync<YourClass>(() async {
 // init YourClass here
 return await YourClass.slowInit();

}

Example

I recently ran an exercise of performing async Bindings initialization prior to app being loaded for user interaction. Here's the code:

import 'package:flutter/material.dart';
import 'package:get/get.dart';

enum Version {
  lazy,
  wait
}
// Cmd-line args/Env vars: https://mcmap.net/q/168065/-how-do-you-pass-arguments-from-command-line-to-main-in-flutter-dart
const String version = String.fromEnvironment('VERSION');
const Version running = version == "lazy" ? Version.lazy : Version.wait;

void main() async {
  //WidgetsFlutterBinding.ensureInitialized(); // if needed for resources
  if (running == Version.lazy) {
    print('running LAZY version');
    LazyBindings().dependencies();
  }

  if (running == Version.wait) {
    print('running AWAIT version');
    await AwaitBindings().dependencies(); // await is key here
  }

  runApp(MyApp());
}

class LazyBindings extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut<MyDbController>(() => MyDbController());
  }
}

/// Simulates a slow (2 sec.) init of a data access object.
/// Calling [await] dependencies(), your app will wait until dependencies are loaded.
class AwaitBindings extends Bindings {
  @override
  Future<void> dependencies() async {
    await Get.putAsync<MyDbController>(() async {
      Dao _dao = await Dao.createAsync();
      return MyDbController(myDao: _dao);
    });
  }
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final MyDbController dbc = Get.find();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GetX Bindings'),
      ),
      body: Center(
        child: Obx(() => Text(dbc.dbItem.value)),
      ),
    );
  }
}

class MyDbController extends GetxController {
  Dao myDao;

  MyDbController({this.myDao});

  RxString dbItem = 'Awaiting data'.obs;

  @override
  void onInit() {
    super.onInit();
    initDao();
  }

  Future<void> initDao() async {
    // instantiate Dao only if null (i.e. not supplied in constructor)
    myDao ??= await Dao.createAsync();
    dbItem.value = myDao.dbValue;
  }
}

class Dao {
  String dbValue;

  Dao._privateConstructor();

  static Future<Dao> createAsync() async {
    var dao = Dao._privateConstructor();
    print('Dao.createAsync() called');
    return dao._initAsync();
  }

  /// Simulates a long-loading process such as remote DB connection or device
  /// file storage access.
  Future<Dao> _initAsync() async {
    dbValue = await Future.delayed(Duration(seconds: 2), () => 'Some DB data');
    print('Dao._initAsync done');
    return this;
  }
}

Burtie answered 15/11, 2020 at 5:9 Comment(0)
W
4

In my case:

TestCartController? cartController;

if(condition){
 cartController = Get.isRegistered<TestCartController>()
            ? Get.find<TestCartController>()
            : Get.put(TestCartController());

}

But in some other widget, I was referring to the above controller as:

final cartController = Get.find<TestCartController>();

The type mismatch issue is because both instances are different, so it was causing a problem for me. I simply removed that ? mark and it worked.

TestCartController cartController;

if(condition){
 cartController = Get.isRegistered<TestCartController>()
            ? Get.find<TestCartController>()
            : Get.put(TestCartController());

}
Weinshienk answered 30/7, 2021 at 20:46 Comment(0)
G
3
Add fenix : true;

class AppBinding implements Bindings {
      @override
      void dependencies() {
        Get.lazyPut<GeneralController>(() => GeneralController(), fenix: true);
        Get.lazyPut<UserController>(() => UserController(), fenix: true);
      }
    }

This will solve your issue.

Greathearted answered 13/9, 2022 at 5:38 Comment(2)
An explanation would be in order. E.g., what did you change? Why did you change it? Why does it work? What is the idea/gist? From the Help Center: "...always explain why the solution you're presenting is appropriate and how it works". Please respond by editing (changing) your answer, not here in comments (but *** *** *** *** *** without *** *** *** *** *** "Edit:", "Update:", or similar - the answer should appear as if it was written today).Tympany
OK, that will not be anytime soon: "This account is temporarily suspended for plagiarism."Tympany
A
2

Reason: It happened when Get.find() was called before Get.put(Controller). Calling Get.find() before initializing the Get.put() shows the error.

Solution: Simply you can call Get.put(controller) into your main class as below. So then Get.find() from any class will get it.

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final YourController controller = Get.put(YourController());
  ...

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
    ......

}
Amadou answered 14/12, 2021 at 10:5 Comment(0)
C
1

If your controller class is not bind with any stateful/stateless class then you can initialise the controller class in the main method like this

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // init app controller
  AppController appController =
  await Get.putAsync<AppController>(() async => AppController(), permanent: true);
  // init temp storage
  await GetStorage.init();
  runApp(const MyApp());
}
Cousin answered 28/3, 2023 at 16:25 Comment(0)
A
1

I encountered this error and was able to resolve it by replacing Obx with GetX. Instead of:

Obx(() => Text(controller.text)),

use

GetX<MyController>(builder:(c) => Text(c.text)),

Note that you should use the GetX builder controller (c) instead of the default one (controller).

Anchises answered 20/9, 2023 at 6:32 Comment(0)
C
0

Make sure your LocalDataSource extends and implement these classes.

LocalDataSource extends GetxController implements GetxService
Costrel answered 4/3, 2023 at 8:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.