clone method Null safety

Image clone()

Creates a disposable handle to this image.

Holders of an Image must dispose of the image when they no longer need to access it or draw it. However, once the underlying image is disposed, it is no longer possible to use it. If a holder of an image needs to share access to that image with another object or method, clone creates a duplicate handle. The underlying image will only be disposed once all outstanding handles are disposed. This allows for safe sharing of image references while still disposing of the underlying resources when all consumers are finished.

It is safe to pass an Image handle to another object or method if the current holder no longer needs it.

To check whether two Image references are referring to the same underlying image memory, use isCloneOf rather than the equality operator or identical.

The following example demonstrates valid usage.

import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';

Future<Image> _loadImage(int width, int height) {
  final Completer<Image> completer = Completer<Image>();
  decodeImageFromPixels(
    Uint8List.fromList(List<int>.filled(width * height * 4, 0xFF)),
    width,
    height,
    PixelFormat.rgba8888,
    // Don't worry about disposing or cloning this image - responsibility
    // is transferred to the caller, and that is safe since this method
    // will not touch it again.
    (Image image) => completer.complete(image),
  );
  return completer.future;
}

Future<void> main() async {
  final Image image = await _loadImage(5, 5);
  // Make sure to clone the image, because MyHolder might dispose it
  // and we need to access it again.
  final MyImageHolder holder = MyImageHolder(image.clone());
  final MyImageHolder holder2 = MyImageHolder(image.clone());
  // Now we dispose it because we won't need it again.
  image.dispose();

  final PictureRecorder recorder = PictureRecorder();
  final Canvas canvas = Canvas(recorder);

  holder.draw(canvas);
  holder.dispose();

  canvas.translate(50, 50);
  holder2.draw(canvas);
  holder2.dispose();
}

class MyImageHolder {
  MyImageHolder(this.image);

  final Image image;

  void draw(Canvas canvas) {
    canvas.drawImage(image, Offset.zero, Paint());
  }

  void dispose() => image.dispose();
}

The returned object behaves identically to this image. Calling dispose on it will only dispose the underlying native resources if it is the last remaining handle.

Implementation

Image clone() {
  if (_disposed) {
    throw StateError(
      'Cannot clone a disposed image.\n'
      'The clone() method of a previously-disposed Image was called. Once an '
      'Image object has been disposed, it can no longer be used to create '
      'handles, as the underlying data may have been released.'
    );
  }
  assert(!_image._disposed);
  return Image._(_image, width, height);
}