3

I am trying to double-check if the User object is successfully created, but Null saftey says the operand cannot be null, so the condition is always true

What if in a scenario where the json data contains invalid type, in this case there might be some errors when creating the user object

class User {
  String? name;
  String? age;

  User({name, age}) {
    this.name = name;
    this.age = age;
  }

  factory User.fromJson(dynamic json) {
    return User(name: json['name'], age: json['age']);
  }
}

void main() {
  String data = '{name: "mike",age: "2"}';

  User user = User.fromJson(data);

  if (user != null) { // Warning: "The operand can't be null, so the condition is always true. Remove the condition."

  }
}

Please advise, Thank you! :)

Manas
  • 3,060
  • 4
  • 27
  • 55

1 Answers1

3

If something wrong is going on creating your User object from a JSON input, it will, in your case, throw an Exception which will crash the program if not catch.

So the variable user cannot be null in your case which is what the warning is telling you.

If you want to have some kind of User.tryFromJson which returns null in case of any problems, you could add something like this to you User class:

  static User? tryFromJson(dynamic json) {
    try {
      return User.fromJson(json);
    } catch (_) {
      return null;
    }
  }

Also, some minor comments. Your User constructor does not make much sense since you could have written the following instead:

User({this.name, this.age});

Also, I would make both arguments required and prevent the nullable types. So something like this (also changed age to int):

class User {
  String name;
  int age;

  User({
    required this.name,
    required this.age,
  });

  factory User.fromJson(dynamic json) => User(
        name: json['name'] as String,
        age: json['age'] as int,
      );

  static User? tryFromJson(dynamic json) {
    try {
      return User.fromJson(json);
    } catch (_) {
      return null;
    }
  }
}

void main() {
  final data = '{name: "mike",age: 2}';
  final user = User.fromJson(data);
}
julemand101
  • 28,470
  • 5
  • 52
  • 48
  • I see! Thank you! in that case, would it be better to execute that in a try and catch? – Manas Jul 21 '21 at 08:45
  • @Mikethetechy Yes, if you are able to handle the situation where the JSON has an invalid format, then use try...catch. – julemand101 Jul 21 '21 at 08:48
  • 1
    perfect! I prefer your suggested approach `tryFromJson` and try & catch is cleaner :) and thank you so much for your improvement tips as well – Manas Jul 21 '21 at 08:54
  • I have one question, if we create user object as per above, it will not be using the factory constructor to create singleton user object but instead create a new instance every time? – Manas Jul 21 '21 at 09:10
  • @Mikethetechy `factory` constructors is just defined as it is not creating an object by itself but it must return an object compatible with the type of the class it is part of. In my example, the `factory` constructor will always create a new object by calling the `User` constructor. So no singleton pattern here. It is correct that `factory` constructors can be used for making singletons but that is only if we are explicit returning the same object instance when calling the `factory` constructor. – julemand101 Jul 21 '21 at 09:44
  • Thank you! so in the above scenario, there is no caching going on automatically by dart right? if we want to cache, we will have to manually set up a static method to return the cached object and handle it? (Was in the impression that defining factory automatically stores the object in cache by dart) so the factory named constructor in the above example is basically just used for object creation which allows us to do some conditional checks while initiating an object, is my understanding correct? – Manas Jul 23 '21 at 07:15
  • @Mikethetechy I think you got confused with `factory` constructor and `static` (and global) fields. `static` fields are, in Dart, lazy evaluated and will first get a value the first time the variable is requested. We can use `factory` constructors to make some cache behavior. But we can also use it to have some logic required to fetch e.g. values we are then calling our normal constructor with. – julemand101 Jul 23 '21 at 07:37
  • 1
    The reason for that pattern is because a `final` value in a `class` must be initialized with a value when initializing the object. So if we have more complicated logic, we normally would want to put that into the body of the constructor. That can be a problem in Dart since the body of the constructor is executed after the object has been initialized so we cannot set `final` fields inside the constructor body. But we can sometimes move this logic into a `factory` constructor and execute that logic before even creating the object and send the result in as parameters to the normal constructor. – julemand101 Jul 23 '21 at 07:39
  • Thank you!! having a much clearer picture now. – Manas Jul 23 '21 at 08:26