Search code examples
flutterdartdart-null-safety

Flutter null safety with fromJson


Recently migrating to Flutter null safety feature, I have a lot of Classes that I need to update.

For my models, I use fromJson to deserialize the data from a json object. This forces me to put the late keyword for each field that is non optional.

Is that the right approach?

class ServerSession {
  late String sessionId;
  late String refreshToken;
  late String accessToken;

  ServerSession({required this.sessionId, required this.refreshToken, required this.accessToken});

  ServerSession.fromJson(Map<String, dynamic> json) {
    sessionId = json['session_id'] ?? json['sessionId'];
    refreshToken = json['refresh_token'] ?? json['refreshToken'];
    accessToken = json['access_token'] ?? json['accessToken'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['sessionId'] = this.sessionId;
    data['refreshToken'] = this.refreshToken;
    data['accessToken'] = this.accessToken;
    return data;
  }
}

Solution

  • No, it is not. You should be using the initializer list for initializing fields of your class. You can read more about the initializer list in the language tour.

    class ServerSession {
      String sessionId;
      String refreshToken;
      String accessToken;
    
      ServerSession({required this.sessionId, required this.refreshToken, required this.accessToken});
    
      ServerSession.fromJson(Map<String, dynamic> json) :
        sessionId = json['session_id'] ?? json['sessionId'],
        refreshToken = json['refresh_token'] ?? json['refreshToken'],
        accessToken = json['access_token'] ?? json['accessToken'];
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['sessionId'] = this.sessionId;
        data['refreshToken'] = this.refreshToken;
        data['accessToken'] = this.accessToken;
        return data;
      }
    }
    

    I personally would use the "normal" constructor for the object and make fromJson a factory constructor, though either method works.

    class ServerSession {
      String sessionId;
      String refreshToken;
      String accessToken;
    
      ServerSession({required this.sessionId, required this.refreshToken, required this.accessToken});
    
      factory ServerSession.fromJson(Map<String, dynamic> json) {
        return ServerSession(
          sessionId: json['session_id'] ?? json['sessionId'],
          refreshToken: json['refresh_token'] ?? json['refreshToken'],
          accessToken: json['access_token'] ?? json['accessToken']
        );
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['sessionId'] = this.sessionId;
        data['refreshToken'] = this.refreshToken;
        data['accessToken'] = this.accessToken;
        return data;
      }
    }