How to fix "The operator ‘{0}’ can’t be unconditionally invoked because the receiver can be ‘null’"

Issue

While trying Dart‘s Sound Null Safety i came up with a problem:


Some Context

Creating a new Flutter project i found the following (and very familiar) piece code

int _counter = 0;

void _incrementCounter() {
  setState(() {
    // This call to setState tells the Flutter framework that something has
    // changed in this State, which causes it to rerun the build method below
    // so that the display can reflect the updated values. If we changed
    // _counter without calling setState(), then the build method would not be
    // called again, and so nothing would appear to happen.
    _counter++;
  });
}

Now, i changed the variable _counter to be nullable and took off the initialization:

int? _counter;

void _incrementCounter() {
  setState(() {
    _counter++;
  });
}

And as expected, i got the following error in the editor:

The operator ‘+’ can’t be unconditionally invoked because the receiver can be ‘null’

The Problem

Following the documentation i added the required checking:

if (_counter!=null)
        _counter++;

but to my astonishment, the error kept on showing and suggesting

Try making the call conditional (using ‘?’ or adding a null check to the target (‘!’))

even though i explicitly am making the call conditionally… so what is wrong?

Solution

UPDATE

As suggested in another SO thread, in a further thread in Github Erik Ernst says:

Type promotion is only applicable to local variables… Promotion of an instance variable is not sound, because it could be overridden by a getter that runs a computation and returns a different object each time it is invoked. Cf. dart-lang/language#1188 for discussions about a mechanism which is similar to type promotion but based on dynamic checks, with some links to related discussions.

So, with that explanation, now i see that only local variables can (so far?) be promoted, and thus my problem could be fixed by writing

int? _counter;

void _incrementCounter() {
  setState(() {
    if (_counter!=null)
        _counter = _counter! + 1;
  });
}

For an alternative fix, see my original answer below.


The other Fix

I finally fixed the problem by capturing the value of the instance variable inside the method as follows:

int? _counter;

void _incrementCounter() {
  setState(() {
    var c = _counter;

    if (c!=null)
      c++;

    _counter = c;
      
  });
}

Why the need to capture the variable?

Well, the whole problem is that

Type promotion is only applicable to local variables… Promotion of an instance variable is not sound, because it could be overridden by a getter that runs a computation and returns a different object each time it is invoked

so in my approach we

  • capture the value of the instance variable
  • then we check for that value to not be null.
  • if that value turned out not to be null then we operate on the local variable which through the null check is now correctly promoted.
  • finally we apply the new value to the instance variable.

Final observations

I am new to Dart and thus i am not 100% sure, but to me it seems that in general, when working with nullable instace variables, my approach is better that that of using the bang operator to cast away nullity:

By casting away nullity you are overlooking the main problem of promoting instance variables, namely that

it could be overridden by a getter that runs a computation and returns a different object each time it is invoked …

That exact problem is what is avoided by capturing the value of the instance variable and working with that locally captured value…

If i am wrong kindly let me know…

Answered By – deczaloth

Answer Checked By – Dawn Plyler (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.