Role Base Authorization in Flutter Web Using Firestore and GetX State Management (MVC Pattern)

909 views Asked by At

I'm just going to develop an enterprise-level Flutter Web solution. So I've decided to follow a proper state management package instead of SetState. This is my first Flutter web application and the first time I tried to use GetX.

I'm going to implement Rolebase Authorization using Firestore. Once the user has logged into the system he will be redirected to the admin page if he is an administrator, otherwise redirected to the user page. Login function working fine. But I'm unable to get the role from Firestore. I have explained all this because I want to know the solution to the following error and to know if I am doing this state management thing properly.

The error clearly saying that the "Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist". But how can I identify the exact reason? Someone can help me understand where I have made a mistake.

Error :

[GETX] "AuthController" has been initialized
[GETX] "UserController" has been initialized
[GETX] "GetMaterialController" has been initialized
[GETX] GOING TO ROUTE /login-page
LoginSuccess
Error: Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist
    at Object.throw_ [as throw] (http://localhost:5000/dart_sdk.js:4328:11)
    at platform_interface_document_snapshot.DocumentSnapshotPlatform.new.get (http://localhost:5000/packages/cloud_firestore_platform_interface/src/platform_interface/platform_interface_write_batch.dart.lib.js:652:19)
    at cloud_firestore.DocumentSnapshot.__.get (http://localhost:5000/packages/cloud_firestore/cloud_firestore.dart.lib.js:697:73)
    at cloud_firestore.DocumentSnapshot.__._get (http://localhost:5000/packages/cloud_firestore/cloud_firestore.dart.lib.js:700:19)
    at new user_model.UserModel.fromDocumentSnapshot (http://localhost:5000/packages/equipment_management_system/models/user_model.dart.lib.js:47:32)
    at database.Database.new.getUser (http://localhost:5000/packages/equipment_management_system/views/pages/login_page.dart.lib.js:3180:16)
    at getUser.next (<anonymous>)
    at http://localhost:5000/dart_sdk.js:37593:33
    at _RootZone.runUnary (http://localhost:5000/dart_sdk.js:37447:58)
    at _FutureListener.thenAwait.handleValue (http://localhost:5000/dart_sdk.js:32424:29)
    at handleValueCallback (http://localhost:5000/dart_sdk.js:32971:49)
    at Function._propagateToListeners (http://localhost:5000/dart_sdk.js:33009:17)
    at _Future.new.[_completeWithValue] (http://localhost:5000/dart_sdk.js:32852:23)
    at async._AsyncCallbackEntry.new.callback (http://localhost:5000/dart_sdk.js:32874:35)
    at Object._microtaskLoop (http://localhost:5000/dart_sdk.js:37708:13)
    at _startMicrotaskLoop (http://localhost:5000/dart_sdk.js:37714:13)
    at http://localhost:5000/dart_sdk.js:33226:9

Auth Controller :

  handleAuthChanged(isLoggedIn) async {
    final UserController userController = Get.put<UserController>(UserController());
    if (isLoggedIn == false) {
      Get.offAllNamed(LoginPageRoute);
    } else {
      print("LoginSuccess");
      userController.user = await Database().getUser(user.value.uid).then((value){
        print(userController.user.id);
        if(userController.user.role == "Admin"){
          Get.offAllNamed(AdminHomePageRoute);
        }else{
          Get.offAllNamed(UserHomePageRoute);
        }
        return;
      });
    }
  }

User Model

class UserModel{
  String id;
  String name;
  String role;

  UserModel({id, name, email, role});

  UserModel.fromDocumentSnapshot(DocumentSnapshot doc){
    id = doc["id"];
    name = doc["name"];
    name = doc["role"];
  }

}

User Controller

class UserController extends GetxController{
  Rx<UserModel> _userModel = UserModel().obs;
  UserModel get user => _userModel.value;
  set user(UserModel value) => this._userModel.value = value;
  void clear(){
    _userModel.value = UserModel();
  }
}

Database Service

class Database{
  final FirebaseFirestore _firebaseFirestore = FirebaseFirestore.instance;

  Future<bool> createNewUser(UserModel user) async {
    await _firebaseFirestore.collection("users").doc(user.id).set({
      "name" : user.name,
      "role" : user.role,
    }).catchError((onError){
      Get.snackbar(
        "", "",
        titleText: Text("An error occurred while connecting to the database", style: TextStyle(fontFamily: "Poppins", fontWeight: FontWeight.bold, fontSize: 16)),
        messageText: Text(onError.message, style: TextStyle(fontFamily: "Poppins")),
        colorText: primaryColor,
        backgroundColor: Colors.white.withOpacity(0.5),
        margin: EdgeInsets.only(top: 20, left: 20, right: 20),
        barBlur: 10,
      );
      return false;
    });
    return true;
  }

  Future<UserModel> getUser(String uid) async{
    DocumentSnapshot doc = await _firebaseFirestore.collection("users").doc(uid).get().catchError((onError){
      Get.snackbar(
        "", "",
        titleText: Text("An error occurred while retrieving data", style: TextStyle(fontFamily: "Poppins", fontWeight: FontWeight.bold, fontSize: 16)),
        messageText: Text(onError.message, style: TextStyle(fontFamily: "Poppins")),
        colorText: primaryColor,
        backgroundColor: Colors.white.withOpacity(0.5),
        margin: EdgeInsets.only(top: 20, left: 20, right: 20),
        barBlur: 10,
      );
    });
      return UserModel.fromDocumentSnapshot(doc);
  }
}

Firestore Collection

enter image description here

2

There are 2 answers

0
Jeremiah Flickinger On

I had the same error recently. I recently upgraded several Firebase packages (cloud_firestore: ^0.14.1+3, firebase_auth: ^0.18.1+2, firebase_dynamic_links: ^0.6.0+2, firebase_storage: ^4.0.1). After, cleaning up all of the deprecated methods, etc. I began getting the same "cannot get a field on a DocumentSnapshotPlatform which does not exist".

I eventually figured out that if I changed all references from doc["fooBar"] to doc.data()["fooBar"] that it resolved the issue. Try changing your code to the below:

class UserModel{
    String id;
    String name;
    String role;

    UserModel({id, name, email, role});

    UserModel.fromDocumentSnapshot(DocumentSnapshot doc){
        id = doc.data()["id"] // previously doc["id"];
        name = doc.data()["name"] // previously doc["name"];
        name = doc.data()["role"] // previously doc["role"];
    }
}

After further reading (https://pub.dev/packages/cloud_firestore/changelog) , I found the following note regarding DocumentSnapshots from version 0.14.0^

"DocumentSnapshot:

BREAKING: The get data getter is now a data() method instead. ... "

0
Jay gajjar On
class UserModel {
  String id;
  String name;
  String role;

  UserModel({id, name, email, role});

  UserModel.fromDocumentSnapshot(DocumentSnapshot doc) {
    id = doc.data().containsKey("id") ? doc.data()["id"] : '';
    name = doc.data().containsKey("name") ? doc.data()["name"] : '';
    role = doc.data().containsKey("role") ? doc.data()["role"] : '';
  }
}