Late Variables
Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 19 of 110.
The late keyword tells Dart that a non-nullable variable will be initialized before it's used, even though it's not assigned immediately. This is useful when you can't initialize a field in the declaration or constructor, but you're certain it will have a value before being accessed.
class User {
late String username;
void initialize(String name) {
username = name;
}
void greet() {
print('Hello, $username!');
}
}
User user = User();
user.initialize('Alice');
user.greet(); // Hello, Alice!Without late, Dart would complain that username isn't initialized. The late modifier promises that you'll assign a value before reading it.
Another benefit of late is lazy initialization. A late variable with an initializer won't compute its value until first accessed:
class Config {
late String settings = loadSettings();
String loadSettings() {
print('Loading...');
return 'default';
}
}
Config config = Config();
print('Config created');
print(config.settings); // "Loading..." prints here, then "default"The loadSettings() method only runs when settings is first accessed, not when the object is created. Be careful though - accessing a late variable before it's initialized throws a runtime error.
Challenge
EasyLet's build a database connection manager that demonstrates both deferred initialization and lazy initialization using late variables.
You'll create two files to organize your code:
database.dart: Define aDatabaseclass that simulates a database connection. Your class should have:- A
late String connectionStringthat will be set after the object is created - A
late String statuswith a lazy initializer that printsChecking connection...and returns'Connected' - A
connect(String connStr)method that sets theconnectionStringand printsConnection configured: [connectionString] - A
query(String sql)method that printsExecuting on [connectionString]: [sql] - A
checkStatus()method that printsStatus: [status](this will trigger the lazy initialization on first call)
- A
main.dart: Import your database class and demonstrate both uses oflate:- Create a
Databaseinstance - Print
Database object created - Call
connect()with the connection string'localhost:5432/mydb' - Call
query()with'SELECT * FROM users' - Call
checkStatus()to trigger the lazy initialization
- Create a
Notice how connectionString is set manually after object creation, while status computes its value only when first accessed.
Expected output:
Database object created
Connection configured: localhost:5432/mydb
Executing on localhost:5432/mydb: SELECT * FROM users
Checking connection...
Status: ConnectedCheat sheet
The late keyword allows you to declare non-nullable variables that will be initialized later, before they're used:
class User {
late String username;
void initialize(String name) {
username = name;
}
}
User user = User();
user.initialize('Alice');Lazy Initialization: A late variable with an initializer computes its value only when first accessed:
class Config {
late String settings = loadSettings();
String loadSettings() {
print('Loading...');
return 'default';
}
}
Config config = Config(); // loadSettings() not called yet
print(config.settings); // loadSettings() called hereWarning: Accessing a late variable before initialization throws a runtime error.
Try it yourself
import 'database.dart';
void main() {
// TODO: Create a Database instance
// TODO: Print 'Database object created'
// TODO: Call connect() with 'localhost:5432/mydb'
// TODO: Call query() with 'SELECT * FROM users'
// TODO: Call checkStatus() to trigger lazy initialization
}
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