Final & Const Fields
Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 18 of 110.
When declaring fields in a class, you can use final or const to control whether values can change after initialization. Both create immutable values, but they work differently.
A final field can only be set once, but its value is determined at runtime. You can assign it in the declaration or through a constructor:
class Person {
final String name;
final DateTime createdAt = DateTime.now();
Person(this.name);
}
Person p = Person('Alice');
// p.name = 'Bob'; // Error: can't reassign final fieldThe name is set when the object is created, and createdAt captures the current time. Both values are locked after initialization.
A static const field must have a value known at compile time. It belongs to the class itself and is shared across all instances:
class Circle {
static const double pi = 3.14159;
final double radius;
Circle(this.radius);
double get area => Circle.pi * radius * radius;
}The key difference: final fields can hold values computed at runtime (like DateTime.now()), while const requires compile-time constants. Instance fields cannot be const directly - only static const is allowed at the class level.
Challenge
EasyLet's build a product catalog system that demonstrates the difference between final and static const fields. You'll create products with immutable properties and shared configuration values.
You'll organize your code into two files:
product.dart: Define aProductclass that represents an item in a store. Your class should have:- A
static const double taxRateset to0.08(8% tax rate shared across all products) - A
static const String currencyset to'USD' - A
final String namefor the product name (set via constructor) - A
final double basePricefor the product's base price (set via constructor) - A
final String sku(stock keeping unit) that combines a prefix with the product name, calculated in an initializer list as'SKU-'followed by the uppercase name - A constructor that takes
nameandbasePrice - A getter
priceWithTaxthat calculates the price including tax - A
display()method that prints the product information
- A
main.dart: Import your product class and create two products:- A
'Laptop'with base price999.99 - A
'Mouse'with base price29.99
display()on each product in the order listed above.- A
The display() method should print in this exact format:
[sku] - [name]: [basePrice] [currency] (with tax: [priceWithTax] [currency])Round priceWithTax to 2 decimal places using toStringAsFixed(2) when displaying.
Expected output:
SKU-LAPTOP - Laptop: 999.99 USD (with tax: 1079.99 USD)
SKU-MOUSE - Mouse: 29.99 USD (with tax: 32.39 USD)Cheat sheet
Use final for fields that are set once at runtime and cannot be changed afterward. Use static const for compile-time constants shared across all instances of a class.
final fields are set at runtime and can be initialized in the declaration or constructor:
class Person {
final String name;
final DateTime createdAt = DateTime.now();
Person(this.name);
}static const fields must have compile-time constant values and belong to the class itself:
class Circle {
static const double pi = 3.14159;
final double radius;
Circle(this.radius);
double get area => Circle.pi * radius * radius;
}You can compute final field values in an initializer list:
class Product {
final String name;
final String sku;
Product(this.name) : sku = 'SKU-${name.toUpperCase()}';
}Key differences:
final: Runtime values, can be different for each instancestatic const: Compile-time constants, shared across all instances- Instance fields cannot be
constdirectly - onlystatic constis allowed
Try it yourself
import 'product.dart';
void main() {
// TODO: Create a Product called 'Laptop' with base price 999.99
// TODO: Create a Product called 'Mouse' with base price 29.99
// TODO: Call display() on each product in order (Laptop first, then Mouse)
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesLibraries & ImportsIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsInstance VariablesConstructor BasicsRecap - Simple Calculator4Null Safety
Intro to Null SafetyNullable vs Non-NullableThe ? and ! OperatorsLate Keyword & Null SafetyNull-Aware OperatorsNull Safety in ClassesRecap - User Profile System7Abstract Classes & Interfaces
Abstract ClassesAbstract MethodsInterfaces in DartImplicit InterfacesImplementing vs ExtendingMultiple InterfacesRecap - Shape Calculator10Collections & Generics
List, Set, Map OverviewType-Safe CollectionsGeneric ClassesGeneric MethodsGeneric ConstraintsIterable & IteratorRecap - Generic Storage13Advanced OOP Concepts
Composition vs InheritanceExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums with Methods16Project: Library Management
Project OverviewBook and User Classes2Constructors in Dart
Default ConstructorNamed ConstructorsInitializer ListsConstant ConstructorsFactory ConstructorsRedirecting ConstructorsRecap - Shape Builder5Encapsulation
Public vs Private MembersThe _ Prefix ConventionLibrary-Level PrivacyGetters & Setters DepthInformation HidingRecap - Student Records8Mixins
Introduction to MixinsCreating MixinsUsing Multiple Mixinson Keyword in MixinsMixin vs InheritanceMixin vs InterfaceRecap - Animal System11Special Methods
toString() OverridehashCode & == OverrideComparable Interfacecall() MethodnoSuchMethod OverrideRecap - Custom Collection14Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern3Class Properties
Instance vs Static MembersFinal & Const FieldsLate VariablesStatic Methods & FieldsGetters and SettersRecap - Bank Account Manager6Inheritance
Basic InheritanceThe super KeywordMethod OverridingThe @override AnnotationThe final Class KeywordConstructors & InheritanceRecap - Employee Hierarchy9Polymorphism
Polymorphism BasicsPolymorphism via InterfacesRuntime Type CheckingThe is & as OperatorsCovariant KeywordRecap - Payment Processor