Skip to content

feat: add sketch.transform(Matrix4) to make pann/zoom easier #85

@escamoteur

Description

@escamoteur

As we discussed on X, this would make it way easier in combination with a InteractiveViewer to allow the user to pan/zoom

This is Claude proposal:

Scribble Transform Plan

Problem

When rotating the phone, the scribble canvas size changes but existing scribble coordinates remain absolute. This causes drawings to appear in the top-left corner instead of maintaining their relative position.

Proposed Solution

Package author will add sketch.transform(Matrix4) method. Combined with Flutter's InteractiveViewer, this provides a complete solution.

Implementation

1. Track Original Canvas Size

Store the canvas dimensions when a scribble is first created:

Size? _originalCanvasSize;

// In a LayoutBuilder or post-frame callback when scribble starts
if (_originalCanvasSize == null && scribbleNotifier.currentSketch.lines.isNotEmpty) {
  _originalCanvasSize = Size(canvasWidth, canvasHeight);
}

2. Calculate Transform Matrix on Orientation Change

When canvas size changes, compute a Matrix4 to scale and center the sketch:

Matrix4 calculateTransform(Size originalSize, Size newSize) {
  // Uniform scale to fit without stretching
  final scaleX = newSize.width / originalSize.width;
  final scaleY = newSize.height / originalSize.height;
  final scale = min(scaleX, scaleY);

  // Calculate centering offset
  final scaledWidth = originalSize.width * scale;
  final scaledHeight = originalSize.height * scale;
  final offsetX = (newSize.width - scaledWidth) / 2;
  final offsetY = (newSize.height - scaledHeight) / 2;

  return Matrix4.identity()
    ..translate(offsetX, offsetY)
    ..scale(scale, scale);
}

3. Apply Transform to Sketch

final transformedSketch = sketch.transform(matrix);

4. Wrap in InteractiveViewer

Allow user to manually pan/zoom for fine adjustments:

InteractiveViewer(
  boundaryMargin: EdgeInsets.all(20),
  minScale: 0.5,
  maxScale: 3.0,
  child: Scribble(
    notifier: scribbleNotifier,
    drawPen: true,
    drawEraser: true,
  ),
)

5. Coordinate InteractiveViewer with ScribbleNotifier

Use the existing scaleFactor property to keep pen width consistent when zoomed:

InteractiveViewer(
  onInteractionUpdate: (details) {
    scribbleNotifier.setScaleFactor(details.scale);
  },
  // ...
)

Benefits

  • Pan/zoom handled by InteractiveViewer gestures
  • Orientation changes auto-transform sketch to fit
  • User can manually adjust if auto-transform isn't perfect
  • Consistent pen width at any zoom level via scaleFactor
  • Clean separation: package handles transform, Flutter handles interaction

Dependencies

  • Waiting for sketch.transform(Matrix4) to be added to scribble package
  • Could implement our own transform function as workaround if needed

Workaround (if needed before package update)

Create a helper function to transform sketch manually:

Sketch transformSketch(Sketch sketch, Matrix4 matrix) {
  return Sketch(
    lines: sketch.lines.map((line) {
      return SketchLine(
        points: line.points.map((point) {
          final vector = matrix.transform3(Vector3(point.x, point.y, 0));
          return Point(x: vector.x, y: vector.y, pressure: point.pressure);
        }).toList(),
        color: line.color,
        width: line.width * matrix.getMaxScaleOnAxis(),
      );
    }).toList(),
  );
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions