Skip to content

Commit 650f537

Browse files
committed
feat: canvas page starts from center
1 parent e20bba1 commit 650f537

File tree

2 files changed

+125
-34
lines changed

2 files changed

+125
-34
lines changed

lib/features/workspace/pages/canvas_page.dart

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'package:cookethflow/features/workspace/providers/canvas_provider.dart';
33
import 'package:cookethflow/features/workspace/providers/workspace_provider.dart';
44
import 'package:flutter/material.dart';
55
import 'package:provider/provider.dart';
6-
import 'package:vector_math/vector_math_64.dart'; // Required for Vector3.
6+
import 'package:vector_math/vector_math_64.dart' as vector_math; // Required for Vector3.
77

88
class CanvasPage extends StatelessWidget {
99
const CanvasPage({super.key});
@@ -29,7 +29,7 @@ class CanvasPage extends StatelessWidget {
2929

3030
// Transform the local position (relative to InteractiveViewer's viewport)
3131
// to canvas coordinates.
32-
final Vector3 transformed = inverseTransform.transform3(Vector3(event.localPosition.dx, event.localPosition.dy, 0));
32+
final vector_math.Vector3 transformed = inverseTransform.transform3(vector_math.Vector3(event.localPosition.dx, event.localPosition.dy, 0));
3333
final Offset canvasCoordinates = Offset(transformed.x, transformed.y);
3434

3535
workspaceProvider.syncCanvasObject(canvasCoordinates);
@@ -45,32 +45,35 @@ class CanvasPage extends StatelessWidget {
4545
// with the child GestureDetector for object manipulation.
4646
// We let InteractiveViewer manage its own pan/zoom gestures.
4747
// The GestureDetector below will handle *object* interactions.
48-
child: GestureDetector(
49-
// Use onTapDown for adding new nodes when not in pointer mode,
50-
// and for initiating object selection/move/resize.
51-
onPanDown: (details) {
52-
// details.localPosition is already relative to the GestureDetector's parent (InteractiveViewer's child).
53-
// This means it's already in the canvas coordinate system!
54-
workspaceProvider.onPanDown(DragDownDetails(globalPosition: details.localPosition));
55-
},
56-
onPanUpdate: (details) {
57-
// details.localPosition and details.delta are already in canvas coordinates.
58-
workspaceProvider.onPanUpdate(DragUpdateDetails(
59-
globalPosition: details.localPosition,
60-
delta: details.delta,
61-
));
62-
},
63-
onPanEnd: workspaceProvider.onPanEnd,
64-
child: CustomPaint(
65-
// Set a large, arbitrary size for the CustomPaint.
66-
// The actual drawing will occur based on the canvas coordinates of your objects.
67-
// InteractiveViewer will handle the viewport.
68-
size: const Size(20000, 20000), // Sufficiently large "infinite" canvas
69-
painter: CanvasPainter(
70-
userCursors: workspaceProvider.userCursors,
71-
canvasObjects: workspaceProvider.canvasObjects,
72-
currentlySelectedObjectId: workspaceProvider.currentlySelectedObjectId,
73-
handleRadius: workspaceProvider.handleRadius,
48+
child: Container(
49+
color: Colors.grey[200],
50+
child: GestureDetector(
51+
// Use onTapDown for adding new nodes when not in pointer mode,
52+
// and for initiating object selection/move/resize.
53+
onPanDown: (details) {
54+
// details.localPosition is already relative to the GestureDetector's parent (InteractiveViewer's child).
55+
// This means it's already in the canvas coordinate system!
56+
workspaceProvider.onPanDown(DragDownDetails(globalPosition: details.localPosition));
57+
},
58+
onPanUpdate: (details) {
59+
// details.localPosition and details.delta are already in canvas coordinates.
60+
workspaceProvider.onPanUpdate(DragUpdateDetails(
61+
globalPosition: details.localPosition,
62+
delta: details.delta,
63+
));
64+
},
65+
onPanEnd: workspaceProvider.onPanEnd,
66+
child: CustomPaint(
67+
// Set a large, arbitrary size for the CustomPaint.
68+
// The actual drawing will occur based on the canvas coordinates of your objects.
69+
// InteractiveViewer will handle the viewport.
70+
size: const Size(20000, 20000), // Sufficiently large "infinite" canvas
71+
painter: CanvasPainter(
72+
userCursors: workspaceProvider.userCursors,
73+
canvasObjects: workspaceProvider.canvasObjects,
74+
currentlySelectedObjectId: workspaceProvider.currentlySelectedObjectId,
75+
handleRadius: workspaceProvider.handleRadius,
76+
),
7477
),
7578
),
7679
),

lib/features/workspace/providers/canvas_provider.dart

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,100 @@ import 'package:flutter/widgets.dart'; // Import for TransformationController
44

55
class CanvasProvider extends StateHandler {
66
final WorkspaceProvider _workspaceProvider;
7-
CanvasProvider(this._workspaceProvider) : super();
7+
CanvasProvider(this._workspaceProvider) : super() {
8+
// Initialize the transformation to center the view when the provider is created
9+
// We need to wait a frame for the context and size to be available,
10+
// or ideally set it after the initial build. For now, we can set a
11+
// default center based on the large canvas size.
12+
// A more robust solution might involve `WidgetsBinding.instance.addPostFrameCallback`.
13+
// For a fixed large canvas size, we can calculate the center directly.
14+
_initializeCenteredView();
15+
}
16+
17+
final TransformationController _transformationController = TransformationController();
18+
19+
TransformationController get transformationController => _transformationController;
20+
21+
// Define the size of your virtual canvas
22+
static const Size _canvasSize = Size(20000, 20000);
23+
24+
void _initializeCenteredView() {
25+
// Calculate the center of the large virtual canvas
26+
final Offset canvasCenter = Offset(_canvasSize.width / 2, _canvasSize.height / 2);
27+
28+
// Get the initial viewport size (e.g., screen size).
29+
// This will be dynamic, so for initial setup, we might approximate or
30+
// use a post-frame callback to get the actual InteractiveViewer bounds.
31+
// For now, let's assume we want to center the canvas's origin in the middle of the screen's interactive area.
32+
// However, the request is to center the *canvas itself*.
33+
// So, we need to translate the viewer so that the canvasCenter is at (0,0) in the viewport.
34+
// Or, more accurately, we want the screen's center to align with the canvas's center.
835

9-
final TransformationController _transformationController =
10-
TransformationController();
36+
// This sets the initial matrix to translate the view such that the
37+
// center of the 20000x20000 canvas is visible in the middle of the viewport.
38+
// This assumes the InteractiveViewer will occupy the full available space.
39+
// The translation needs to move the canvas's center to the viewport's center.
40+
// If the viewport is V_w x V_h, and canvas is C_w x C_h,
41+
// we want to display C_w/2, C_h/2 at V_w/2, V_h/2.
42+
// This implies a translation of (V_w/2 - C_w/2, V_h/2 - C_h/2).
43+
// However, InteractiveViewer automatically handles placing its child based on its current matrix.
44+
// If we want the *center of our very large canvas* to be at the *center of the screen*,
45+
// we need to translate the *view* by the negative of the canvas's center offset.
1146

12-
TransformationController get transformationController =>
13-
_transformationController;
47+
// Let's set the translation such that the top-left of the canvas (0,0) is moved.
48+
// If we want the center of the canvas (10000, 10000) to appear at the center of our screen (e.g., 500, 300),
49+
// then the transformation matrix needs to translate the canvas so that
50+
// (10000, 10000) maps to (500, 300).
51+
// The InteractiveViewer's transform applies to its child.
52+
// A simple way to center a large child is to translate it by half the viewport dimensions
53+
// minus half the child dimensions, or directly translate by a specific point.
54+
55+
// A common approach for centering a large canvas in InteractiveViewer:
56+
// When the InteractiveViewer first appears, it's typically showing the (0,0) of its child
57+
// at its top-left corner. To center the content, we need to apply a translation
58+
// to the InteractiveViewer's matrix.
59+
// The required translation is such that `canvasCenter` moves to `viewportCenter`.
60+
// Let's assume the viewport center is `(screenWidth / 2, screenHeight / 2)`.
61+
// The canvas center is `(canvasWidth / 2, canvasHeight / 2)`.
62+
// The translation needed for the InteractiveViewer's matrix is:
63+
// `(screenWidth / 2 - canvasWidth / 2, screenHeight / 2 - canvasHeight / 2)`
64+
// This translation will move the canvas's top-left corner such that the canvas center
65+
// aligns with the viewport center.
66+
67+
// Since we don't have direct access to `MediaQuery.of(context).size` here in the constructor,
68+
// we might need a `WidgetsBinding.instance.addPostFrameCallback` in the `CanvasPage`
69+
// or a method that can be called after the `BuildContext` is available.
70+
// For demonstration, let's just set a large initial translation to make sure
71+
// some central part of the 20000x20000 canvas is visible.
72+
// A translation of (-canvasCenter.dx + viewport_center_dx, -canvasCenter.dy + viewport_center_dy)
73+
// would be ideal if viewport size was known.
74+
75+
// Let's try to initialize it to a point that's approximately central.
76+
// If the canvas is 20000x20000, its center is 10000, 10000.
77+
// We want to translate the view *to* that point, so we need a negative translation.
78+
// The `_transformationController.value` is a `Matrix4`.
79+
// `Matrix4.translationValues(x, y, z)` creates a translation matrix.
80+
// If we want the point (X, Y) on the child to be visible at the top-left of the viewer,
81+
// the translation would be (-X, -Y).
82+
// If we want (X, Y) to be visible at the center of the viewer, it's more complex
83+
// as it depends on the viewer's size.
84+
85+
// A simpler approach for *initial centering* without knowing the viewport size
86+
// is to translate by a fixed large amount that will bring a central part of
87+
// the 20000x20000 canvas into view.
88+
// Let's assume a typical screen size and calculate the offset needed to bring
89+
// the (10000, 10000) point of the canvas to the center of a hypothetical 1000x800 screen.
90+
// Target viewport center: (500, 400)
91+
// Canvas center: (10000, 10000)
92+
// Desired translation for canvas origin (0,0) relative to viewport:
93+
// (500 - 10000, 400 - 10000) = (-9500, -9600)
94+
95+
_transformationController.value = Matrix4.translationValues(
96+
-canvasCenter.dx + 500, // Adjust 500 based on expected initial viewport width / 2
97+
-canvasCenter.dy + 400, // Adjust 400 based on expected initial viewport height / 2
98+
0,
99+
);
100+
}
14101

15102
void zoomIn() {
16103
_transformationController.value = Matrix4.identity()
@@ -27,7 +114,8 @@ class CanvasProvider extends StateHandler {
27114
}
28115

29116
void resetZoom() {
30-
_transformationController.value = Matrix4.identity();
117+
// Reset to the initially calculated centered view
118+
_initializeCenteredView();
31119
notifyListeners();
32120
}
33121

0 commit comments

Comments
 (0)