One of the first things explained in most tutorials and programming guides is the use and declaration of variables. In Dart, you could declare those with const
, final
, dynamic
, var
, and by using the type itself; but what are their differences and when should we use each one?
Choosing the correct declaration is crucial to writing clean, safe, and performant code. Let's see each one in detail and when we should use it:
var
var
is short for variable. It is used to declare a variable whose type is statically inferred:
var text = "hello";
In the example above we are declaring a variable of type String
. Dart is able to infer the type, which means that there are times when it is able to know what type it is by the value that we have given. In this case, when assigning a text string delimited by the "
characters, it is quite evident that it is a String
.
Just because we defined that variable using var
doesn't mean we can change its type later:
var text = "hello";
text = 10;
If we execute that piece of code, the compiler will fail and will let us know that we cannot change the type of a variable.
Another way to declare variable properties is directly using the type identifier instead of the var
keyword:
var first = "first";
String second = "second";
In this example we are creating a variable of type String
with value first, and then a variable of type String
with value second.
It is considered good practice to be explicit about the type declaration when declaring a property of a class, whereas variables in a smaller scope such as a method we can use the var
keyword:
class MyClass {
String name = "David";
void someMethod() {
var anotherName = "Tom";
}
}
Using any of the previous methodologies we can create variables whose value is reassignable:
var text = "hello";
print(text); // Prints: hello
text = "bye";
print(text); // Prints: bye
In short, we should use var
(or the name of the type) when we want to declare a variable when we know its type and that it is going to be modifiable during the execution of our program.
dynamic
dynamic
is similar to var
in that it is a variable whose value can be changed during program execution, but its type can also vary:
var text = "hello";
text = 10; // Throws an error
dynamic otherText = "otherText";
otherText = 10; // This is fine
Generally speaking, you should avoid using dynamic
for your variable declarations as much as possible. The reason is not knowing the type can lead to unexpected errors, something you've probably experienced if you've ever worked with a dynamically typed language like javascript.
That's not to say that it can't be useful on some occasions though, a typical example would be decoding a JSON value:
final json = jsonDecode(content) as Map<String, dynamic>;
In this example we are decoding a JSON value as a map of String
and dynamic
. We know that each identifier is going to be a String
, but we don't know what type each value will have, so we use dynamic
. Finally, to use any of the values, we should perform a type check to avoid errors:
final json = jsonDecode(content) as Map<String, dynamic>;
if (json['text'] is String) {
String text = json['text'] as String;
}
In short, dynamic
should be used only for variables whose type is unknown.
final
final
is a keyword that we use to declare variables whose value can't be reassigned, which means that its value won't change. Its use is recommended whenever possible:
final String text = "this value will never change";
It can also be shortened using type inference:
final text = "this value will never change";
It's important to note that final
cannot be used together with var
:
final var text = "final var"; // Throws an error
As I said, the final
variables cannot be modified:
final text = "final var";
text = "another text"; // Throws an error
The recommendation is to use final
whenever we know that a value cannot change, but to apply this rule we must first know const
:
const
const
, like final
; is an immutable variable whose value cannot be changed; but it has to be a compile-time constant:
const text = "const value";
If this constant value is in the body of a class, it's best to mark it as static
for further optimization:
class MyClass {
static const text = "const value";
}
Since the value must be evaluable at compile time, it is not always possible to use it. The general rule for variables whose value is not going to change is to use const
whenever possible and final
otherwise.
class MyClass {
static const text = "const value";
}
void main() {
const instance = MyClass(); // Throws an error
}
This code will fail since MyClass is not a constant value, however if we replace const
with final
we will get a working and efficient code:
class MyClass {
static const text = "const value";
}
void main() {
final instance = MyClass();
}
Another possible solution is to declare MyClass as a constant value by using const
in its constructor:
class MyClass {
static const text = "const value";
const MyClass();
}
void main() {
const instance = MyClass();
}
const in the context of Flutter
It is recommended to use const
constructors whenever possible when creating Flutter widgets. The reason is the performance increase, since Flutter can save some calculations by understanding that it can reuse that widget from a previous redraw in the current one, since it is a constant value.
Here is a very interesting article by Crizant Lai that analyzes the performance impact of using a widget with or without a const
constructor.
Conclusion
In this article we have seen the differences between const
, final
, dynamic
and var
and when to use each one. If you want to learn more details about Dart and its type system I invite you to go to the Dart language tour.
Thank you for reading this far,
Happy coding!
Top comments (0)