ANTIPALSU Label template editor using flutter

editor.dart 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. import 'dart:convert';
  2. import 'dart:developer';
  3. import 'package:defer_pointer/defer_pointer.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_canvas_editor/history.dart';
  6. import 'package:flutter_canvas_editor/style/canvas_style.dart';
  7. import 'package:flutter_canvas_editor/widgets/elements.dart';
  8. import 'package:toastification/toastification.dart';
  9. import 'package:uuid/uuid.dart';
  10. import 'package:collection/collection.dart';
  11. import '../main.dart';
  12. var uuid = Uuid();
  13. class Editor extends ChangeNotifier {
  14. String editorVersion = '0.1m';
  15. String? selectedElmId;
  16. bool isEditing = false;
  17. final textboxResizerDeferredPointerHandlerLink = DeferredPointerHandlerLink();
  18. String? valueOnStartEditing;
  19. // ? Canvas State
  20. double canvasScale = 1.0;
  21. final CanvasProperty canvasProperty = CanvasProperty(
  22. width: 780,
  23. height: 1000,
  24. );
  25. TransformationController canvasTransformationController = TransformationController();
  26. void setCanvasTransformationInitialZoom(BuildContext context) {
  27. final deviceWidth = MediaQuery.of(context).size.width;
  28. canvasScale = deviceWidth / canvasProperty.width * 0.9;
  29. canvasTransformationController.value = Matrix4.identity()..scale(canvasScale);
  30. }
  31. void resetCanvasTransformationScale() {
  32. print('canvas scale $canvasScale');
  33. canvasTransformationController.value = Matrix4.identity()..scale(canvasScale);
  34. }
  35. void disposeCanvasTransformationController() {
  36. canvasTransformationController.dispose();
  37. }
  38. // List<ElementProperty> elementProperties = [
  39. // ElementProperty(
  40. // id: uuid.v4(),
  41. // valueController: TextEditingController(text: '{{QRCODE}}'),
  42. // type: ElementType.qr,
  43. // position: ElementPosition(top: 0, left: 0),
  44. // width: 80,
  45. // quarterTurns: 0,
  46. // elementKey: GlobalKey(),
  47. // qrScale: 3
  48. // )
  49. // ];
  50. // This list store all changes history, and currentCanvasState (stackState.last)
  51. List<List<ElementState>> stateStack = [
  52. // ? default state
  53. [
  54. ElementState(
  55. id: uuid.v4(),
  56. valueController: TextEditingController(text: '{{QRCODE}}'),
  57. type: ElementType.qr,
  58. position: ElementPosition(top: 0, left: 0),
  59. width: 80,
  60. quarterTurns: 0,
  61. elementKey: GlobalKey(),
  62. qrScale: 3
  63. )
  64. ]
  65. ];
  66. // current canvas state
  67. List<ElementState> get currentElementsState => stateStack.last;
  68. // ? History stack
  69. // List<CanvasHistory> undoStack = [];
  70. List<List<ElementState>> redoStack = [];
  71. void setNewElementsState(List<ElementState> newElementsState) {
  72. _setNewStateTextEdit(newElementsState);
  73. stateStack.add(newElementsState);
  74. redoStack.clear();
  75. notifyListeners();
  76. }
  77. void undo() {
  78. addRedoEntry(stateStack.last);
  79. // apply to current state
  80. // if (undoStack.last.type == CanvasHistoryModifyType.textEdit && undoStack.last.getState != elementProperties) {
  81. // undoStack.removeLast();
  82. // }
  83. // elementProperties = undoStack.last.getState;
  84. stateStack.removeLast();
  85. // unselect element
  86. if (currentElementsState.firstWhereOrNull((e) => e.id == selectedElmId) == null) {
  87. unSelectElm();
  88. }
  89. notifyListeners();
  90. }
  91. void addRedoEntry(List<ElementState> elementProperties) {
  92. redoStack.add(_cloneElementsState(elementProperties));
  93. notifyListeners();
  94. }
  95. void redo() {
  96. // undoStack.add(CanvasHistory(CanvasHistoryModifyType.redo, _cloneCurrentState(elementProperties)));
  97. stateStack.add(_cloneElementsState(redoStack.last));
  98. // apply to current state
  99. // elementProperties = redoStack.last;
  100. redoStack.removeLast();
  101. notifyListeners();
  102. }
  103. List<ElementState> _cloneElementsState(List<ElementState> elementProperties) {
  104. List<ElementState> clonedElementProperties = elementProperties.map((elementProperty) {
  105. return ElementState(
  106. id: elementProperty.id,
  107. valueController: TextEditingController(text: elementProperty.valueController.text),
  108. type: elementProperty.type,
  109. position: ElementPosition(top: elementProperty.position.top, left: elementProperty.position.left),
  110. width: elementProperty.width,
  111. quarterTurns: elementProperty.quarterTurns,
  112. elementKey: elementProperty.elementKey,
  113. qrScale: elementProperty.qrScale,
  114. fontScale: elementProperty.fontScale,
  115. variableType: elementProperty.variableType,
  116. isLocked: elementProperty.isLocked
  117. );
  118. }).toList();
  119. return clonedElementProperties;
  120. }
  121. // ? udpate canvas
  122. void updateCanvasProperty(BuildContext context, double width, double height) {
  123. canvasProperty.height = height;
  124. canvasProperty.width = width;
  125. // undoStack.clear();
  126. stateStack = [currentElementsState];
  127. redoStack.clear();
  128. _adjustOutOfBoundElement();
  129. setCanvasTransformationInitialZoom(context);
  130. notifyListeners();
  131. }
  132. void _adjustOutOfBoundElement() {
  133. for (var element in currentElementsState) {
  134. bool isOutOfBoundFromTop = (element.position.top + 10) > canvasProperty.height;
  135. bool isOutOfBoundFromLeft = (element.position.left + 10) > canvasProperty.width;
  136. // if (isOutOfBoundFromTop | isOutOfBoundFromLeft) {
  137. // element.position.top = 0;
  138. // element.position.left = 0;
  139. // }
  140. if (isOutOfBoundFromTop) {
  141. element.position.top = canvasProperty.height - (element.elementKey.currentContext?.size?.height ?? 0);
  142. }
  143. if (isOutOfBoundFromLeft) {
  144. element.position.left = canvasProperty.width - (element.elementKey.currentContext?.size?.width ?? 0);
  145. }
  146. }
  147. }
  148. // ? Primitive Element
  149. void addTextElement(){
  150. String id = uuid.v4();
  151. ElementState newElement = ElementState(
  152. id: id,
  153. valueController: TextEditingController(text: 'Double tap to edit text'),
  154. type: ElementType.text,
  155. position: ElementPosition(top: 0, left: 0),
  156. // size: ElementSize(width: 20, height: 70),
  157. width: 20,
  158. elementKey: GlobalKey(),
  159. quarterTurns: 0
  160. );
  161. // Set State
  162. // List<ElementState> newElementsState = [..._cloneElementsState(currentElementsState), newElement];
  163. // setNewElementsState(newElementsState);
  164. _setAddNewElementState(newElement);
  165. notifyListeners();
  166. selectElmById(id);
  167. }
  168. void addTextboxElement() {
  169. String id = uuid.v4();
  170. ElementState newElement = ElementState(
  171. id: id,
  172. valueController: TextEditingController(text: 'Double tap to edit text'),
  173. type: ElementType.textbox,
  174. position: ElementPosition(top: 0, left: 0),
  175. width: 70,
  176. quarterTurns: 0,
  177. elementKey: GlobalKey()
  178. );
  179. // Set State
  180. _setAddNewElementState(newElement);
  181. notifyListeners();
  182. selectElmById(id);
  183. }
  184. // ? Variable Element
  185. void addProductNameElement() {
  186. String id = uuid.v4();
  187. ElementState newElement = ElementState(
  188. id: id,
  189. valueController: TextEditingController(text: '{{PRODUCTNAME}}'),
  190. type: ElementType.textbox,
  191. variableType: ElementVariableType.productName,
  192. position: ElementPosition(top: 0, left: 0),
  193. width: 250,
  194. quarterTurns: 0,
  195. elementKey: GlobalKey()
  196. );
  197. // Set State
  198. _setAddNewElementState(newElement);
  199. notifyListeners();
  200. selectElmById(id);
  201. }
  202. void addVariantNameElement() {
  203. String id = uuid.v4();
  204. ElementState newElement = ElementState(
  205. id: id,
  206. valueController: TextEditingController(text: '{{VARIANTNAME}}'),
  207. type: ElementType.textbox,
  208. variableType: ElementVariableType.variantName,
  209. position: ElementPosition(top: 0, left: 0),
  210. width: 250,
  211. quarterTurns: 0,
  212. elementKey: GlobalKey()
  213. );
  214. // Set State
  215. _setAddNewElementState(newElement);
  216. notifyListeners();
  217. selectElmById(id);
  218. }
  219. void addProductionCodeElement() {
  220. String id = uuid.v4();
  221. ElementState newElement = ElementState(
  222. id: id,
  223. valueController: TextEditingController(text: '{{PRODUCTIONCODE}}'),
  224. type: ElementType.textbox,
  225. variableType: ElementVariableType.productionCode,
  226. position: ElementPosition(top: 0, left: 0),
  227. width: 250,
  228. quarterTurns: 0,
  229. elementKey: GlobalKey()
  230. );
  231. // Set State
  232. _setAddNewElementState(newElement);
  233. notifyListeners();
  234. selectElmById(id);
  235. }
  236. void addProductionDateElement() {
  237. String id = uuid.v4();
  238. ElementState newElement = ElementState(
  239. id: id,
  240. valueController: TextEditingController(text: '{{PRODUCTIONDATE}}'),
  241. type: ElementType.text,
  242. variableType: ElementVariableType.productionDate,
  243. position: ElementPosition(top: 0, left: 0),
  244. width: 250,
  245. quarterTurns: 0,
  246. elementKey: GlobalKey()
  247. );
  248. // Save History
  249. _setAddNewElementState(newElement);
  250. notifyListeners();
  251. selectElmById(id);
  252. }
  253. void addSerialNumberElement() {
  254. String id = uuid.v4();
  255. ElementState newElement = ElementState(
  256. id: id,
  257. valueController: TextEditingController(text: '{{SERIALNUMBER}}'),
  258. type: ElementType.text,
  259. variableType: ElementVariableType.serialNumber,
  260. position: ElementPosition(top: 0, left: 0),
  261. width: 250,
  262. quarterTurns: 0,
  263. elementKey: GlobalKey()
  264. );
  265. // Set State
  266. _setAddNewElementState(newElement);
  267. notifyListeners();
  268. selectElmById(id);
  269. }
  270. void updateElmPosition(Offset offset) {
  271. ElementState? element = selectedElm;
  272. if (element == null) return;
  273. // element.position = ElementPosition(top: offset.dy, left: offset.dx);
  274. print(offset.dx);
  275. element.position.top += offset.dy.round();
  276. element.position.left += offset.dx.round();
  277. notifyListeners();
  278. }
  279. // Reset position after drag end
  280. void resetElmPosition(ElementPosition? elementPosition) {
  281. ElementState? element = selectedElm;
  282. if (element == null) return;
  283. if (elementPosition == null) return;
  284. element.position = elementPosition;
  285. }
  286. void moveElement(Offset offset) {
  287. log('[MOVING ELEMENT]');
  288. ElementState? element = selectedElm;
  289. if (element == null) return;
  290. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  291. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  292. newElement.position.top += offset.dy.round();
  293. newElement.position.left += offset.dx.round();
  294. setNewElementsState(newElementsState);
  295. }
  296. ElementPosition getClonedElementPosition(ElementState element) {
  297. return ElementPosition(
  298. top: element.position.top,
  299. left: element.position.left
  300. );
  301. }
  302. void updateElmWitdh(double width) {
  303. ElementState? element = selectedElm;
  304. if (element == null) return;
  305. element.width = width;
  306. notifyListeners();
  307. }
  308. void commitTextboxResize(double width, double textboxDragStartWidth) {
  309. if (selectedElm == null) return;
  310. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  311. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  312. newElement.width = width;
  313. // reset previous element
  314. _resetPreviousTextboxWidth(selectedElm!, textboxDragStartWidth);
  315. setNewElementsState(newElementsState);
  316. }
  317. void _resetPreviousTextboxWidth(ElementState previousElement, double textboxDragStartWidth) {
  318. if (selectedElm == null) return;
  319. previousElement.width = textboxDragStartWidth;
  320. }
  321. void toggleLockElement() {
  322. if (selectedElm == null) return;
  323. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  324. var selectedNewElement = newElementsState.firstWhere((e) => e.id == selectedElm!.id);
  325. selectedNewElement.isLocked = !selectedNewElement.isLocked;
  326. setNewElementsState(newElementsState);
  327. // selectedElm!.isLocked = !selectedElm!.isLocked;
  328. notifyListeners();
  329. }
  330. bool shouldIgnoreTouch(String elementId) {
  331. if (elementId == selectedElmId) return false;
  332. if (selectedElmId == null) return false;
  333. return true;
  334. }
  335. void selectElmById(String id) {
  336. selectedElmId = id;
  337. valueOnStartEditing = currentElementsState.firstWhere((e) => e.id == selectedElmId).valueController.text;
  338. notifyListeners();
  339. }
  340. void enableEdit() {
  341. if (isVariableElement) return;
  342. if (selectedElm!.isLocked) {
  343. _showLockedToast('Cant modify locked element');
  344. return;
  345. }
  346. isEditing = true;
  347. notifyListeners();
  348. }
  349. void disableEdit() {
  350. isEditing = false;
  351. notifyListeners();
  352. }
  353. void unSelectElm() {
  354. selectedElmId = null;
  355. isEditing = false;
  356. valueOnStartEditing = null;
  357. notifyListeners();
  358. }
  359. bool isSelected(String id) {
  360. return selectedElmId == id;
  361. }
  362. // ? Getters
  363. String get selectedElmType {
  364. if (currentElementsState.isNotEmpty && selectedElmId != null) {
  365. final selectedElm = currentElementsState.firstWhere((element) => element.id == selectedElmId);
  366. return elementGetter(selectedElm.type);
  367. }
  368. return '';
  369. }
  370. ElementState? get selectedElm {
  371. if (currentElementsState.isNotEmpty && selectedElmId != null) {
  372. return currentElementsState.firstWhereOrNull((element) => element.id == selectedElmId);
  373. }
  374. return null;
  375. }
  376. GlobalKey? get selectedElmKey {
  377. if (currentElementsState.isNotEmpty && selectedElmId != null) {
  378. return currentElementsState.firstWhere((element) => element.id == selectedElmId).elementKey;
  379. }
  380. return null;
  381. }
  382. bool get shouldShowFontResizer {
  383. if (selectedElm == null) return false;
  384. if (![ElementType.text, ElementType.textbox].contains(selectedElm!.type)) return false;
  385. return true;
  386. }
  387. bool get shouldShowQrResizer {
  388. if (selectedElm == null) return false;
  389. if (selectedElm!.type != ElementType.qr) return false;
  390. return true;
  391. }
  392. bool get shouldShowDeleteElementButton {
  393. if (selectedElm == null) return false;
  394. if ([ElementType.qr].contains(selectedElm!.type)) return false;
  395. return true;
  396. }
  397. bool get isVariableElement {
  398. if (selectedElm == null) return false;
  399. if (selectedElm!.variableType == null) return false;
  400. return true;
  401. }
  402. // ? Element overlay
  403. bool shouldShowTextboxResizer(String elmId) {
  404. if (selectedElmId == null) return false;
  405. if (selectedElmId != elmId ) return false;
  406. if (selectedElm!.type != ElementType.textbox) return false;
  407. return true;
  408. }
  409. bool shouldShowOverlay(String elmId) {
  410. if (selectedElmId == null) return false;
  411. if (selectedElmId != elmId ) return false;
  412. if (selectedElm!.type == ElementType.qr) return false;
  413. return true;
  414. }
  415. // Toolbar
  416. bool get insertElementMode{
  417. if (selectedElm == null) return true;
  418. return false;
  419. }
  420. /// Can only rotate [ElementType.text, ElementType.textBox]
  421. void rotate() {
  422. if (selectedElm == null) return;
  423. if (selectedElm!.isLocked) {
  424. _showLockedToast('Cant rotate locked element');
  425. return;
  426. }
  427. if (![ElementType.text, ElementType.textbox].contains(selectedElm!.type)) return;
  428. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  429. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  430. if (newElement.type == ElementType.text) _rotateText(newElement);
  431. if (newElement.type == ElementType.textbox) _rotateTextBox(newElement);
  432. setNewElementsState(newElementsState);
  433. // Adjust Size
  434. // double currentElementHeight = selectedElmKey!.currentContext!.size!.height;
  435. // double currentElementWidth = selectedElmKey!.currentContext!.size!.width;
  436. // selectedElmKey!.currentContext!.size!.height = currentElementWidth;
  437. // selectedElmKey!.currentContext!.size!.width = currentElementHeight;
  438. notifyListeners();
  439. }
  440. void editText(TextEditingController controller) {
  441. if (selectedElm == null) return;
  442. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  443. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  444. newElement.valueController.text = controller.text;
  445. // newElement.valueController.selection = TextSelection.collapsed(offset: controller.selection.base.offset);
  446. setNewElementsState(newElementsState);
  447. }
  448. void setValueOnStartEditing(String text) {
  449. valueOnStartEditing = text;
  450. }
  451. // FontSize Handler
  452. void changeFontSize(int? fontSize) {
  453. if (fontSize == null) return;
  454. if (selectedElm == null) return;
  455. if (selectedElm!.isLocked) {
  456. _showLockedToast('Cant modify locked element');
  457. return;
  458. }
  459. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  460. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  461. newElement.fontScale = fontSize;
  462. setNewElementsState(newElementsState);
  463. notifyListeners();
  464. }
  465. void incrementFontSize() {
  466. if (selectedElm == null) return;
  467. final incrementTo = selectedElm!.fontScale + 1;
  468. if (selectedElm!.isLocked) {
  469. _showLockedToast('Cant modify locked element');
  470. return;
  471. }
  472. // check if value is allowed for resize
  473. if (CanvasStyle.fontSizeMap.containsKey(incrementTo)) {
  474. // // ? Save History
  475. // setNewElementsState(CanvasHistoryModifyType.resize, elementProperties);
  476. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  477. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  478. // selectedElm!.fontScale = incrementTo;
  479. newElement.fontScale = incrementTo;
  480. setNewElementsState(newElementsState);
  481. print('kepenjet increase');
  482. } else {
  483. print('cant increment');
  484. }
  485. notifyListeners();
  486. }
  487. void decrementFontSize() {
  488. if (selectedElm == null) return;
  489. final decrementTo = selectedElm!.fontScale - 1;
  490. if (selectedElm!.isLocked) {
  491. _showLockedToast('Cant modify locked element');
  492. return;
  493. }
  494. // check if value is allowed for resize
  495. if (CanvasStyle.fontSizeMap.containsKey(decrementTo)) {
  496. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  497. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  498. newElement.fontScale = decrementTo;
  499. setNewElementsState(newElementsState);
  500. } else {
  501. print('cant decrement');
  502. }
  503. }
  504. // Qr Size Handler
  505. void changeQrSize(int? fontSize) {
  506. if (fontSize == null) return;
  507. if (selectedElm == null) return;
  508. if (selectedElm!.isLocked) {
  509. _showLockedToast('Cant modify locked element');
  510. return;
  511. }
  512. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  513. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  514. // selectedElm!.qrScale = fontSize;
  515. newElement.qrScale = fontSize;
  516. setNewElementsState(newElementsState);
  517. }
  518. void incrementQrSize() {
  519. if (selectedElm == null) return;
  520. if (selectedElm!.type != ElementType.qr) return;
  521. final incrementTo = selectedElm!.qrScale! + 1;
  522. if (selectedElm!.isLocked) {
  523. _showLockedToast('Cant modify locked element');
  524. return;
  525. }
  526. // check if value is allowed for resize
  527. if (CanvasStyle.qrSizeMap.containsKey(incrementTo)) {
  528. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  529. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  530. // selectedElm!.qrScale = incrementTo;
  531. newElement.qrScale = incrementTo;
  532. setNewElementsState(newElementsState);
  533. } else {
  534. print('cant increment');
  535. }
  536. }
  537. void decrementQrSize() {
  538. if (selectedElm == null) return;
  539. if (selectedElm!.type != ElementType.qr) return;
  540. final decrementTo = selectedElm!.qrScale! - 1;
  541. if (selectedElm!.isLocked) {
  542. _showLockedToast('Cant modify locked element');
  543. return;
  544. }
  545. // check if value is allowed for resize
  546. if (CanvasStyle.qrSizeMap.containsKey(decrementTo)) {
  547. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  548. var newElement = newElementsState.firstWhere((e) => e.id == selectedElmId);
  549. // selectedElm!.qrScale = decrementTo;
  550. newElement.qrScale = decrementTo;
  551. setNewElementsState(newElementsState);
  552. } else {
  553. print('cant decrement');
  554. }
  555. notifyListeners();
  556. }
  557. // Delete Element
  558. Future<void> deleteElement(BuildContext context) async {
  559. if (selectedElm == null) return;
  560. if (selectedElm!.isLocked) {
  561. _showLockedToast('Cant delete locked element');
  562. return;
  563. }
  564. final shouldDelete = await showDialog(
  565. context: context,
  566. builder: (context) {
  567. return AlertDialog(
  568. title: Text('Delete Element ?'),
  569. content: Text('Are you sure want to delete this element ?'),
  570. actions: [
  571. TextButton(
  572. onPressed: () => Navigator.pop(context, false),
  573. child: Text('Cancel')
  574. ),
  575. TextButton(
  576. onPressed: () => Navigator.pop(context, true),
  577. child: Text('Delete')
  578. ),
  579. ],
  580. );
  581. },
  582. );
  583. if (!shouldDelete) return;
  584. // // ? Save History
  585. // setNewElementsState(CanvasHistoryModifyType.remove, elementProperties);
  586. // elementProperties.removeWhere((e) => e.id == selectedElm!.id);
  587. List<ElementState> newElementsState = _cloneElementsState(currentElementsState);
  588. log('selectedElmId: ${selectedElm!.id}');
  589. for (var i = 0; i < newElementsState.length; i++) {
  590. log('element[$i]: ${newElementsState[i].id}');
  591. }
  592. newElementsState.removeWhere((e) => e.id == selectedElm!.id);
  593. setNewElementsState(newElementsState);
  594. unSelectElm();
  595. notifyListeners();
  596. }
  597. // ? snapping element
  598. // Offset? snapToElement(Offset newPosition) {
  599. // for (var element in elementProperties) {
  600. // if ((newPosition.dx - element.))
  601. // }
  602. // }
  603. // ? Template to JSON
  604. String buildJSON() {
  605. var elementsMap = [];
  606. var canvasResultMap = {
  607. 'title': 'Test',
  608. 'width': (canvasProperty.width / 10).round(),
  609. 'height': (canvasProperty.height / 10).round(),
  610. 'content': {
  611. 'version': editorVersion,
  612. 'elements': []
  613. }
  614. };
  615. for (var element in currentElementsState) {
  616. var elementMap = {
  617. 'id': element.id,
  618. 'content': element.valueController.text,
  619. 'height': element.elementKey.currentContext?.size?.height.round() ?? 0,
  620. 'width': element.elementKey.currentContext?.size?.width.round() ?? 0,
  621. 'position': {
  622. 'top': element.position.top.round(),
  623. 'left': element.position.left.round()
  624. },
  625. 'size': _getElementSizeResult(element),
  626. 'rotation': _getElementRotationResult(element.quarterTurns),
  627. 'type': _getElementTypeResult(element)
  628. };
  629. elementsMap.add(elementMap);
  630. }
  631. (canvasResultMap['content'] as Map<String, dynamic>)['elements'] = elementsMap;
  632. final templateJsonResult = jsonEncode(canvasResultMap);
  633. print(templateJsonResult);
  634. log(templateJsonResult);
  635. return templateJsonResult;
  636. }
  637. String _getElementTypeResult(ElementState element) {
  638. switch (element.type) {
  639. case ElementType.text:
  640. return 'text';
  641. case ElementType.textbox:
  642. return 'textbox';
  643. case ElementType.qr:
  644. return 'qrcode';
  645. default:
  646. return '';
  647. }
  648. }
  649. int _getElementRotationResult(int quarterTurns) {
  650. switch (quarterTurns) {
  651. case 1:
  652. return 90;
  653. case 2:
  654. return 180;
  655. case 3:
  656. return -90;
  657. default:
  658. return 0;
  659. }
  660. }
  661. int _getElementSizeResult(ElementState element) {
  662. if (element.type == ElementType.qr) {
  663. return element.qrScale ?? 1;
  664. }
  665. return element.fontScale;
  666. }
  667. // ? Helper Method
  668. void _setAddNewElementState(ElementState newElement) {
  669. List<ElementState> newElementsState = [..._cloneElementsState(currentElementsState), newElement];
  670. setNewElementsState(newElementsState);
  671. }
  672. void _rotateText(ElementState element) {
  673. if (element.quarterTurns < 3) {
  674. element.quarterTurns += 1;
  675. } else {
  676. element.quarterTurns = 0;
  677. }
  678. }
  679. void _rotateTextBox(ElementState element) {
  680. if (element.quarterTurns == 0) {
  681. element.quarterTurns = 3;
  682. } else {
  683. element.quarterTurns = 0;
  684. }
  685. }
  686. void _showLockedToast(String titleText) {
  687. toastification.show(
  688. title: Text(titleText),
  689. description: Text('unlock to change element property'),
  690. closeButtonShowType: CloseButtonShowType.none,
  691. style: ToastificationStyle.minimal,
  692. type: ToastificationType.warning,
  693. autoCloseDuration: const Duration(seconds: 3),
  694. alignment: Alignment.bottomCenter,
  695. dragToClose: true
  696. );
  697. }
  698. void _setNewStateTextEdit(List<ElementState> newElementsState) {
  699. log(selectedElm?.id ?? 'null');
  700. if (selectedElm == null) return;
  701. var newElement = newElementsState.firstWhereOrNull((e) => e.id == selectedElmId);
  702. if (newElement != null && [ElementType.text, ElementType.textbox].contains(newElement.type)) {
  703. newElement.valueController.selection = TextSelection.collapsed(offset: selectedElm!.valueController.selection.base.offset);
  704. }
  705. }
  706. }
  707. enum SetActionType { add, remove, move, resize, lock, rotate, textEdit, redo }