Lazy Getters
Lazy Getters
About
- Late Initialization: Compute on first access
- Memoization: Cache results
- Performance: Avoid unnecessary computation
- Syntax: Regular getter with caching
Main Topics
-
Basic Pattern
-
Definition: Initialize on first access
-
Example:
class Config { Map<String, dynamic>? _parsed; Map<String, dynamic> get config { return _parsed ??= _loadConfig(); } }
-
-
Heavy Computation
- Definition: Expensive operations
- Example:
get statistics { return _stats ??= _calculateStats(); }
-
Late Final
- Definition: Combine with late
- Example:
late final _result = compute(); get result => _result;
-
Thread Safety
-
Definition: Consider concurrency
-
Example:
import 'package:synchronized/synchronized.dart'; final _lock = Lock(); get value { return _lock.synchronized(() => _value ??= compute()); }
-
-
Cache Invalidation
- Definition: Reset when needed
- Example:
void resetCache() { _cachedValue = null; }
How to Use
- Expensive Data: Defer computation
- Rarely Used: Avoid upfront cost
- Immutable Data: Safe to cache
- Memory: Be mindful of large caches
How It Works
- First Access: Triggers computation
- Subsequent Access: Returns cached value
- Memory: Stores computed result
- Lifetime: Same as instance
Example:
class ImageLoader {
late final _image = _loadImage();
Image get image => _image;
Future<Image> _loadImage() async {
// Network request
}
}
Conlusion
Lazy getters provide an efficient way to defer expensive computations until they’re actually needed, while ensuring the computation only happens once. This pattern is particularly valuable for resource-intensive operations and initialization of rarely-used properties.