Dive into Dart — const & final keywords
For Absolute Beginner in Dart Language.
Dive into Dart — const & final keywords.
For Absolute Beginner in Dart Language.
There is a lot of confusion, especially amongst beginners; I know I needed clarification on the keywords ‘const’
and ‘final’
. What are these keywords used for? When should it be used? And what is the difference between these two? That is what we are going to talk about in this article. Again, I am writing for someone who is a complete beginner, but others will also benefit from it. The whole idea is I am putting myself into their shoes because I have gone through the process, and sometimes I wish I had someone explaining it to me in plain and straightforward language. It would have been so much easier for me to understand and practise with it.
First, let us start with these two terminologies which programmers often use.
- Compile-time: When the code is converted into machine language and is ready for execution, it is known as compile-time.
- Runtime: When the executable code has started running. The program is up and running, generally occurring after compile time.
I love baking brownies and cookies and am really good at it; at least, that’s what my children tell me.
Imagine you are baking cookies and following a delicious recipe of Chocolate Chip Cookies. In this case, we will declare values ‘final’
or ‘const’
.
Final:
- Number of cups of flour: You measure out the flour exactly once. You cannot add more flour later while baking; otherwise, the cookies will turn out differently.
- Baking temperature: Once you set the oven to 350°F, you don’t change it because you must bake the cookies properly.
Const:
- Number of cookies per tray: You make 12 cookies per tray and keep it consistent for all batches. Changing the number would affect baking time and consistency.
- Shape of the cookie cutter: You use a star-shaped cookie cutter, and all the cookies turn out the same. You can’t suddenly decide to make round cookies because it wouldn’t be the same recipe anymore.
Let’s dive deeper into the technical side of ‘final’
and ‘const’
using the above analogy.
- Compile-time: Imagine you’re reading the recipe book before you even start baking, which is similar to compiling time in programming. The compiler analyzes the code and prepares everything it needs to run the program.
- Runtime: Now you’re mixing ingredients, putting the cookies in the oven, and checking for doneness, similar to runtime in programming, where the actual code execution happens.
Final:
- Think of the flour quantity as a
final
variable. You measure it once at compile time (like reading the recipe), and its value is fixed throughout the program (like the baking process). You can’t add more flour later because the compiler already planned for that specific amount. - Baking temperature is another example. You set it at compile time (choosing the oven setting), and it remains constant at run time (the actual baking). Changing it mid-bake would mess up the cookies, just like changing the
final
variable’s value mid-program would break things.
Const:
- Chocolate chips are like
const
variables. You choose a specific type (milk chocolate, semi-sweet) at compile time and embed it in the program itself, like pre-made ingredients. You can’t switch to different chips at run time because the compiler already assumed those specific ones in its calculations. - The cookie cutter shape is another example. It’s like a
const
expression, a fixed value determined at compile time (choosing the cutter). The compiler knows how many cookies fit on a tray with that shape, so changing the shape of the cookie cutter mid-bake would mess up the tray layout and baking time, just like changing a const expression would break the program’s logic.
Importance of compile time and run time:
- Const: By fixing values at compile time, the compiler can optimize the code, making it faster and more efficient. It also guarantees consistency and immutability (unchangeable), preventing accidental changes that could break the program.
- Final: While less restrictive than
const
, it still ensures consistency within a program. You can still have calculations or user input set the value at run time, but it’s fixed once assigned. At the same time, it allows the programmers some flexibility and maintains the benefits of immutability.
Key differences:
- Mutability: In the application, the
final
keyword allows changing the value before program execution (like choosing ingredients before baking), whileconst
fixes the value (like using a specific cookie mould). - Compile-time vs. runtime:
const
values are determined and embedded at compile time, leading to optimizations and improved performance.Final
values are set at compile time or during program execution. - Flexibility: The
final
keyword offers flexibility for dynamic adjustments, while const provides strict consistency and immutability.
Remember, choosing between ‘final’
and ‘const’
depends on your needs. Use ‘const’
whenever possible for maximum efficiency and consistency and ‘final’
when you need some flexibility but want to avoid accidental changes.
Like choosing the right ingredients and tools for baking delicious cookies, understanding and using ‘final’
and ‘const’
effectively helps you write clean, efficient, and reliable programs.
Was the above example mouth-watering? Well, let us talk in more programming terms and look at a few examples to understand better where we can declare a ‘final’
or a ‘const’
variable.
Example 1: Declaring variables for Firestore Database.
// Using const for a constant collection path
static const String userCollectionPath = 'users';
// Using final for a runtime constant document ID
final String userId;
‘const’
is used for a constant string representing the collection path (userCollectionPath). This constant is known at compile-time and doesn’t change during the program’s execution.‘final’
is used for a runtime constant representing the user ID (userId). This value is provided when the DatabaseService instance is created but won’t change afterwards.
Example 2: Creating a class for SQL Database in Flutter.
import 'dart:async';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class User {
final int id;
final String firstName;
final String lastName;
final int age;
User({required this.id, required this.firstName, required this.lastName, required this.age});
// Using const for SQL table and column names
static const String tableName = 'users';
static const String columnId = 'id';
static const String columnFirstName = 'first_name';
static const String columnLastName = 'last_name';
static const String columnAge = 'age';
}
class DatabaseService {
late Database _database;
Future<void> openDatabase() async {
// Using const for the database name
const String dbName = 'my_database.db';
// Using const for SQL table creation query
const String createTableQuery = '''
CREATE TABLE ${User.tableName} (
${User.columnId} INTEGER PRIMARY KEY AUTOINCREMENT,
${User.columnFirstName} TEXT,
${User.columnLastName} TEXT,
${User.columnAge} INTEGER
)
''';
// Get a location using getDatabasesPath
String databasesPath = await getDatabasesPath();
String path = join(databasesPath, dbName);
// Open the database, creating the table if it doesn't exist
_database = await openDatabase(
path,
version: 1,
onCreate: (db, version) async {
await db.execute(createTableQuery);
},
);
}
Future<void> insertUser(User user) async {
// Using final for SQL insert query
final String insertQuery = '''
INSERT INTO ${User.tableName} (
${User.columnFirstName},
${User.columnLastName},
${User.columnAge}
) VALUES (?, ?, ?)
''';
await _database.transaction((txn) async {
await txn.rawInsert(insertQuery, [user.firstName, user.lastName, user.age]);
});
}
Future<User?> getUser(int userId) async {
// Using final for SQL select query
final String selectQuery = '''
SELECT * FROM ${User.tableName} WHERE ${User.columnId} = ?
''';
List<Map<String, dynamic>> result = await _database.rawQuery(selectQuery, [userId]);
if (result.isNotEmpty) {
return User(
id: result[0][User.columnId],
firstName: result[0][User.columnFirstName],
lastName: result[0][User.columnLastName],
age: result[0][User.columnAge],
);
} else {
return null;
}
}
}
‘const’
is used for SQL table and column names (User.tableName
,User.columnId
, etc.). These values are known at compile-time and won’t change during the program’s execution.‘final’
is used for SQL queries (insertQuery
,selectQuery
). These queries are determined at runtime, but their values won’t change once defined.‘final’
is also used for theDatabaseService
and User instances created from the retrieved data. Once these instances are set, they won’t change.
Here are a few more examples to be thorough on this concept. I aim to provide as much information as possible so that readers understand and use the idea for their programming. I have done the same with my Flutter widget articles.
Const: Variable examples.
// Const variables must be initialized with a constant value.
const String birthDate = "1998/04/13";
/*While DateTime.now() which is not compile time constant.
Because this method will return the time when the line is
getting executed at runtime. So we can't assign the DateTime.now()
to a const variable. This will throw an error.
*/
const datetime = DateTime.now();
// this is acceptable
const int number = 5;
// below 2 statement will cause compilation error.
// Because const variable must be initialized at the same line.
const numB;
numB = 6;
// Without type or var
const number = 7;
// With a type
const int numB= 7;
// the below line causes a compilation error.
// Cause const caannot be used with type var.
const var numC= 6;
class A {
//Only static fields can be declared as const in Class level variables.
static const birds = "pigeon";
}
class B {
// below statement will give compilation error.
// because const variable is not possible to be used with instance level
// variable.
const a = 5;
}
Final: Variable example.
// The final variable 'number' can only be set once.
final int number = 7;
// below statement will give compilation error.
// Because number variable is declared as final.
number = 6;
// Without type or var
final a = 5;
// With a type
final int b = 5;
// Can't use var along with final keyword.
// below line cause compilation issue.
// Members can't be declared to be both 'final' and 'var'.
final var c = 6;
// class level variables must be initialised.
static final a = 5;
// DateTime.now() will return the time when the line is getting
// executed. Which is a runtime value.
static final b = DateTime.now();
The instance level final
variable must be initialized in the same line or the constructor initialization. The value will be put into memory when the object is created.
void main() {
bakeCookies(3);
A objA = new A();
B objB = new B('name');
C objC = new C('flutter', 6);
}
class A {
final int a = 5;
}
// Constructor with a parameter.
class B {
final String b;
B(this.b);
}
// Constructor with multiple parameter.
class C {
final String c;
C(this.c, int d) {
// Do something with d
}
}
// assigning a list to final variable.
final numbers = [5, 6, 7, 5.6,];
// Below statement will give compilation error.
// Because we are trying to reinitialize the object with another list.
// The final variable 'numbers' can only be set once.
numbers = [9.9, 10,];
Crucial points to remember:
When using the const and final keywords in Dart to declare and initialize variables, there are some crucial points to keep in mind:
- Immutability:
- Both
const
andfinal
express immutability, signifying that you cannot change the variable’s value after assigning it. Recognize that once you set a value, it remains constant.
2. Compile-Time vs. Runtime:
const
is evaluated at compile-time. The value must be known and fixed during compilation.final
is evaluated at runtime. While it won’t change after being set, its value is determined during the program’s execution.
3. Type Annotation:
const
can be used with any data type, including user-defined classes, as long as the values are compile-time constants.final
is often used when the type is known during runtime, such as when dealing with user input or configurations.
4. Initialization:
const
requires assigning the value at compile-time and must be a constant expression. Dart should be able to evaluate it during compile-time.- You can assign the value at runtime for the
final
variable, but once set, it remains unchangeable.
4. Use Cases:
- Use
const
for truly constant values, known at compile-time, and won’t change during the program’s execution. Examples include mathematical constants or fixed lists. - Use
final
for values that are determined at runtime but won’t change after being set. It is helpful for configurations, user inputs, or values calculated once and used throughout the program.
5. Performance Considerations:
const
values are evaluated at compile-time, potentially resulting in better performance as the values are known beforehand.- The compiler will evaluate
final
variables at runtime, and their initialization can be deferred until needed, allowing for more flexibility but potentially with a slight runtime cost.
6. Late Keyword (for final):
- You can use the
late
Keyword if you need to delay the initialization of afinal
variable until runtime. It allows you to set the value before the variable is accessed but enforces immutability afterwards.
Remembering these points will help you choose between const and final based on your specific use case and ensure your code behaves as expected.
Final thoughts
I hope I have successfully explained the distinction between the const
and final
keywords for the readers. Please let me know if you believe something is missing from this article. I would be highly grateful if you could show your appreciation by clapping a few times for me. The faster you clap, the more magical it will be. Keep experimenting, and stay safe!