var is used to declare a variable where the type is inferred based on the assigned value at compile time.
Once a var variable is assigned a type, it cannot change to a different type.
Example:
void main() {
var name = "Alice"; // `name` is inferred as a `String`
print(name);
name = "Bob"; // This is allowed because `name` is a mutable `String`
print(name); // name = 123; // Error: The variable `name` is inferred as a `String`, so it cannot hold an `int`
}
dynamic is used when you want a variable that can hold values of any type and can change type at runtime.
Dart will not perform type checks on a dynamic variable, which can lead to runtime errors if types are not handled carefully.
Example:
void main() {
dynamic variable = "Hello"; // Initially a `String`
print(variable);
variable = 42; // Now an `int`
print(variable);
variable = true; // Now a `bool`
print(variable); // No type safety is provided with `dynamic`, so be cautious when using it.
}
final is used to declare a variable that can only be set once.
The value of a final variable is determined at runtime, making it suitable for values that may only be available when the program is running.
Once assigned, a final variable cannot be changed.
Example:
void main() {
final currentTime = DateTime.now(); // Value set at runtime
print("Current time: $currentTime");
// currentTime = DateTime.now(); // Error: `currentTime` is final and cannot be reassigned
}
const is used for compile-time constants, which means the value must be known and assigned at compile time.
const variables are deeply immutable, meaning they cannot change and are fixed in memory for efficiency.
const is usually used for values that are fixed and known ahead of time (e.g., mathematical constants, configuration values).
Example:
void main() {
const pi = 3.14159;
print("Value of pi: $pi"); // pi = 3.14; // Error: `pi` is const and cannot be reassigned
// Using const with collections
const colors = ['red', 'green', 'blue'];
print("Colors: $colors"); // colors[0] = 'yellow'; // Error: `colors` is const and elements cannot be modified
}
In Dart, var and dynamic are both used to declare variables without explicitly specifying their type, but they behave differently. Let's break down both concepts and understand their differences.
var is used to declare a variable without specifying its type, and Dart will infer the type based on the value assigned to the variable at runtime.
Once the type is inferred, the variable’s type is fixed and cannot be changed. This means that a var variable is not completely "dynamic"; its type is determined at assignment and cannot be reassigned to a different type later.
Key Characteristics of var:
Type inference: Dart automatically infers the type when the variable is initialized.
Fixed type: After the type is inferred, it becomes fixed for that variable.
Compile-time check: Dart performs type checking at compile-time.
Example of var:
dart
Copy code
void main() {
var name = 'John'; // Dart infers that name is of type String
print(name); // Prints: John
// Uncommenting the following line will result in an error because 'name' is of type String
// name = 42; // Error: A value of type 'int' can't be assigned to a variable of type 'String'.
}
In the above example, Dart infers that name is a String because it's initialized with a string. Therefore, trying to assign a value of type int to name will result in a compile-time error.
dynamic is a special type in Dart that allows no type checking during compilation, meaning that the variable can hold any type of value at runtime.
dynamic variables are resolved at runtime and can be reassigned to any type, even after being initialized.
No type safety: Since Dart does not check the type of a dynamic variable during compilation, you can assign values of any type to a dynamic variable.
Key Characteristics of dynamic:
No type inference: The type is not inferred by Dart; it is explicitly marked as dynamic, which means it can hold any type.
Runtime checks: The type of the variable is resolved at runtime, so the type can change as needed.
No type safety: Since it bypasses compile-time type checking, errors related to type incompatibilities will occur only at runtime.
Example of dynamic:
dart
Copy code
void main() {
dynamic name = 'Alice'; // Initially assigned a String
print(name); // Prints: Alice
name = 42; // Now assigned an int (no error)
print(name); // Prints: 42
name = true; // Now assigned a boolean (still no error)
print(name); // Prints: true
}
In the above example, name is declared as dynamic, so we can change its type multiple times during runtime (from String to int to bool) without any errors at compile-time.
Feature
var
dynamic
Type Inference
The type is inferred based on the assigned value.
No type inference; the type is explicitly dynamic.
Type Safety
Type is fixed after initialization; compile-time type checking is enforced.
No compile-time type checking; type can change at runtime.
Reassignment
Cannot change the type after initialization.
Can be reassigned to any type at any time.
Example Use Case
Use when you know the type will not change and can be inferred from the initial value.
Use when the type can change during runtime or is unknown.
Using var:
dart
Copy code
void main() {
var number = 10; // Inferred as int
print(number); // Prints: 10
// Uncommenting the following line will result in an error because 'number' is an int
// number = 'hello'; // Error: A value of type 'String' can't be assigned to a variable of type 'int'.
}
Using dynamic:
dart
Copy code
void main() {
dynamic number = 10; // Initially an int
print(number); // Prints: 10
number = 'hello'; // Reassigned to a String (no error)
print(number); // Prints: hello
number = 3.14; // Reassigned to a double (no error)
print(number); // Prints: 3.14
}
As you can see in the second example, with dynamic, the type of the variable is flexible, and you can change it at runtime without any errors.
Use var when:
You want Dart to infer the type at compile-time.
You are sure the type will not change after initialization.
You want type safety at compile-time.
Use dynamic when:
The type of the variable is not known at compile-time and can vary.
You need the flexibility to change the variable's type during runtime.
You want to bypass Dart's type safety features for certain cases (although this is generally discouraged unless absolutely necessary).
dart
Copy code
void main() {
var number = 10; // Dart infers the type as int
print('Number: $number'); // Prints: Number: 10
// Uncommenting the following line will result in an error because 'number' is of type int
// number = 'Hello'; // Error: A value of type 'String' can't be assigned to a variable of type 'int'.
}
dart
Copy code
void main() {
dynamic value = 10; // Initially an int
print('Value: $value'); // Prints: Value: 10
value = 'Hello Dart'; // Reassigned to a String
print('Value: $value'); // Prints: Value: Hello Dart
value = true; // Reassigned to a boolean
print('Value: $value'); // Prints: Value: true
}
dart
Copy code
void main() {
var fruits = ['Apple', 'Banana', 'Cherry']; // Inferred as List<String>
print('Fruits: $fruits'); // Prints: Fruits: [Apple, Banana, Cherry]
// Uncommenting the following line will result in an error because 'fruits' is of type List<String>
// fruits.add(10); // Error: A value of type 'int' can't be added to a list of type 'String'.
}
dart
Copy code
void main() {
dynamic items = ['Apple', 'Banana', 'Cherry']; // Initially a List<String>
print('Items: $items'); // Prints: Items: [Apple, Banana, Cherry]
items.add(10); // Added an integer to the list
print('Items: $items'); // Prints: Items: [Apple, Banana, Cherry, 10]
items = {'key': 'value'}; // Reassigned to a Map
print('Items: $items'); // Prints: Items: {key: value}
}
dart
Copy code
int multiply(int a, int b) {
return a * b;
}
void main() {
var result = multiply(5, 4); // Dart infers the type as int
print('Result: $result'); // Prints: Result: 20
}
dart
Copy code
dynamic add(dynamic a, dynamic b) {
return a + b;
}
void main() {
dynamic result = add(10, 5); // Adds two integers
print('Result: $result'); // Prints: Result: 15
result = add('Hello', ' Dart'); // Adds two strings
print('Result: $result'); // Prints: Result: Hello Dart
}
dart
Copy code
void main() {
var age = 25;
var message = (age >= 18) ? 'Adult' : 'Minor';
print('Message: $message'); // Prints: Message: Adult
}
dart
Copy code
void main() {
dynamic x = 10; // Integer
print('x: $x'); // Prints: x: 10
x = 'Hello Dart'; // String
print('x: $x'); // Prints: x: Hello Dart
x = 3.14; // Double
print('x: $x'); // Prints: x: 3.14
x = true; // Boolean
print('x: $x'); // Prints: x: true
}
dart
Copy code
void main() {
var numbers = [1, 2, 3, 4, 5]; // List of integers
for (var number in numbers) {
print('Number: $number'); // Prints: Number: 1, 2, 3, 4, 5
}
}
dart
Copy code
void main() {
dynamic map = {'name': 'Alice', 'age': 25}; // Initial Map
print('Map: $map'); // Prints: Map: {name: Alice, age: 25}
map['location'] = 'Wonderland'; // Adding a new key-value pair
print('Updated Map: $map'); // Prints: Updated Map: {name: Alice, age: 25, location: Wonderland}
map = [1, 2, 3]; // Reassigned to a list
print('Reassigned Map (now List): $map'); // Prints: Reassigned Map (now List): [1, 2, 3]
}
var:
Dart infers the type of the variable at compile-time based on the initial value assigned.
The type cannot be changed once inferred, and the variable is statically typed.
dynamic:
The variable can be reassigned to any type at any time.
No compile-time type checking is enforced, allowing the variable to hold different types during runtime.
Use var when you want the type to be inferred, but once assigned, it should not change.
Use dynamic when you need flexibility to change the type of a variable during runtime, but this comes at the cost of losing compile-time type safety.