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

  1. Basic Pattern

    • Definition: Initialize on first access

    • Example:

      class Config {
        Map<String, dynamic>? _parsed;
      
        Map<String, dynamic> get config {
          return _parsed ??= _loadConfig();
        }
      }
  2. Heavy Computation

    • Definition: Expensive operations
    • Example:
      get statistics {
        return _stats ??= _calculateStats();
      }
  3. Late Final

    • Definition: Combine with late
    • Example:
      late final _result = compute();
      get result => _result;
  4. Thread Safety

    • Definition: Consider concurrency

    • Example:

      import 'package:synchronized/synchronized.dart';
      
      final _lock = Lock();
      get value {
        return _lock.synchronized(() => _value ??= compute());
      }
  5. 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

  1. First Access: Triggers computation
  2. Subsequent Access: Returns cached value
  3. Memory: Stores computed result
  4. 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.