16 Class Init Flow
class User {
String name;
String email;
String displayName;
DateTime createdAt;bool isActive;
// Constructor with initializer list AND constructor body
this.name, this.email) : displayName = name.toUpperCase() {
User(// Constructor body - runs after initializer list
= DateTime.now();
createdAt = true;
isActive 'User ${displayName} created at ${createdAt}');
print(}
}
16.1 Class Structure Overview
User Class
├── Instance Variables
│ ├── name (String)
│ ├── email (String)
│ ├── displayName (String)
│ ├── createdAt (DateTime)
│ └── isActive (bool)
└── Constructor
├── Parameters: name, email
├── Initializer List: displayName = name.toUpperCase()
└── Constructor Body: createdAt, isActive, print statement
16.2 Detailed Initialization Flow
When you create a new User
object, here’s the complete sequence:
Step 1: Memory Allocation - Dart allocates memory for the new User
instance - All instance variables are initially null/uninitialized
Step 2: Parameter Assignment - this.name = name
(from parameter) - this.email = email
(from parameter)
Step 3: Initializer List Execution - displayName = name.toUpperCase()
runs - This happens before the constructor body - name
is already available, so we can safely use it
Step 4: Constructor Body Execution - createdAt = DateTime.now()
runs - isActive = true
runs - print('User ${displayName} created at ${createdAt}')
runs - All fields from initializer list are already available here
Step 5: Object Ready - All fields are now initialized and the object is ready to use
16.3 Visual Flow Diagram
User("Sarah", "sarah@email.com")
↓
Memory allocated
[name: null, email: null, displayName: null, createdAt: null, isActive: null]
↓
Parameter assignment:
this.name = "Sarah"
this.email = "sarah@email.com"
↓
Initializer list:
displayName = "Sarah".toUpperCase()
displayName = "SARAH"
↓
Constructor body:
createdAt = DateTime.now() // e.g., 2025-07-18 14:30:25
isActive = true
print('User SARAH created at 2025-07-18 14:30:25')
↓
Object ready:
[name: "Sarah", email: "sarah@email.com", displayName: "SARAH",
createdAt: 2025-07-18 14:30:25, isActive: true]
16.4 Key Differences with Constructor Body
Initializer List vs Constructor Body:
- Initializer List (
: displayName = name.toUpperCase()
):- Runs first
- Direct field assignment
- Must be compile-time deterministic expressions
- Cannot contain complex logic or method calls
- Constructor Body (
{ ... }
):- Runs after initializer list
- Can contain any Dart code
- Can call methods, use conditionals, loops
- Can access all previously initialized fields
16.5 Usage Example
void main() {
var user = User("Bob", "bob@example.com");
// Output: User BOB created at 2025-07-18 14:30:25.123456
.name); // "Bob"
print(user.email); // "bob@example.com"
print(user.displayName); // "BOB"
print(user.createdAt); // 2025-07-18 14:30:25.123456
print(user.isActive); // true
print(user}
16.6 When to Use Each Approach
Use Initializer List for: - Simple field calculations based on parameters - Required field initialization - final
field initialization
Use Constructor Body for: - Complex initialization logic - Method calls - Conditional initialization - Logging or side effects (like our print statement)
This pattern is very common in Flutter for widgets and data models where you need both simple field mapping and complex setup logic.