DEV Community

Cover image for const, final, dynamic, var... when and why?
David Serrano
David Serrano

Posted on • Originally published at davidserrano.io

const, final, dynamic, var... when and why?

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?

📽 Video version available on YouTube and Odysee

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";
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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";
   }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>;
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

It can also be shortened using type inference:

final text = "this value will never change";
Enter fullscreen mode Exit fullscreen mode

It's important to note that final cannot be used together with var:

final var text = "final var"; // Throws an error
Enter fullscreen mode Exit fullscreen mode

As I said, the final variables cannot be modified:

final text = "final var";
text = "another text"; // Throws an error
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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";
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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)