Here is a simplified summary of the ways to initialize a final class variable.
class MyClass {
final int x; // <-- initialize this
}
Initializer value
class MyClass {
final int x = 'hello'.length;
}
You'd only use final
if the initialization could only be done at runtime. Otherwise, static const
is better:
class MyClass {
static const int x = 0;
}
Initializer formal
class MyClass {
MyClass(this.x);
final int x;
}
This is the most common approach.
Initializer list
class MyClass {
MyClass(int x)
: _x = x;
final int _x;
}
This is useful when you want to keep a field private.
Default parameter value
You can surround the parameter with square brackets ([]
) for an unnamed parameter or curly braces ({}
) for a named parameter and then give it a default value.
class MyClass {
MyClass({this.x = 0});
final int x;
}
This is useful if you want to make the parameter optional.
You could accomplish the same thing with an initializer list as well:
class MyClass {
MyClass({int? x})
: _x = x ?? 0;
final int _x;
}
Late initialization
class MyClass {
MyClass(String? a) {
x = a?.length ?? 0;
}
late final int x;
}
This is useful if you need to do more complex initialization than is allowed in the initializer list. For example, I've done this when initializing a gesture recognizer in Flutter.
Lazy initialization
Another advantage of using late
is that it doesn't initialize a value until you access the value.
class MyClass {
late final int x = _doHeavyTask();
int _doHeavyTask() {
var sum = 0;
for (var i = 0; i < 100000000; i++) {
sum += 1;
}
return sum;
}
}
This is useful if you have a heavy calculation that you only want call if you absolutely need it.
This doesn't initialize x
:
final myClass = MyClass();
But this does initialize x
:
final myClass = MyClass();
final value = myClass.x;