captureBoardPng function
Future<Uint8List?>
captureBoardPng(
- GlobalKey<
State< boundaryKey, {StatefulWidget> > - double pixelRatio = 2.0,
- String? overlayText,
Captures boundaryKey (must be a RepaintBoundary) as a PNG, overlaying
overlayText (if any) on a translucent bar at the bottom of the image —
so the image carries its own level/score/date info even if the recipient
only views/saves the picture, independent of the share caption. Returns
null if the widget hasn't built yet (context is null). Kept separate from
shareBoardImage so it's testable without touching share_plus's platform
channel.
Implementation
Future<Uint8List?> captureBoardPng(
GlobalKey boundaryKey, {
double pixelRatio = 2.0,
String? overlayText,
}) async {
final ctx = boundaryKey.currentContext;
if (ctx == null) return null;
// BUG-29: type-check instead of force-casting — a key mismatched to a
// non-RepaintBoundary widget (caller integration bug) now returns the
// already-documented "no capture" result instead of an unhandled crash.
final renderObject = ctx.findRenderObject();
if (renderObject is! RenderRepaintBoundary) return null;
final board = await renderObject.toImage(pixelRatio: pixelRatio);
// BUG-29: both `board` and (when an overlay is drawn) the new composited
// image hold native pixel buffers that leak unless disposed — every
// share (score card, journey card) previously leaked at least one.
ui.Image? overlayImage;
try {
final image = (overlayText == null || overlayText.isEmpty)
? board
: (overlayImage = await _withTextOverlay(
board,
overlayText,
pixelRatio,
));
final bytes = await image.toByteData(format: ui.ImageByteFormat.png);
return bytes?.buffer.asUint8List();
} finally {
board.dispose();
overlayImage?.dispose();
}
}