Closed ProZhar closed 2 years ago
Or maybe some like this:
For save:
mixin MyDisposePolicy implements DisposePolicy {
@override
Map<String, dynamic> disposeDiagramEditor() {
canvasWriter.saveToJson();
}
}
For init:
mixin MyInitPolicy implements InitPolicy {
@override
initializeDiagramEditor() {
canvasWriter.loadFromJson(json);
}
}
Hello, first of all I want to thank you for using this package. It's a honor to see that somebody uses it.
I've been already thinking about this feature to be implemented in the package. Some day in the future it can be there but I am afraid there can be some yet unknown issues with the dynamic data. It would be really nice if I manage to implement the serialization that is compatible with any already existing diagram editor file format.
However, I am sure you can implement simple json serialization outside the package in your code for now.
I did not plan to spend time with it today... but it happed 😅 It's not tested much but it should work. You can try it now: diagram_editor 0.1.2.
I would like to hear your feedback then. Thanks.
I have already done this too 😅 I just put it here, maybe it will need you. All work fine with all data even canvasState, components, links properties and dynamic data with different types.
///Allows you to create and save editor data to json
mixin JsonPolicy on BasePolicySet {
initializeFromJson(Map<String, dynamic> json) {
if (json['canvasState'] != null) {
JsonCanvasStateReader.fromJson(json['canvasState'], this);
}
if (json['components'] != null) {
canvasReader.model.canvasModel.components =
HashMap<String, ComponentData>.from((json['components'] as Map).map(
(id, jsonData) => MapEntry(
id,
JsonComponentData.fromJson(
jsonData, (json) => MyComponentData.fromJson(json)))));
}
if (json['links'] != null) {
canvasReader.model.canvasModel.links = HashMap<String, LinkData>.from(
(json['links'] as Map).map((id, jsonData) =>
MapEntry(
id,
JsonLinkData.fromJson(
jsonData, (json) => MyLinkData.fromJson(json)))));
}
canvasWriter.state.updateCanvas();
}
Map<String, dynamic> toJson() {
CanvasStateReader canvasStateReader = canvasReader.state;
CanvasModel canvasModel = canvasReader.model.canvasModel;
HashMap<String, ComponentData> components =
canvasModel.components;
HashMap<String, LinkData> links = canvasModel.links;
final data = <String, dynamic>{};
data['canvasState'] = canvasStateReader.toJson();
data['components'] = components
.map((id, componentData) => MapEntry(id, componentData.toJson()));
data['links'] =
links.map((id, linkData) => MapEntry(id, linkData.toJson()));
return data;
}
}
extension JsonCanvasStateReader on CanvasStateReader {
static fromJson(Map<String, dynamic> json, JsonPolicy jsonPolicy) {
jsonPolicy.canvasWriter.state.setPosition(JsonOffset.fromJson(json['position']));
jsonPolicy.canvasWriter.state.setScale(json['scale'] as double);
jsonPolicy.canvasReader.state.canvasState.mouseScaleSpeed = json['mouseScaleSpeed'] as double;
jsonPolicy.canvasWriter.state.setMaxScale(json['maxScale'] as double);
jsonPolicy.canvasWriter.state.setMinScale(json['minScale'] as double);
jsonPolicy.canvasWriter.state.setCanvasColor(Color(json['color'] as int));
}
Map<String, dynamic> toJson() => {
'position': position.toJson(),
'scale': scale,
'mouseScaleSpeed': mouseScaleSpeed,
'maxScale': maxScale,
'minScale': minScale,
'color': color.value,
};
}
extension JsonOffset on Offset {
static Offset fromJson(Map<String, dynamic> json) => Offset(
json['dx'] as double,
json['dy'] as double,
);
Map<String, dynamic> toJson() => {'dx': dx, 'dy': dy};
}
extension JsonSize on Size {
static Size fromJson(Map<String, dynamic> json) => Size(
json['width'] as double,
json['height'] as double,
);
Map<String, dynamic> toJson() => {'width': width, 'height': height};
}
extension JsonLinkStyle on LinkStyle {
static LinkStyle fromJson(Map<String, dynamic> json) => LinkStyle(
lineType: LineType.values.firstWhere((e) => e.name == json['lineType']),
arrowType:
ArrowType.values.firstWhere((e) => e.name == json['arrowType']),
backArrowType:
ArrowType.values.firstWhere((e) => e.name == json['backArrowType']),
arrowSize: json['arrowSize'] as double,
backArrowSize: json['backArrowSize'] as double,
lineWidth: json['lineWidth'] as double,
color: Color(json['color'] as int),
);
Map<String, dynamic> toJson() => {
'lineType': lineType.name,
'arrowType': arrowType.name,
'backArrowType': backArrowType.name,
'arrowSize': arrowSize,
'backArrowSize': backArrowSize,
'lineWidth': lineWidth,
'color': color.value,
};
}
extension JsonLinkData on LinkData {
static LinkData fromJson(Map<String, dynamic> json,
dynamic Function(Map<String, dynamic> json) createCustomLinkData) {
dynamic customLinkData;
switch (json['data'].runtimeType) {
case Null:
customLinkData = null;
break;
case bool:
customLinkData = json['data'] as bool;
break;
case int:
customLinkData = json['data'] as int;
break;
case double:
customLinkData = json['data'] as double;
break;
case String:
customLinkData = json['data'] as String;
break;
default:
try {
customLinkData = createCustomLinkData(json['data']);
} catch (e) {
throw FlutterError(
'$e\n->Your custom link data class must implement fromJson(Map<String, dynamic> json) named constructor');
}
}
return LinkData(
id: json['id'] as String,
sourceComponentId: json['sourceComponentId'] as String,
targetComponentId: json['targetComponentId'] as String,
linkStyle:
JsonLinkStyle.fromJson(json['linkStyle'] as Map<String, dynamic>),
linkPoints: (json['linkPoints'] as List<dynamic>)
.map((e) => JsonOffset.fromJson(e))
.toList(),
data: customLinkData,
);
}
Map<String, dynamic> toJson() {
dynamic customLinkData;
switch (data.runtimeType) {
case Null:
case bool:
case int:
case double:
case String:
customLinkData = data;
break;
default:
try {
customLinkData = data.toJson();
} catch (e) {
throw FlutterError(
'$e\n->Your custom link data class must implement toJson() method');
}
}
return <String, dynamic>{
'id': id,
'sourceComponentId': sourceComponentId,
'targetComponentId': targetComponentId,
'linkStyle': linkStyle.toJson(),
'linkPoints': linkPoints.map((offset) => offset.toJson()).toList(),
'areJointsVisible': areJointsVisible,
'data': customLinkData,
};
}
}
extension JsonConnection on Connection {
static Connection fromJson(Map<String, dynamic> json) {
return json['type'] == 'ConnectionIn'
? ConnectionIn(
connectionId: json['connectionId'],
otherComponentId: json['otherComponentId'],
)
: ConnectionOut(
connectionId: json['connectionId'],
otherComponentId: json['otherComponentId'],
);
}
Map<String, dynamic> toJson() => {
'type': runtimeType.toString(),
'connectionId': connectionId,
'otherComponentId': otherComponentId,
};
}
extension JsonComponentData on ComponentData {
static ComponentData fromJson(Map<String, dynamic> json,
dynamic Function(Map<String, dynamic> json) createCustomComponentData) {
dynamic customComponentData;
switch (json['data'].runtimeType) {
case Null:
customComponentData = null;
break;
case bool:
customComponentData = json['data'] as bool;
break;
case int:
customComponentData = json['data'] as int;
break;
case double:
customComponentData = json['data'] as double;
break;
case String:
customComponentData = json['data'] as String;
break;
default:
try {
customComponentData = createCustomComponentData(json['data']);
} catch (e) {
throw FlutterError(
'$e\n->Your custom component data class must implement fromJson(Map<String, dynamic> json) named constructor');
}
}
return ComponentData(
id: json['id'] as String,
position: JsonOffset.fromJson(json['position']),
size: JsonSize.fromJson(json['size']),
minSize: JsonSize.fromJson(json['minSize']),
type: json['type'] as String?,
data: customComponentData,
)
..zOrder = json['zOrder'] as int
..parentId = (json['parentId'] as String?)
..childrenIds.addAll((json['childrenIds'] as List).map((e) => e as String))
..connections.addAll(
(json['connections'] as List).map((e) => JsonConnection.fromJson(e)));
}
Map<String, dynamic> toJson() {
dynamic customComponentData;
switch (data.runtimeType) {
case Null:
case bool:
case int:
case double:
case String:
customComponentData = data;
break;
default:
try {
customComponentData = data.toJson();
} catch (e) {
throw FlutterError(
'$e\n->Your custom component data class must implement toJson() method');
}
}
return <String, dynamic>{
'id': id,
'position': position.toJson(),
'size': size.toJson(),
'minSize': minSize.toJson(),
'type': type,
'zOrder': zOrder,
'parentId': parentId,
'childrenIds': childrenIds,
'connections': connections.map((e) => e.toJson()).toList(),
'data': customComponentData,
};
}
}
/// A place where you can init the canvas or your diagram
/// (eg. load an existing diagram).
mixin MyInitPolicy implements InitPolicy, JsonPolicy {
@override
initializeDiagramEditor() {
initializeFromJson(... Your Map<String, dynamic> json put here...);
}
}
On the weekend I will try a new version and write feedback then. Thanks.
In my code version a json look like this, even linkPoints added
{
"canvasState": {
"position": {
"dx": -56.0,
"dy": 85.0
},
"scale": 1.0,
"mouseScaleSpeed": 0.8,
"maxScale": 8.0,
"minScale": 0.1,
"color": 4292927712
},
"components": {
"d404a631-f15d-48bf-99fc-967013967eac": {
"id": "d404a631-f15d-48bf-99fc-967013967eac",
"position": {
"dx": 418.0,
"dy": 365.8703703703704
},
"size": {
"width": 270.0,
"height": 55.0
},
"minSize": {
"width": 40.0,
"height": 40.0
},
"type": "main_actor",
"zOrder": 3,
"parentId": null,
"childrenIds": [],
"connections": [
{
"type": "ConnectionIn",
"connectionId": "3ce7b335-68a8-4f42-839e-d827ba8c3ea8",
"otherComponentId": "567f3950-10c0-410c-b539-e8bdbb012a3e"
},
{
"type": "ConnectionOut",
"connectionId": "787d2824-e17c-4783-a879-98290b57a54d",
"otherComponentId": "cb779772-7189-4a94-95df-71fc8e0eb91f"
}
],
"data": {
"text": "33333"
}
},
"cb779772-7189-4a94-95df-71fc8e0eb91f": {
"id": "cb779772-7189-4a94-95df-71fc8e0eb91f",
"position": {
"dx": 453.0,
"dy": 19.5
},
"size": {
"width": 270.0,
"height": 55.0
},
"minSize": {
"width": 40.0,
"height": 40.0
},
"type": "text",
"zOrder": 1,
"parentId": null,
"childrenIds": [],
"connections": [
{
"type": "ConnectionOut",
"connectionId": "36cc3e98-8635-4475-9b1f-8cc54394ff53",
"otherComponentId": "567f3950-10c0-410c-b539-e8bdbb012a3e"
},
{
"type": "ConnectionIn",
"connectionId": "787d2824-e17c-4783-a879-98290b57a54d",
"otherComponentId": "d404a631-f15d-48bf-99fc-967013967eac"
}
],
"data": {
"text": "11111"
}
},
"567f3950-10c0-410c-b539-e8bdbb012a3e": {
"id": "567f3950-10c0-410c-b539-e8bdbb012a3e",
"position": {
"dx": 659.0,
"dy": 201.68518518518522
},
"size": {
"width": 270.0,
"height": 55.0
},
"minSize": {
"width": 40.0,
"height": 40.0
},
"type": "actor",
"zOrder": 2,
"parentId": null,
"childrenIds": [],
"connections": [
{
"type": "ConnectionIn",
"connectionId": "36cc3e98-8635-4475-9b1f-8cc54394ff53",
"otherComponentId": "cb779772-7189-4a94-95df-71fc8e0eb91f"
},
{
"type": "ConnectionOut",
"connectionId": "3ce7b335-68a8-4f42-839e-d827ba8c3ea8",
"otherComponentId": "d404a631-f15d-48bf-99fc-967013967eac"
}
],
"data": {
"text": "2222"
}
}
},
"links": {
"3ce7b335-68a8-4f42-839e-d827ba8c3ea8": {
"id": "3ce7b335-68a8-4f42-839e-d827ba8c3ea8",
"sourceComponentId": "567f3950-10c0-410c-b539-e8bdbb012a3e",
"targetComponentId": "d404a631-f15d-48bf-99fc-967013967eac",
"linkStyle": {
"lineType": "solid",
"arrowType": "pointedArrow",
"backArrowType": "none",
"arrowSize": 5.0,
"backArrowSize": 5.0,
"lineWidth": 1.5,
"color": 4278190080
},
"linkPoints": [
{
"dx": 753.6339950372209,
"dy": 256.6851851851852
},
{
"dx": 593.3660049627791,
"dy": 365.8703703703704
}
],
"areJointsVisible": false,
"data": {
"startLabel": "",
"endLabel": ""
}
},
"36cc3e98-8635-4475-9b1f-8cc54394ff53": {
"id": "36cc3e98-8635-4475-9b1f-8cc54394ff53",
"sourceComponentId": "cb779772-7189-4a94-95df-71fc8e0eb91f",
"targetComponentId": "567f3950-10c0-410c-b539-e8bdbb012a3e",
"linkStyle": {
"lineType": "solid",
"arrowType": "pointedArrow",
"backArrowType": "none",
"arrowSize": 5.0,
"backArrowSize": 5.0,
"lineWidth": 1.5,
"color": 4278190080
},
"linkPoints": [
{
"dx": 619.0947347021752,
"dy": 74.5
},
{
"dx": 762.9052652978247,
"dy": 201.68518518518522
}
],
"areJointsVisible": false,
"data": {
"startLabel": "",
"endLabel": ""
}
},
"787d2824-e17c-4783-a879-98290b57a54d": {
"id": "787d2824-e17c-4783-a879-98290b57a54d",
"sourceComponentId": "d404a631-f15d-48bf-99fc-967013967eac",
"targetComponentId": "cb779772-7189-4a94-95df-71fc8e0eb91f",
"linkStyle": {
"lineType": "solid",
"arrowType": "pointedArrow",
"backArrowType": "none",
"arrowSize": 5.0,
"backArrowSize": 5.0,
"lineWidth": 1.5,
"color": 4278190080
},
"linkPoints": [
{
"dx": 530.6161470163122,
"dy": 365.8703703703704
},
{
"dx": 407.0,
"dy": 214.0
},
{
"dx": 569.0,
"dy": 217.0
},
{
"dx": 584.9264705882354,
"dy": 74.5
}
],
"areJointsVisible": true,
"data": {
"startLabel": "",
"endLabel": ""
}
}
}
}
Hi. My feedback - I have added a pull request.
Hi is it possible to add toJson() method and factory fromJson() to all your models: