Constants are not just an obnoxious version of final variables that will haunt you in your sleep with all of the errors associated with them. Compile-time constants are actually a nice way to improve the performance of your app by not instantiating the same object multiple times and additionally, so to say, "pre-instantiating" objects at compile time.
Const or Final?
Let's get this question out of the way before moving on to exploring constants in depth. It seemingly doesn't matter whether you have a const
or final
keyword before a variable. These variables simply cannot be modified after their declaration.
main.dart
void run() {
const myConstNumber = 5;
final myFinalNumber = 5;
myConstNumber = 42; // error
myFinalNumber = 42; // error
}
final
is additionally suited for more occasions while const
can be used only for top-level, static or local variables. That means no const
instance fields.
main.dart
class MyClass {
final int myFinalField;
const int myConstField; // error
MyClass(this.myFinalField, this.myConstField);
}
To avoid a taxing decision-making process whenever you want to create a variable, many developers simply choose final
every time without even thinking about const
. I hope this will change once you see the benefits of canonical instances. But first...
Built-in constants
Every Dart type with a literal is practically constant. Writing string literals such as 'hello'
or numeric literals like 3.14
directly into the code naturally creates objects which are known at compile time.
What's more, even collection literals can be put into constant variables.
main.dart
void run() {
const myList = [1, 2, 3];
const myMap = {'one': 1};
const mySet = {1, 2, 3};
}
if
and for
, the ...
spread operator, type checking and casting is also supported inside constant collection literals.But again, much like with numeric and string literals you can just as well put collection literals into final fields. ?
main.dart
void run() {
final myList = [1, 2, 3];
final myMap = {'one': 1};
final mySet = {1, 2, 3};
}
Compiler's view on constants
As you could already see, there is literally no difference between const
and final
variables from the programmer's point of view, other than the fact that constants are harder to work with. The Dart compiler has a totally different pair of eyes though and it sees a huge difference, best illustrated on a code snippet. The highlighted parts below are seen as constant to the compiler.
main.dart
void run() {
final finalVariable = 123456;
const constVariable = 123456;
}
It may not be apparent at first glance but there is a big benefit to the whole field being constant instead of just the value being constant. Constant variables can be used further down the line in other places requiring constants.
main.dart
void run() {
final finalVariable = 123456;
const constVariable = 123456;
const notWorking = finalVariable; // error
const working = constVariable;
}
Assigning constant variables to other constant variables may not be something you do on a daily basis but there is one thing where passing around constant variables comes in handy...
Const constructors
Many Flutter classes have const
constructors, for example, the EdgeInsets
class used for Padding
. This is extremely useful performance-wise because of what is known as canonical instances.
const EdgeInsets.all(8)
hundreds of times throughout your app doesn't clutter up the memory with hundreds of different instances. Instead, whenever you use the same arguments for a const
constructor or a factory, the same "canonical" instance will be reused.You can also create your own class with a const
constructor. The only rule is: Constant class fields must be final
and capable of being constant themselves.
main.dart
class MyConstClass {
final int field1;
final List<String> field2;
const MyConstClass(this.field1, this.field2);
}
void run() {
// passing in a const variable
const constVariable = 123456;
const x = MyConstClass(constVariable, ['hello', 'there']);
}
This means, you cannot have a field of a type which doesn't have a const
constructor. Sort of...
main.dart
class MyWannabeConstClass {
// Future doesn't have a const constructor or a const factory
final Future<int> field;
// Dart allows us to define a seemingly nonsensical constructor:
const MyWannabeConstClass(this.field);
}
void run() {
// Dart doesn't allow us to use the const constructor:
const x = MyWannabeConstClass(Future.value(123)); // error
}
So why does Dart even allow us to define a constructor which seems to always fail? Well, because it won't fail if you say so. Class having a const
constructor doesn't mean you always have to get canonical instances. You can also instantiate new instances as usual.
main.dart
void run() {
// Passing in a Future doesn't give errors with a non-const constructor
final implicitNew = MyWannabeConstClass(Future.value(123));
final explicitNew = new MyWannabeConstClass(Future.value(123));
}
const
constructor is possible, it's rather discouraged as you lose out on all the amazing performance benefits of reusing the same instance possibly thousands of times in a big Flutter app.Const context
As you can see above, the sole fact of changing a const
variable into a final
variable automatically creates a "new" instance.
Much like the new
keyword is optional when instantiating new instances, the const
keyword is also optional when trying to get existing canonical instances.
There is absolutely no difference in the following two lines:
main.dart
void run() {
const implicitConst = MyConstClass(1);
const explicitConst = const MyConstClass(1);
}
But there is still a place for explicitly invoking the const
constructor when you want to store a constant, canonical instance inside a non-constant variable.
main.dart
void run() {
final regularVariable = const MyConstClass(1);
}
Const = ?
This tutorial has hopefully demystified the meaning of const
constructors and Dart constants in general. Try using const
whenever possible and you will bring small performance improvements into your apps line by line. And you know how it is with small improvements - they add up.
Your article helped me a lot, is there any more related content? Thanks!