https://dart.dev/guides/language/effective-dart/design#avoid-public-late-final-fields-without-initializers

AVOID public late final fields without initializers.

Unlike other final fields, a late final field without an initializer does define a setter. If that field is public, then the setter is public. This is rarely what you want. Fields are usually marked late so that they can be initialized internally at some point in the instance’s lifetime, often inside the constructor body.

Unless you do want users to call the setter, it’s better to pick one of the following solutions:

Don’t use late. Use late, but initialize the late field at its declaration. Use late, but make the late field private and define a public getter for it.

The above explanation is abstract and I have no concrete image of what kind of risk this rule envisions.

I would be grateful if you could give me a hint as to how to think.


Solution 1: obywan

Not sure if it's true, but I think this advice is to enforce better architecture. If field is final then you can set it value only once and almost always you need to initialize it from within the class it belongs to. But if setter for final field is public then people might attempt to set it value from elsewhere AFTER it is already initialized, and this will lead to an error.

UPD: two sections above there is this advice, that basically summarizes my point:

Objects shouldn’t generally expose more state than they need to.

...and setter is not needed after final field has been initialized.


Solution 2: Ivo

The risk is that you accidental try to assign a value twice, which will result in an error.

late final String a;

void someMethod() {   
  a = "a";
  a = "b";
}

The above code compiles perfectly fine and is valid code because of the late but leads to a crash.

As for the suggested solutions

Don’t use late.

Use late, but initialize the late field at its declaration.

final String a = "b";
// or
late final String a = "b";

void someMethod() {   
  a = "a";
  a = "b";
}

This makes it that the above code doesn't even compile, making it sure that the crash doesn't happen.


Solution 3: ken

  1. Late modifier means a variable's value is not known during declaration but will definitely get initialized and not null when it's accessed.

  2. When we declare a variable as final, it means that it will only assigned once. Therefore, all the final fields has to be initialized either at declaration or inside the class constructor.

Given above facts, for one to declare a late final variable as public would probably a mistake. One should either pass the value to final field as class constructor parameter, or declare the late final field as private and initialize it internally.

Let see the example why late final field should not be made public

class Coffee {
  late final String temperature;   // public field as there's no prefix underscore, e.g. _temperature

  // based on description from the guide, a setter will be created automatically for public late final
  set temperature(String val) => temperature = val;

  // again, do you think you would want above? 
  // as it's final field, it means it should only be initialized once! 
  // The creation of the setter for late final (public field) does not make too much sense
  // Therefore usage late final as public field is rarely what you will want

}