This tutorial explains the meaning of variable keywords in Dart which include var
, final
, const
, static
, and dynamic
.
Using var
Keyword
var
is used to declare a variable with inferred type. If you declare a variable using the var
keyword, Dart will infer the type according to the initializer type. If there is no initializer, the type will be set to dynamic
.
A variable declared using var
can be reassigned. However, you cannot assign a value of different type unless it's declared with no initializer in which the type is set to dynamic
.
var text1 = 'Hello world'; // String
text1 = 'Hello world from Woolha.com';
text1 = 1; // Error: A value of type 'int' can't be assigned to a variable of type 'String'. (compile-time)
var text2; // dynamic
text2 = 2;
text2 = 'Two';
In the code above, text1
is assigned with a String value. The declaration statement is actually the same as String text1 = 'Hello world';
. It can be reassigned with another String value. However, if you try to assign a value of another with type, the compiler will give you an error.
Meanwhile, the text2
variable doesn't have an initial value. As a result, Dart sets the variable to have a dynamic
type. Therefore, it can be reassigned with another value of any type.
We can conclude that the initializer type determines the type of a variable declared using the var
keyword. If you prefer to initialize variables without explicitly stating the types, using the var
keyword is the solution.
In addition, it also makes it less likely to remove the generic part of a type. For example, if you create a List
variable whose elements are integers, the type should be List<int>
. If you explicitly write the types, you may forget to write the generic type. As a result, the variable type becomes List<dynamic>
instead. It will not happen if you use var
because Dart can detect that the list contains integer only values and therefore the type is set to List<int>
.
List numbers1 = [1, 2, 3]; // List<dynamic>
var numbers2 = [1, 2, 3]; // List<int>
var
is useful for declaring variables with a small scope whose value can be reassigned later. For example, the variables inside a method. However, for class members and method parameters, it's recommended to explicitly state the types.
Using final
Keyword
The final
keyword means the variable cannot be reassigned. If you try to reassign it, the compiler will give you an error.
To declare a variable using the final
keyword, you can either explicitly state the type or let Dart infer the type. The type inference is similar to the var
keyword. It depends on the type of the initializer value. It can be a dynamic
type if there is no initializer.
final DateTime currentTime1 = DateTime.now();
currentTime1 = DateTime.now(); // Error. The final variable 'currentTime1' can only be set once. (compile-time)
final currentTime2 = DateTime.now(); // DateTime
final currentTime3; // dynamic
final
only prevents a variable to be reassigned. It doesn't guarantee that the variable cannot be modified. In the example below, despite the List
variable being declared using the final
keyword, it's still possible to modify the List
value.
final scores = [80, 90, 100];
scores.remove(80);
print(scores); // [90, 100]
If you have a variable that cannot be reassigned, it would be better to use final
to avoid accidental reassignment. final
is suitable for cases where the value cannot be determined at the compile time. For example, the current time value can only be determined when the code is executed.
Using const
Keyword
const
can be used for variables whose value can be determined at compile time and cannot be reassigned . Therefore, it's usually used to store hard-coded constant values.
Similar to using const
, you can explicitly write the type or not. In the latter case, Dart will infer the variable type based on the initializer value. The difference is you cannot define a const
with no initializer value.
const Duration readTimeout = Duration(seconds: 30);
readTimeout = Duration(seconds: 300); // Constant variables can't be assigned a value.
const connectTimeout = Duration(seconds: 10);
connectTimeout = Duration(seconds: 100); // Constant variables can't be assigned a value.
const timeout; // Error: The constant 'timeout' must be initialized. (compile-time)
The usage of const is related to object canonicalization, which affects how object comparison works in Dart. You may already know about the const constructors. By defining a const constructor, two objects created using the same constructor argument values will only be created once. They will refer to the same object in the memory. Proper usage of the const
keyword may affect the performance of your application.
class Person {
final String name;
const Person({required this.name});
}
main() {
const person = Person(name: 'Ivan');
}
That capability is also utilized a lot by Flutter. In Flutter, there are a lot of widgets with a const constructor. If a widget is created using a const constructor, Flutter may prevent that particular widget from being rebuilt when the parent tree is being rebuilt.
Using static
Keyword
In Dart, the static
keyword is used on class data members. It can be used on class variables and class methods. By declaring a class member using the static
keyword, Dart will only create it once and it's the same across all instances of the class. In addition, you don't need to create an instance of the class to access it.
Below is the example of a static
variable. To define a static variable, you can write the static
keyword followed by the var
, final
, const
or the explicit variable type before the variable name.
class MyHelper {
static int counter = 0;
void doSomething() {
counter++;
print('counter: $counter');
}
}
The class above has a static variable counter
. There is a method named doSomething
that increments the counter
value and prints the current value. Let's see what happens if we have two instances of MyHelper
class.
var helper1 = MyHelper();
helper1.doSomething(); // counter: 1
var helper2 = MyHelper();
helper2.doSomething(); // counter: 2
The result is, inside helper2.doSomething()
, the counter
value is 2 despite helper2
only calls the doSomething
method once. That's because all instances of MyHelper
class share the same counter
variable.
It's also very common to combine the static
keyword with the const
keyword for declaring constant values. If you want to create a class that stores constant values, it's also better to make the class to not have any instance.
Using dynamic
Keyword
The dynamic
keyword is used to disable static type-checking. That allows you to call any method on a variable with dynamic
type. The checking will be done at runtime instead of compile time. Therefore, it's possible that your code compiled successfully but gets an error later when the code is executed.
A variable with the dynamic
keyword can be reassigned with a value of any type. To check the type of the value that's currently assigned to a dynamic
variable, you can check the runtimeType
property of the object.
The dynamic
keyword can be preceded by final
or const
. In that case, the variable cannot be reassigned.
dynamic x = 1;
print(x.runtimeType);
x = 'a';
print(x.runtimeType);
int x2 = x as int; // type 'String' is not a subtype of type 'int' in type cast (runtime)
const dynamic y = 2;
y = 20; // Error: Constant variables can't be assigned a value. (compile-time)
final dynamic z = 3;
z = 30; // Error: The final variable 'z' can only be set once. (compile-time)
Summary
In this tutorial, we have learned the meaning of var
, final
, const
, static
, and dynamic
keywords in Dart programming language. Having understood those keywords, you should be able to determine which one to use when declaring a variable.
You can also read about: