-1

I have this code:

RaisedButton(
    child: Text('Add'),
    onPressed: () async {
        if (_formKey.currentState.validate()) {
            DocumentReference docRef =
                await DatabaseServices().addUserProperty(
                    user.userUid,
                    _currentPropertyName,
                    _currentPropertyNotes,
                    _currentPropertyZone,
                    _currentPropertyAddress `enter code here`,
                    _currentPropertyLandArea,
                    _currentPropertyDatePurchased,
                    _currentPropertyRatesBillingCode,
                    _currentPropertyInsurancePolicy,
                    _currentPropertyInsuranceSource,
                    _currentPropertyInsuranceExpiryDate `enter code here`,
                    _currentPropertyLegalDescription,
                    _currentPropertyValuation,
                    _currentPropertyValuationSource,
                    false,
                    Timestamp.now(),
                    Timestamp.now(),
                );
            await DatabaseServices().addPropertyUnit(
                user.userUid,
                docRef.documentID,
                'Single unit',
                '',
                '',
                0,
                false,
                false,
                Timestamp.now(),
                Timestamp.now(),
            );
            Navigator.pop(context);
        }
    })

where I am trying to use 'docRef.documentID' from the just generated documentId for addUserProperty. I want to save this as a field in the addPropertyUnit. I get the error 'The getter 'documentID' was called on null' on the line docRef.documentID. What should I do?

Thanks for the suggestion ralemos. I did place the second await etc inside a .then(). I now have this code:

RaisedButton(
                child: Text('Add'),
                onPressed: () async {
                  if (_formKey.currentState.validate()) {
                    DocumentReference docRef = await DatabaseServices()
                        .addUserProperty(
                      user.userUid,
                      _currentPropertyName,
                      _currentPropertyNotes,
                      _currentPropertyZone,
                      _currentPropertyAddress,
                      _currentPropertyLandArea,
                      _currentPropertyDatePurchased,
                      _currentPropertyRatesBillingCode,
                      _currentPropertyInsurancePolicy,
                      _currentPropertyInsuranceSource,
                      _currentPropertyInsuranceExpiryDate,
                      _currentPropertyLegalDescription,
                      _currentPropertyValuation,
                      _currentPropertyValuationSource,
                      false,
                      Timestamp.now(),
                      Timestamp.now(),
                    )
                        .then((docRef) async {
                      await DatabaseServices().addPropertyUnit(
                        user.userUid,
                        'nnnnnnnnnn',
//                        docRef.documentID,
                        'Single unit',
                        '',
                        '',
                        0,
                        false,
                        false,
                        Timestamp.now(),
                        Timestamp.now(),
                      );
                      return null;
                    });
//                    print('docRef: ${docRef.documentID}');

                    Navigator.pop(context);
                  }
                })  

which works in that it saves a new PropertyUnit but how do I pass the docRef.documentID from .addUserProperty to .addPropertyUnit()? Currently it is just saving 'nnnnnnnnnnn' instead. If I define 'DocumentReference docRef;' just below the 'Widget build(BuildContext context) {', docRef.documentID is available in .addPropertyUnit but still get a runtime error of ' The getter 'documentID' was called on null'.

The code for addPropertyUnit is:

// add a unit to a property
  Future addPropertyUnit(
    String userUid,
    String unitPropertyUid,
    String unitName,
    String unitNotes,
    String unitLeaseDescription,
    num unitArea,
    bool unitResidential,
    bool unitArchived,
    Timestamp unitRecordCreatedDateTime,
    Timestamp unitRecordLastEdited,
  ) async {
    return await userUnitCollection.document().setData(
      {
        'userUid': userUid,
        'propertyUid': unitPropertyUid,
        'unitName': unitName,
        'unitNotes': unitNotes,
        'unitLeaseDescription': unitLeaseDescription,
        'unitArea': unitArea,
        'unitResidential': unitResidential,
        'unitArchived': unitArchived,
        'unitRecordCreatedDateTime': unitRecordCreatedDateTime,
        'unitRecordLastEdited': unitRecordLastEdited,
      },
    );
  }

