Skip to content

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

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(),
  );
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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