0

I'm new to GetX flutter state management. I'm using two controllers, one for Login and other for Home data(fetching some restaurants data through API call). I'm having trouble in bindings. I'm using bindings in my app following GetX docs. But I'm unable to use it properly and getting error. Following is the code -:

main.dart

void main() async {
  await GetStorage.init('My Storage');
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flunkey Task',
      getPages: [
        GetPage(
            name: '/',
            page: () => LandingPage(),
            binding: BindingsBuilder(() {
              Get.lazyPut<LoginController>(() => LoginController());
            })),
        GetPage(
            name: '/login',
            page: () => LoginScreen(),
            binding: BindingsBuilder(() {
              Get.lazyPut<LoginController>(() => LoginController());
            })),
        GetPage(
          name: '/home',
          page: () => HomeScreen(),
          binding: BindingsBuilder(() {
            Get.lazyPut<HomeController>(() => HomeController());
          }),
        )
      ],
      initialRoute: '/',
    );
  }
}

class LandingPage extends StatelessWidget {
  LandingPage({Key? key}) : super(key: key);

  final _controller = Get.find<LoginController>();
  @override
  Widget build(BuildContext context) {
    SizeConfig().init(context);

    return Obx(() =>
        _controller.isLoggedIn.value == true ? HomeScreen() : LoginScreen());
  }
}

loginController.dart

class LoginController extends GetxController {
  final box = GetStorage('My Storage');

  var isLoggedIn = false.obs;

  final formKey = GlobalKey<FormState>();
  final usernameTED = TextEditingController();
  final passwordTED = TextEditingController();

  @override
  void onInit() {
    isLoggedIn(loginStatus);
    super.onInit();
  }

  @override
  void onClose() {
    usernameTED.dispose();
    passwordTED.dispose();
    super.onClose();
  }

  String? checkUsername(String username) {
    if (username.isEmpty || username.length < 3 || username.length > 11) {
      return 'Username must have 3-11 characters';
    }
    return null;
  }

  String? checkPassword(String password) {
    if (password.isEmpty || password.length < 3 || password.length > 11) {
      return 'Password must have 3-11 characters';
    }
    return null;
  }

  Future<void> login() async {
    if (!formKey.currentState!.validate()) {
      return;
    }

    if ((usernameTED.text.trim() == 'flunkey' &&
            passwordTED.text.trim() == 'password123') ||
        (usernameTED.text.trim() == 'user' &&
            passwordTED.text.trim() == 'password123')) {
      formKey.currentState!.save();

      await changeLoginStatus(true);
      await saveUserName(usernameTED.text);
      usernameTED.clear();
      passwordTED.clear();
    } else {
      Get.snackbar('Login Error', 'User does not exists',
          backgroundColor: Colors.red[400]);
    }
  }

  void signOut() async {
    await changeLoginStatus(false);
  }

  Future<void> changeLoginStatus(bool status) async {
    await box.write('status', status);
    isLoggedIn(status);
  }

  Future<void> saveUserName(String name) async {
    await box.write('name', name);
  }

  bool get loginStatus => box.read('status') ?? false;
  String get currentUserName => box.read('name') ?? '';
}

homeController.dart

class HomeController extends GetxController {
  final _isLoading = false.obs;
  final _restaurantData = <restau.Datum>[].obs;

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

  bool get isLoading => _isLoading.value;
  List<restau.Datum> get getRestaurants => _restaurantData;

  Future<void> getData() async {
    try {
      _isLoading(true);
      var apiData = await RestaurantDataApiCall.getRestaurantData();
      _restaurantData.value = apiData!.data.data;
      _isLoading(false);
    } catch (e, s) {
      print(e);
      print(s);
    }
  }
}

Following is the error I'm getting.

enter image description here

I'm using Get.find() on Login Screen and Get.find() on Home screen as following,

enter image description here

enter image description here

Please guide me how to properly use Bindings in GetX.

Shubham Vimal
  • 55
  • 1
  • 12
  • Randal Schwartz, on pub.dev Getx is more popular than RiverPod, BLoC etc. Is Getx bad ? – Shubham Vimal Sep 21 '22 at 06:51
  • Nice to see you here @RandalSchwartz . Shubham the most comments about GetX is that about your coupling. This is an all in one package , so it's easy to use all feature of getx and and make a hard coupling in your code. And you know, hard coupling don't is a good thing. Personally I like GetX, but only for state manager and I use it only for this purpose. Getx is easy to configure, mantain and understand. – Sergio Clemente Sep 21 '22 at 11:28
  • Thanks @Sergio Clemente , mostly I use Getx for state management. But only a few times I've used it for routing or showing dialog. – Shubham Vimal Sep 21 '22 at 15:08

2 Answers2

7

I don't like to bind the controllers on route. I create a MainBind.dart and put inside this class all getx controllers.

class MainBinding implements Bindings {

 @override
  Future<void> dependencies() async{
      Get.lazyPut<AppController>(() => AppController(), fenix: true);
  }
}

And in my Main.dart :

void main() async{
  WidgetsFlutterBinding.ensureInitialized();

  MainBinding mainBinding = MainBinding();
  await mainBinding.dependencies();

  runApp(const MyApp());

}

In this way I'm sure that Controllers are binded. But you can try use Put insted lazyPut too..

Sergio Clemente
  • 809
  • 8
  • 12
  • Thanks Sergio Clemente, your answer works. But I want to know whether it is a good thing to put all controllers before runApp(). Won't it be more suitable that when we need specific controller only then we use Get.put() ? If you can explain, it will be very helpful. – Shubham Vimal Sep 21 '22 at 06:50
  • 1
    If you use LazyPut the Controller will be inputed only in the time that you call . So for this reason you don't be a memory loss or something like this. For this reason I use Fenix too, because if the Controller is no more used the memory is cleanup. And Fenix true back again the controller to memory. check this : https://stackoverflow.com/questions/71734768/diff-between-get-put-and-get-lazyput#:~:text=To%20my%20understanding%20put%20already,in%20the%20builder%20for%20it. – Sergio Clemente Sep 21 '22 at 11:37
  • Tnaks @Sergio Clemente for this. Mush appreciated. But there is an option called ' permanent ' but no ' fenix '. Rest assured I've understood put and lazyput. Thanks for your help. – Shubham Vimal Sep 21 '22 at 15:06
0

You can use StatefulWidget with state class which will contain your controller.

E.g.

StateClass bla, bla {
  late final yourController = Get.put<YourController>();

  @override
  dispose() {
    Get.delete<YourController>();
  }
}

That's it!

Mykola Meshkov
  • 126
  • 1
  • 4