And addUserProperty:

 // add a property
  Future addUserProperty(
    String userUid,
    String propertyName,
    String propertyNotes,
    String propertyZone,
    String propertyAddress,
    double propertyLandArea,
    DateTime propertyDatePurchased,
    String propertyRatesBillingCode,
    String propertyInsurancePolicy,
    String propertyInsuranceSource,
    DateTime propertyInsuranceExpiryDate,
    String propertyLegalDescription,
    double propertyValuation,
    String propertyValuationSource,
    bool propertyArchived,
    Timestamp propertyRecordCreatedDateTime,
    Timestamp propertyRecordLastEdited,
  ) async {
    return await userPropertyCollection.document().setData(
      {
        'userUid': userUid,
        'propertyName': propertyName,
        'propertyNotes': propertyNotes,
        'propertyZone': propertyZone,
        'propertyAddress': propertyAddress,
        'propertyLandArea': propertyLandArea,
        'propertyDatePurchased': propertyDatePurchased,
        'propertyRatesBillingCode': propertyRatesBillingCode,
        'propertyInsurancePolicy': propertyInsurancePolicy,
        'propertyInsuranceSource': propertyInsuranceSource,
        'propertyInsuranceDate': propertyInsuranceExpiryDate,
        'propertyLegalDescription': propertyLegalDescription,
        'propertyMarketValuation': propertyValuation,
        'propertyMarketValuationSource': propertyValuationSource,
        'propertyArchived': propertyArchived,
        'propertyRecordCreatedDateTime': propertyRecordCreatedDateTime,
        'propertyRecordLastEdited': propertyRecordLastEdited,
      },
    );
  }
Sctajc
  • 947
  • 1
  • 8
  • 18
  • I believe that the problem is that you are using the `docRef.documentID` in a syncronous way, so you are entering `.addPropertyUnit()` before the `docRef.documentID` has been populated, try adding everything that is after `.addUserProperty()` to `.addUserProperty().then()`, this way your subsequent tasks will wait for the value to be populated. Do let me know if this works. – Ralemos Mar 26 '20 at 10:16

3 Answers3

1

Use the code below. I believe it can work.

RaisedButton(
            child: Text('Add'),
            onPressed: () async {
              if (_formKey.currentState.validate()) {
                DocumentReference docRef = await DatabaseServices()
                    .addUserProperty(
                  user.userUid,
                  _currentPropertyName,
                  _currentPropertyNotes,
                  _currentPropertyZone,
                  _currentPropertyAddress,
                  _currentPropertyLandArea,
                  _currentPropertyDatePurchased,
                  _currentPropertyRatesBillingCode,
                  _currentPropertyInsurancePolicy,
                  _currentPropertyInsuranceSource,
                  _currentPropertyInsuranceExpiryDate,
                  _currentPropertyLegalDescription,
                  _currentPropertyValuation,
                  _currentPropertyValuationSource,
                  false,
                  Timestamp.now(),
                  Timestamp.now(),
                );
                  await DatabaseServices().addPropertyUnit(
                    user.userUid,
                    docRef.documentID,
                    'Single unit',
                    '',
                    '',
                    0,
                    false,
                    false,
                    Timestamp.now(),
                    Timestamp.now());
               print('docRef: ${docRef.documentID}');

                Navigator.pop(context);
              }
            })  

The difference between our code is that this code is waiting for addUserProperty() to finish first to get the docRef object and then when docRef is not null anymore, It runs addPropertyUnit() function so in this way, docRef will not be null.

Updated answer:

