I have a class that I am creating that looks like this:

class Movie {
  final String title, posterPath, overview;

  Movie(this.title, this.posterPath, this.overview);

  Movie.fromJson(Map json) {
    title = json["title"];
    posterPath = json["poster_path"];
    overview = json['overview';
  }
}

I am getting a warning that says that "The final variables 'overview', 'posterPath', & '1' more must be initialized. There are also warnings around each variable saying 'title' can't be used as a setter because it is final.

When I write the constructor using this syntax, the warnings go away:

Movie.fromJson(Map json)
   : title = json["title"],
     posterPath = json["poster_path"],
     overview = json['overview'];

What exactly is going on here?


Solution 1: Nader Dabit

I just found some documentation around this, & it seams that the second version with the : is what's called the "initializer list" which allows you to initialize instance variables before the constructor body runs.

There is more detail around this in the documentation here.


Solution 2: Rémi Rousselet

Dart separates properties initialization from the constructor body.

A constructor has 3 parts :

  • the name/parameters definition
  • properties initialization/super call/asserts
  • A body, similar to a function immediately run on construction

Both the initialization and body parts are optional. final variables must be initialized on the first 2 parts. They cannot be initialized inside the body.

A full constructor will look like the following :

MyClass(int value)
    : assert(value > 0),
      property = value, 
      super();
{
  print("Hello World");
} 

The main purpose of this initializer part is for body-less constructors which allows const constructors, a dart specific feature. See How does the const constructor actually work? for more details on these.


Solution 3: lrn

Dart objects must be fully initialized before anyone gets a reference to the new object. Since the body of a constructor can access this, the object needs to be initialized before entering the constructor body.

To do that, generative Dart constructors have an initializer list, looking similiar to C++, where you can initialize fields, including final fields, but you cannot access the object itself yet. The syntax:

Movie.fromJson(Map json)
    : title = json["title"],
      posterPath = json["poster_path"],
      overview = json['overview'];

uses an initializer list (the list of assignments after the :) to initialize the final instance variables title, posterPath and overview.

The first constructor uses an "initializing formal" this.title to directly put the parameter into the field.

The constructor

Movie(this.title, this.posterPath, this.overview);

is effectively a shorthand for:

Movie(String title, String posterPath, String overview)
    : this.title = title, this.posterPath = posterPath, this.overview = overview;

Your constructor can combine all of these and a body:

Movie(this.title, this.posterPath, String overview)
   : this.overview = overview ?? "Default Overview!" {
  if (title == null) throw ArgumentError.notNull("title");
}

(A const constructor cannot have a body, but it can have an initializer list with some restrictions on the allowed expressions to ensure that they can be evaluated at compile-time).