What is the difference between normal constructor vs Factory constructor proviide me with some real world example ?
Answer
Overview
A normal (generative) constructor always creates a new instance of the class. A factory constructor can return an existing instance, return a cache, or return a subtype.
Normal Constructor
Always creates a NEW instance. Cannot return an existing object or subtype.
dartclass Logger { final String name; Logger(this.name); // Normal constructor -- always new object } // Every call creates a NEW instance final log1 = Logger('auth'); // New object final log2 = Logger('auth'); // Another NEW object -- different instance! print(identical(log1, log2)); // false -- different objects
Factory Constructor
Can control what is returned -- existing instance, subtype, or new object with complex logic.
Use Case 1: Singleton Pattern
dartclass AppDatabase { static AppDatabase? _instance; AppDatabase._(); // Private constructor // Factory returns the same instance every time factory AppDatabase.getInstance() { _instance ??= AppDatabase._(); return _instance!; } Future<void> query(String sql) async { ... } } // Usage final db1 = AppDatabase.getInstance(); final db2 = AppDatabase.getInstance(); print(identical(db1, db2)); // true -- same instance
Use Case 2: fromJson (Most Common)
dartclass User { final String name; final int age; final String role; // Normal constructor User({required this.name, required this.age, required this.role}); // Factory -- creates instance from JSON with validation/mapping factory User.fromJson(Map<String, dynamic> json) { return User( name: json['name'] as String, age: (json['age'] as num).toInt(), role: json['role'] as String? ?? 'user', ); } } // Usage final json = {'name': 'Alice', 'age': 28}; final user = User.fromJson(json);
Use Case 3: Return Subtype
dartabstract class Animal { String get sound; // Factory returns different subtypes based on input factory Animal.create(String type) { switch (type) { case 'dog': return Dog(); case 'cat': return Cat(); default: throw ArgumentError('Unknown animal: $type'); } } } class Dog extends Animal { String get sound => 'Woof'; } class Cat extends Animal { String get sound => 'Meow'; } // Returns Dog or Cat -- not Animal (abstract) final animal = Animal.create('dog'); print(animal.sound); // Woof
Use Case 4: Caching
dartclass ColorPalette { static final Map<String, ColorPalette> _cache = {}; final String name; ColorPalette._(this.name); factory ColorPalette(String name) { // Return cached instance if exists return _cache.putIfAbsent(name, () => ColorPalette._(name)); } } final a = ColorPalette('dark'); final b = ColorPalette('dark'); print(identical(a, b)); // true -- returned from cache
Key Differences
| Feature | Normal Constructor | Factory Constructor |
|---|---|---|
| Always new instance | Yes | Not necessarily |
| Can return subtype | No | Yes |
| Can return cached | No | Yes |
| Uses text | Yes | No |
| Late final fields | Allowed | Not allowed |
| Keyword | text | text |
Common Real-World Pattern:
is the most common factory in Flutter -- it parses JSON into a Dart object.textfactory User.fromJson(json)