Use the code below for you addUserProperty:

    // add a property
  Future< DocumentReference> addUserProperty( //the return type has changed to DocumentReference
    String userUid,
    String propertyName,
    String propertyNotes,
    String propertyZone,
    String propertyAddress,
    double propertyLandArea,
    DateTime propertyDatePurchased,
    String propertyRatesBillingCode,
    String propertyInsurancePolicy,
    String propertyInsuranceSource,
    DateTime propertyInsuranceExpiryDate,
    String propertyLegalDescription,
    double propertyValuation,
    String propertyValuationSource,
    bool propertyArchived,
    Timestamp propertyRecordCreatedDateTime,
    Timestamp propertyRecordLastEdited,
  ) async {
    DocumentReference document = userPropertyCollection.document(); //new document is created here

     await document.setData( // document data is set here.
      {
        'userUid': userUid,
        'propertyName': propertyName,
        'propertyNotes': propertyNotes,
        'propertyZone': propertyZone,
        'propertyAddress': propertyAddress,
        'propertyLandArea': propertyLandArea,
        'propertyDatePurchased': propertyDatePurchased,
        'propertyRatesBillingCode': propertyRatesBillingCode,
        'propertyInsurancePolicy': propertyInsurancePolicy,
        'propertyInsuranceSource': propertyInsuranceSource,
        'propertyInsuranceDate': propertyInsuranceExpiryDate,
        'propertyLegalDescription': propertyLegalDescription,
        'propertyMarketValuation': propertyValuation,
        'propertyMarketValuationSource': propertyValuationSource,
        'propertyArchived': propertyArchived,
        'propertyRecordCreatedDateTime': propertyRecordCreatedDateTime,
        'propertyRecordLastEdited': propertyRecordLastEdited,
      },
    );
return document; // returns the document after setting the data finishes so in this way, your docRef must not be null anymore.
  }
Morez
  • 2,085
  • 2
  • 10
  • 33
  • Sorry Morez (and Muhammad below). I still get ' The getter 'documentID' was called on null!!! I do question if it could be something to do with my .addPropertyUnit(). I have added it to my original query – Sctajc Mar 29 '20 at 20:25
  • How does your addUserProperty() look like? Does it return anything? Can you please add it to your query? You are getting the error because your docRef is null and I think it's because of your addUserProperty() method as it might not return any value – Morez Mar 29 '20 at 20:54
  • I have added addUserProperty. It does a return after .setData. – Sctajc Mar 29 '20 at 21:04
  • setData method returns nothing. You need to create the document first and then set its data and then return the created document. Please accept my answer if it helps. Thanks :) – Morez Mar 29 '20 at 21:09
  • Thanks Morez. Changed 'userPropertyCollection.document().setData(' to 'return await userPropertyCollection.add'. .setData does not just do the same as .add if record does not exist. – Sctajc Mar 29 '20 at 21:26
  • You can do that but I think the code I wrote is easier to read and understand but it's the developer choice. Happy to help:) – Morez Mar 29 '20 at 21:27
0

Try this! Will surely do the job for you.

RaisedButton(
   child: Text('Add'),
   onPressed: () async {
     if (_formKey.currentState.validate()) {
         DocumentReference docRef = await DatabaseServices().addUserProperty(
            user.userUid,
            _currentPropertyName,
            _currentPropertyNotes,
            _currentPropertyZone,
            _currentPropertyAddress,
            _currentPropertyLandArea,
            _currentPropertyDatePurchased,
            _currentPropertyRatesBillingCode,
            _currentPropertyInsurancePolicy,
            _currentPropertyInsuranceSource,
            _currentPropertyInsuranceExpiryDate,
            _currentPropertyLegalDescription,
            _currentPropertyValuation,
            _currentPropertyValuationSource,
            false,
            Timestamp.now(),
            Timestamp.now(),
         ).then((docRef) async {

           print('addUserProperty Done');
           // docRef.documentID available here
           print('docRef: ${docRef.documentID}'); 

           await DatabaseServices().addPropertyUnit(
              user.userUid,
              docRef.documentID,
              'Single unit',
              '',
              '',
              0,
              false,
              false,
              Timestamp.now(),
              Timestamp.now(),
           ).then(() async {
             print('addPropertyUnit Done'); 
           });
         });

       Navigator.pop(context);
     }
})  
Muhammad Noman
  • 1,344
  • 1
  • 14
  • 28
0

Perhaps consider getting the unique document id, that will be used by Firestore to post the data, BEFORE posting the data. You would then have an id that you can use for both addUserProperty and addPropertyUnit.

Example code to generate this unique id is:

DocumentReference ref = db.collection("my_collection").doc();
String myId = ref.id;

I use this technique in Firebase RTDB and it works fine but I am not that familiar with Firestore so can't post you updated code.

Please see this SO post for more information: Firestore - Is It Possible To Get The ID Before It Was Added?

GrahamD
  • 2,952
  • 3
  • 15
  • 26