Home > front end >  How can I make null-safety assertions to avoid using null check (!) or conditional (?) operators in
How can I make null-safety assertions to avoid using null check (!) or conditional (?) operators in

Time:04-23

Dart compiler does not understand that the variable can not be null when I use it inside an if (x != null) statement. It still requires to use conditional ? or null check ! operators to access one of the variable's fields or methods.

Here is an example:

String? string;

void test() {
  if (string != null) {
    print(string.length);
  }
}

This produces a compile-time error and says that

The property 'length' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!').

However, the receiver actually can't be null since it's wrapped with if (string != null) block. Accessing the field with string?.length or string!.length works fine but it can be confusing when I need to use different fields or methods of the variable.

String? string;

void test() {
  if (string != null) {
    print(string.length);
    print(string.isNotEmpty);
    print(string.trim());
    print(string.contains('x'));
  }
}

All of these statements raise the same error. I also tried putting assert(string != null); but the compiler still does not understand that the string is not null.

To give a more sophisticated example;

User? user;

@override
Widget build(BuildContext context) {
  if (user == null) {
    return Text('No user available');
  } else {
    return buildUserDetails();
  }
}

Widget buildUserDetails() {
  return Column(
    children: [
      Text(user.name),
      Text(user.email),
    ],
  );
}

This is still a problem for the compiler.

CodePudding user response:

However, the receiver actually can't be null since it's wrapped

And that assumption is plain wrong.

Your variable is a global variable and any other part of your program, through multi-threading or other shenanigans can slip in between your if and the next line and change the variable.

That is why only local variables can be promoted to their non-null equivalent when the compiler proves that they cannot be null in certain code execution branches like an if.

The following will work perfectly fine, because you are operating on a local variable that the compiler can be sure won't be changed by outside operations:

String? string;

void test() {
  final local = string;
  if (local != null) {

    // here, local was promoted from "string?" to "string"
    // since the "if" makes sure it is not null AND
    // the variable is not accessible to anything but this
    // function, so it cannot be changed from the outside
    // and is contrained to the sequential flow of this method.

    print(local.length);       
  }
}

CodePudding user response:

These are for sound null safety. Thats why whenever you start calling a functions / accessing it makes sure that the variable is not null by using ! or provide other case for null using ?.

Suppose for following case :

    if (string != null) {
        string=null; // Or through other function xyx() {string=null;} string becomes null then your if condition is void
        print(string.length);
        print(string.isNotEmpty);
        print(string.trim());
        print(string.contains('x'));
      }

// Still sound null safety that's why above is not allowed
    if (string != null) {
        string=null; // Or through other function xyx() {string=null;} string becomes null then your if condition is void
        print(string!.length);
        print(string!.isNotEmpty);
        print(string!.trim());
        print(string!.contains('x'));
      }

So for sound null safety it is required to be checked if string is not null before accessing it.

As per your comments you need to assign this nullable string to a non nullable string (~isdatablank~ means String was null) and proceed

String? string;
  String s=string??"~isdatablank~";
  if (s != "~isdatablank~") {
        print(s.length);
        print(s.isNotEmpty);
        print(s.trim());
        print(s.contains('x'));
      }
  • Related