Search code examples
flutterdartfirebase-authenticationgoogle-cloud-functionsflutter-getx

Flutter - user model is NULL even after binding it using bindStream


I am trying to add a shopping cart functionality to my app wherein the UserModel contains fields name,email,uid and cart. I have created an AuthController extends GetxController where I have created userModel isntance and then in setInitialScreen function I am binding the userModel to a function "listenToUser()" which is a Stream and recieves snapshot from firestore and maps it to the userModel. But on printing the userModel I see null being printed in the console meaning the data is not getting binded which is causing problems as i can't access the cart stored in the userModel. Edit: I saw that I need to attach obs to userModel like: Rx<model.User>? userModel = model.User().obs; but there's a problem that all the fields in this model are required and how can i pass these field values when they have not yet been intialized. Console output: Console showing null for userModel

AuthController code

class AuthController extends GetxController {
  static AuthController instance = Get.find();
  late Rx<User?> _user;
  Rx<model.User>? userModel;

  @override
  void onReady() {
    super.onReady();
    _user = Rx<User?>(firebaseAuth.currentUser);
    _user.bindStream(firebaseAuth.authStateChanges());
    ever(_user, _setInitialScreen);
  }

  _setInitialScreen(User? user) {
    if (user == null) {
      Get.offAll(() => LoginScreen());
    } else {
      userModel?.bindStream(listenToUser());
      Get.offAll(() => const HomeScreen());
      print(userModel); // PRINTING USER MODEL TO SEE IF ITS NULL
      // userModel?.bindStream(listenToUser());
    }
  }

  // registering the user
  void registerUser(String username, String email, String password) async {
    try {
      if (username.isNotEmpty && email.isNotEmpty && password.isNotEmpty) {
        // save our user to our auth and firebase firestore
        UserCredential cred = await firebaseAuth.createUserWithEmailAndPassword(
          email: email,
          password: password,
        );
        model.User user = model.User(
            name: username, email: email, uid: cred.user!.uid, cart: []);
        await firestore
            .collection('users')
            .doc(cred.user!.uid)
            .set(user.toJson());
      } else {
        Get.snackbar(
          'Error Creating Account',
          'Please enter all the fields',
        );
      }
    } catch (e) {
      Get.snackbar(
        'Error Creating Account',
        e.toString(),
      );
    }
  }

  void loginUser(String email, String password) async {
    try {
      if (email.isNotEmpty && password.isNotEmpty) {
        await firebaseAuth.signInWithEmailAndPassword(
            email: email, password: password);
        print('log success');
      } else {
        Get.snackbar(
          'Error Logging in',
          'Please enter all the fields',
        );
      }
    } catch (e) {
      Get.snackbar(
        'Error Logging in',
        e.toString(),
      );
    }
  }

  updateUserData(Map<String, dynamic> data) {
    print("UPDATED");
    firestore.collection('users').doc(_user.value?.uid).update(data);
  }

  Stream<model.User> listenToUser() => firestore
      .collection('users')
      .doc(_user.value?.uid)
      .snapshots()
      .map((snapshot) => model.User.fromSnap(snapshot));
}

User Model code:



class User {
  // static const UID = "uid";
  // static const NAME = "name";
  // static const EMAIL = "email";

  String uid;
  String name;
  String email;
  List<CartItemModel> cart;

  User(
      {required this.name,
      required this.email,
      required this.uid,
      required this.cart});

  Map<String, dynamic> toJson() =>
      {"name": name, "email": email, "uid": uid, "cart": cart};

  // static User fromSnap(DocumentSnapshot snap) {
  static User fromSnap(DocumentSnapshot snap) {
    var snapshot = snap.data() as Map<String, dynamic>;
    return User(
        name: snapshot['name'],
        email: snapshot['email'],
        uid: snapshot['uid'],
        cart: _convertCartItems(snapshot['cart'] ?? []));
  }

  // List<CartItemModel> _convertCartItems(List cartFromDb) {
  static List<CartItemModel> _convertCartItems(List cartFromDb) {
    List<CartItemModel> _result = [];
    // logger.i(cartFromDb.lengt);
    print(cartFromDb.length);
    cartFromDb.forEach((element) {
      _result.add(CartItemModel.fromMap(element));
    });
    return _result;
  }
}

Also I referred this github for shopping cart functionality but I have made some changes to make it null safe: cart functionality github


Solution

  • Use Rxn<T>() for nullable rx:

    final _user = Rxn<User>();
    

    Then on onInit():

    _user.bindStream(firebaseAuth.authStateChanges());