1

How do I null check or create a null safe block in Flutter?

Here is an example:

class Dog {
  final List<String>? breeds;
  Dog(this.breeds);
}

void handleDog(Dog dog) {
    printBreeds(dog.breeds); //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.
}

void printBreeds(List<String> breeds) {
  breeds.forEach((breed) {
    print(breed);
  });
}

If you try to surround it with an if case you get the same error:

void handleDog(Dog dog){
  if(dog.breeds != null) {
    printBreeds(dog.breeds); //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.
  }
}

If you create a new property and then null check it it works, but it becomes bothersome to create new properties each time you want to null check:

void handleDog(Dog dog) {
  final List<String>? breeds = dog.breeds;
  if (breeds != null) {
    printBreeds(breeds); // OK!
  }
}

Is there a better way to do this?
Like the ?.let{} syntax in kotlin?

Joel Broström
  • 3,530
  • 1
  • 34
  • 61

3 Answers3

2

To get something similar to Kotlins .let{} i created the following generic extension :

extension NullSafeBlock<T> on T? {
  void let(Function(T it) runnable) {
    final instance = this;
    if (instance != null) {
      runnable(instance);
    }
  }
}

And it can be used like this:

void handleDog(Dog dog) {
  dog.breeds?.let((it) => printBreeds(it));
}

"it" inside the let function will never be null at runtime.

Thanks to all the suggestions, but they were all some variation of moving the null check further down the code execution cain, which was not what i was looking for.

Joel Broström
  • 3,530
  • 1
  • 34
  • 61
  • Looks nice! But I'm not very sure if you should put an extenion on `T?` – iDecode Mar 31 '21 at 07:21
  • Why not? T is a generic class, so now every nullable class can invoke the `let` method. – Joel Broström Mar 31 '21 at 12:52
  • Right you can do that. But think it this way. If you have say `int a`, you can still do `a.let` which doesn't make any sense. You can use `let` on every single type, this is the over use of `extension`. What you should rather do is use `extension` on `List`. – iDecode Mar 31 '21 at 13:08
  • You're right in that it will work on all classes even if they are not nullable, but it is because it's more than just a null check tool. It actually creates a scooped block where the value of the invoked variable will be consistent throughout the existence of the block. And the point is to have it on all classes. If you worked with kotlin this is an exact copy of it's implementation, and if you worked with swift this is similar to the `if let xy = xyz {}` syntax. Writing this I do realize Dart does not have any scooped functions. Things like `.apply`, `.run`, `.with`, or `.also` does not exist – Joel Broström Apr 01 '21 at 07:46
1

Yes, you'll have create a local variable just like you did to handle those things because if you don't create a local variable then if there is a class which is extending the Dog class can override breeds which will then become nullable even after you had checked it in the first place.

The other solution you can try is changing the List<String> to nullable in printBreeds method.

void handleDog(Dog dog) {
  printBreeds(dog.breeds);
}

void printBreeds(List<String>? breeds) {
  breeds?.forEach((breed) {
    print(breed);
  });
}
iDecode
  • 22,623
  • 19
  • 99
  • 186
  • 1
    Your answer is the same as the others but I like it the best since you confirmed it is not possible to null check a member of a class without first creating a new instance of it. The reason I don't like moving the null check down the chain is that handling null checks early eliminates any cascading checks later on. And maybe more importantly a function called printBreeds(breeds) should, in my opinion, require a list of breeds to be printed. If a state does not have a list of breeds it simply should not call printBreeds(breeds). Would you agree? – Joel Broström Mar 30 '21 at 13:21
  • I was the one who posted the answer first haha. I agree with you but `print` is something which is needed for debugging and a `List?` actually means the items in the list can be `null` too. So, it all depends on your use case. – iDecode Mar 30 '21 at 13:48
  • Accurate, you were first. But inaccurate, List? means the list can be null, but the elements in the list is non-nullable. For more information you can scroll to "Lists and set types" in this article. https://medium.com/flutterdevs/null-safety-in-dart-be09568ea8b8 – Joel Broström Mar 30 '21 at 15:17
  • I posted an answer that closer answers my question, but thank you for your input and exchange of ideas. – Joel Broström Mar 30 '21 at 16:14
  • @JoelBroström Hi, I accidently made mistake in writing `?` in the `List?`. Actually, `List` can have nullable items but the list can't be `null` and `List?` can't have nullable items but the list can be `null` – iDecode Mar 31 '21 at 07:15
0

This error is right //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.

as null type list is passing to function which says it accepts a non-null list

By below way breeds can be accessible

void printBreeds(List<String>? breeds) {
  breeds?.forEach((breed) {
    print(breed);
  });
}

Also, if we don't want nullable operation every time, we can handle it while calling Example:

class Dog {
  final List<String>? breeds;
  Dog(this.breeds);
}

void handleDog(Dog dog) {
    print("handleDog");
    printBreeds(dog.breeds!);  
}
// This method only called if breeds not null
void printBreeds(List<String> breeds) {
  print("printBreeds");
  breeds.forEach((breed) {
    print(breed);
  });
}

void main() {
  var dog = Dog(null);
   handleDog(dog);
}

Output:

printBreeds

Jitesh Mohite
  • 31,138
  • 12
  • 157
  • 147
  • I agree with the error and that it's right, the question is how to go about solving it. The suggested method works, but pushing nullability down the chain seams like a counter productive solution. I think it would be better to handle the null checks as soon as possible to limit the potential states of variables early on. I also think a method called printBreeds(breeds) should only be invoked if you have a list of breeds to print. Would you agree? – Joel Broström Mar 30 '21 at 13:15
  • check my edited answer, it should work for you – Jitesh Mohite Mar 30 '21 at 13:43
  • @Joel Broström: does this make sense now? – Jitesh Mohite Mar 30 '21 at 15:02
  • Thank you for taking the time to help me! However, by "casting away nullability (!)" in `printBreeds(dog.breeds!);` we don't solve the problem. It will compile, but if you pass null to printBreeds the app will throw an exception instead. Exclamation point (`!`) does not mean do nothing if null, but simply promise the compiler the value won't be null at run time. I'm not sure if you were saying otherwise, just wanted to point it out. The line `// This method only called if breeds not null` could be interpreted in different ways. Anyways, thanks for your help! – Joel Broström Mar 30 '21 at 15:02
  • 1
    Thanks for your help. It got me thinking and i uploaded my own solution as an answer. – Joel Broström Mar 30 '21 at 16:16