1- // lib/features/workspace/providers/workspace_provider.dart (Fully Modified)
2-
3- import 'dart:ui' ;
1+ import 'dart:ui' as ui;
42import 'dart:convert' ; // For jsonDecode/jsonEncode
53import 'dart:math' ;
64import 'package:cookethflow/core/helpers/file_helper.dart' ;
@@ -11,6 +9,7 @@ import 'package:cookethflow/core/utils/enums.dart';
119import 'package:cookethflow/core/utils/state_handler.dart' ;
1210import 'package:cookethflow/features/dashboard/providers/dashboard_provider.dart' ;
1311import 'package:cookethflow/features/models/canvas_models/canvas_object.dart' ;
12+ import 'package:cookethflow/features/models/canvas_models/canvas_painter.dart' ;
1413import 'package:cookethflow/features/models/canvas_models/objects/circle_object.dart' ;
1514import 'package:cookethflow/features/models/canvas_models/objects/connector_object.dart' ;
1615import 'package:cookethflow/features/models/canvas_models/objects/cylinder_object.dart' ;
@@ -78,8 +77,7 @@ class WorkspaceProvider extends StateHandler {
7877 String ? _connectorSourceId;
7978 Alignment ? _connectorSourceAlignment;
8079 Offset ? _connectorDragPosition;
81-
82- // Create an instance of FileServices
80+
8381 final FileServices _fileServices = FileServices ();
8482
8583 bool get isLoading => _isLoading;
@@ -118,40 +116,34 @@ class WorkspaceProvider extends StateHandler {
118116 return _tempQuillController;
119117 }
120118
121- // --- NEW EXPORT METHOD ---
122119 Future <void > exportWorkspaceAsJson () async {
123120 if (_currentWorkspace == null ) {
124121 print ("Cannot export: No workspace is currently loaded." );
125122 return ;
126123 }
127124
128125 try {
129- // 1. Gather all necessary data
130126 final workspaceData = _currentWorkspace! .toJson ();
131127 final canvasObjectsData =
132128 _canvasObjects.values.map ((obj) => obj.toJson ()).toList ();
133129
134- // 2. Structure the data into a single map
135130 final exportData = {
136131 'workspace' : {
137132 'name' : workspaceData["name" ],
138- 'data' : workspaceData["data" ]
133+ 'data' : workspaceData["data" ],
139134 },
140135 'canvasObjects' : canvasObjectsData,
141136 };
142137
143- // 3. Encode the map into a formatted JSON string
144- const jsonEncoder = JsonEncoder .withIndent (' ' ); // For pretty printing
138+ const jsonEncoder = JsonEncoder .withIndent (' ' );
145139 final jsonString = jsonEncoder.convert (exportData);
146140
147- // 4. Generate a safe and unique file name
148141 final safeWorkspaceName = _currentWorkspace! .name
149142 .replaceAll (RegExp (r'[^\w\s-]' ), '' )
150143 .replaceAll (' ' , '_' );
151144 final uniqueId = const Uuid ().v4 ().substring (0 , 8 );
152- final fileName = '${safeWorkspaceName }_$uniqueId ' ; // Service adds extension
145+ final fileName = '${safeWorkspaceName }_$uniqueId ' ;
153146
154- // 5. Use the FileServices to trigger the download
155147 final result = await _fileServices.exportFile (
156148 defaultName: fileName,
157149 jsonString: jsonString,
@@ -167,6 +159,87 @@ class WorkspaceProvider extends StateHandler {
167159 }
168160 }
169161
162+ Future <void > exportWorkspaceAsPng () async {
163+ if (_currentWorkspace == null || _canvasObjects.isEmpty) {
164+ print ("Cannot export: No workspace or content to export." );
165+ return ;
166+ }
167+ try {
168+ Rect ? contentBounds;
169+ for (final object in _canvasObjects.values) {
170+ if (contentBounds == null ) {
171+ contentBounds = object.getBounds ();
172+ } else {
173+ contentBounds = contentBounds.expandToInclude (object.getBounds ());
174+ }
175+ }
176+
177+ if (contentBounds == null ) {
178+ print ("No objects on canvas to export." );
179+ return ;
180+ }
181+
182+ const double padding = 50.0 ;
183+ final imageBounds = Rect .fromLTRB (
184+ contentBounds.left - padding,
185+ contentBounds.top - padding,
186+ contentBounds.right + padding,
187+ contentBounds.bottom + padding,
188+ );
189+
190+ final recorder = ui.PictureRecorder ();
191+ final canvas = Canvas (recorder);
192+
193+ canvas.translate (- imageBounds.left, - imageBounds.top);
194+
195+ final backgroundPaint = Paint ()..color = _currentWorkspaceColor;
196+ canvas.drawRect (
197+ Rect .fromLTWH (0 , 0 , imageBounds.width, imageBounds.height),
198+ backgroundPaint);
199+
200+ final painter = CanvasPainter (
201+ canvasObjects: _canvasObjects,
202+ userCursors: {},
203+ currentlySelectedObjectId: null ,
204+ interactionMode: InteractionMode .none,
205+ handleRadius: 0 ,
206+ connectionPointRadius: _connectionPointRadius,
207+ connectorDragPosition: null ,
208+ connectorSourceId: null ,
209+ connectorSourceAlignment: null ,
210+ );
211+ painter.paint (canvas, imageBounds.size);
212+
213+ final picture = recorder.endRecording ();
214+ final img = await picture.toImage (
215+ imageBounds.width.toInt (),
216+ imageBounds.height.toInt (),
217+ );
218+ final byteData = await img.toByteData (format: ui.ImageByteFormat .png);
219+ final pngBytes = byteData! .buffer.asUint8List ();
220+
221+ final safeWorkspaceName = _currentWorkspace! .name
222+ .replaceAll (RegExp (r'[^\w\s-]' ), '' )
223+ .replaceAll (' ' , '_' );
224+ final uniqueId = const Uuid ().v4 ().substring (0 , 8 );
225+ final fileName = '${safeWorkspaceName }_$uniqueId ' ;
226+
227+ final result = await _fileServices.exportPNG (
228+ defaultName: fileName,
229+ pngBytes: pngBytes,
230+ );
231+
232+ if (result == 'success' ) {
233+ print ("Workspace exported successfully as $fileName .png" );
234+ } else {
235+ print ("PNG export failed or was cancelled: $result " );
236+ }
237+ } catch (e) {
238+ print ("An error occurred during PNG export: $e " );
239+ }
240+ }
241+
242+
170243 void setStickyNoteMode (Color color) {
171244 _currentMode = DrawMode .stickyNote;
172245 _nextObjectColor = color;
@@ -279,7 +352,7 @@ class WorkspaceProvider extends StateHandler {
279352 }
280353 }
281354 }
282-
355+
283356 if (payload['deleted_ids' ] != null ) {
284357 final List <String > deletedIds = List <String >.from (
285358 payload['deleted_ids' ],
@@ -1000,4 +1073,5 @@ class WorkspaceProvider extends StateHandler {
10001073 return a == b;
10011074 }
10021075 }
1003- }
1076+ }
1077+
0 commit comments