1

I'm just working through this whole null-safety mode with my Flutter project and unsure what the difference is with ? and ! in calls to object methods.

For example, the hint was to add a ! conditional. Here's an example I have right now, and I'm unsure if this should be a ? or a ! at the findNbr!.replaceAll().

Future checkItem({String? findNbr}) async {
  int? x = int.tryParse(findNbr!.replaceAll('-', ''));

  ...

Does this mean replaceAll() will not run if findNbr is null?

Or should it be a ? instead? findNbr?.replaceAll() EDIT: I just noticed I cannot use findNbr?, it's telling String? can't be assigned parameter String.

Or does it mean I say it's not null and run it anyway?

For your information, I have not come close to running my app yet so I have no idea if it even works. But I figure I better know what it's doing before get too much more done. I'm still in the process of converting everything and there's 75-100 dart files. I'm not sure I get the point of it all to be honest, because I just add ? to everything, so its all nullable anyway.

2 Answers 2

1
Future checkItem({String? findNbr}) async {
  int? x = int.tryParse(findNbr!.replaceAll('-', ''));

  ...

Does this mean replaceAll() will not run if findNbr is null?

Correct. If findNbr is null, then findNbr! will throw a runtime exception. That would be bad, especially since checkItem's function signature advertises that findNbr is allowed to be null, and therefore it would violate callers' expectations.

Or should it be a ? instead? findNbr?.replaceAll() EDIT: I just noticed I cannot use findNbr?, it's telling String? can't be assigned parameter String.

You can't use findNbr?.replaceAll(...) because if findNbr is null, then it would be invoking int.tryParse(null), but int.tryParse is not allowed to take a null argument.

What you need to do is one of:

  • Make findNbr no longer optional:

    Future checkItem({required String findNbr}) async {
      int? x = int.tryParse(findNbr.replaceAll('-', ''));
      ...
    
  • Allow findNbr to be optional but have a non-null default value:

    Future checkItem({String findNbr = ''}) async {
      int? x = int.tryParse(findNbr.replaceAll('-', ''));
      ...
    
  • Allow findNbr to be optional but explicitly decide what to do if it is null. For example:

    Future checkItem({String? findNbr}) async {
      int? x = findNbr == null ? null : int.tryParse(findNbr.replaceAll('-', ''));
      ...
    

I'm not sure I get the point of it all to be honest, because I just add ? to everything, so its all nullable anyway.

If you blindly add ? to all types and add ! to all variables, then yes, null-safety would be pointless: doing that would give you the same behavior as Dart before null-safety.

The point of null-safety is to prevent things that shouldn't be null from ever being null. You could have written such code before, but without null-safety, that meant performing runtime null checks (e.g. assert(x != null);, if (x != null) { ... }, or relying on a null-pointer-exception to crash the program if null was used where it wasn't expected). Null-safety means that such checks now can be done at build-time by static analysis, which means that errors can be caught earlier and more completely. Furthermore, whereas previously functions needed to explicitly document whether arguments and return values were allowed to be null (and inadequate or incorrect documentation could be a source of errors), now they're self-documenting in that regard. It's just like using int foo(String s) versus dynamic foo(dynamic s); using strong types catches errors earlier and better describes the function's contract.

I recommend reading Understanding Null Safety if you haven't already done so.

Sign up to request clarification or add additional context in comments.

2 Comments

Awesome answer, and a solution! After I wrote this question I figured I'd have to do some kind of findNbr != null check. It's just the combined variable with a function "variable!.function()" thing that confused me and I didn't see that specific variation in docs. And yes, I'm not blindly adding ? operators. I do take each one and assess, tweak it and remove a null check, which is nice. But there's a huge amount of functions with optional arguments and cases where variables can indeed be null, like database records. I appreciate you pointing it out, along with the null-safety info. Thx!
@gregthegeek I forgot about a third way to deal with it: by adding a non-null default value. I've updated my answer.
0

I would like to advice you to use the ! operator, also the called bang operator, as little as possible. You should only use this operator when the dart analyser is wrong and you know for 100% that the value will never be null.

Below is an example of where the dart analyser would be wrong and you should use the bang operator.

// We have a class dog with a nullable name.
class Dog {
  String? name;
  
  Dog({this.name});
}

void main() {
  // We create a dog without a name.
  final dog = Dog();
  
  // We assign the dog a name.
  dog.name = 'George';
  
  // The dart analyser will show an error because it can't know if the
  // name of the object is not null.
  //
  // Will throw: `A value of type 'String?' can't be assigned to a
  // variable of type 'String'`.
  String myDogsName = dog.name;

  // To avoid this, you should use the bang operator because you `know` it
  // is not null.
  String myDogsName = dog.name!;
}

The ? operator simply tells Dart that the value can be null. So every time you want to place a ? operator, ask yourself, can this value ever be null?

The null safety features in Dart are mainly created for helping the developer remember when a value can be null. Dart will now simply tell you when you made a variable nullable in order to force null checks or default values for example.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.