123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829 |
- import 'dart:convert';
- import 'dart:developer';
- import 'package:defer_pointer/defer_pointer.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_canvas_editor/history.dart';
- import 'package:flutter_canvas_editor/style/canvas_style.dart';
- import 'package:flutter_canvas_editor/widgets/elements.dart';
- import 'package:toastification/toastification.dart';
- import 'package:uuid/uuid.dart';
- import 'package:collection/collection.dart';
- import '../main.dart';
- var uuid = Uuid();
- class Editor extends ChangeNotifier {
- String editorVersion = '0.1m';
- String? selectedElmId;
-
- bool isEditing = false;
- final textboxResizerDeferredPointerHandlerLink = DeferredPointerHandlerLink();
- // ? Canvas State
- double canvasScale = 1.0;
- final CanvasProperty canvasProperty = CanvasProperty(
- width: 780,
- height: 1000,
- );
- TransformationController canvasTransformationController = TransformationController();
- void setCanvasTransformationInitialZoom(BuildContext context) {
- final deviceWidth = MediaQuery.of(context).size.width;
- canvasScale = deviceWidth / canvasProperty.width * 0.9;
- canvasTransformationController.value = Matrix4.identity()..scale(canvasScale);
- }
- void resetCanvasTransformationScale() {
- print('canvas scale $canvasScale');
- canvasTransformationController.value = Matrix4.identity()..scale(canvasScale);
- }
- void disposeCanvasTransformationController() {
- canvasTransformationController.dispose();
- }
- List<ElementProperty> elementProperties = [
- ElementProperty(
- id: uuid.v4(),
- valueController: TextEditingController(text: '{{QRCODE}}'),
- type: ElementType.qr,
- position: ElementPosition(top: 0, left: 0),
- width: 80,
- quarterTurns: 0,
- elementKey: GlobalKey(),
- qrScale: 3
- )
- ];
- // ? History stack
- List<CanvasHistory> undoStack = [];
- List<List<ElementProperty>> redoStack = [];
- void addUndoEntry(CanvasHistoryModifyType canvasHistoryType, List<ElementProperty> elementProperties) {
- undoStack.add(CanvasHistory(canvasHistoryType, _cloneCurrentState(elementProperties)));
- redoStack.clear();
- notifyListeners();
- }
- void undo() {
- addRedoEntry(elementProperties);
- // apply to current state
- if (undoStack.last.type == CanvasHistoryModifyType.textEdit && undoStack.last.getState != elementProperties) {
- undoStack.removeLast();
- }
- elementProperties = undoStack.last.getState;
- // unselect element
- if (elementProperties.firstWhereOrNull((e) => e.id == selectedElmId) == null) {
- unSelectElm();
- }
- undoStack.removeLast();
- notifyListeners();
- }
- void addRedoEntry(List<ElementProperty> elementProperties) {
- redoStack.add(_cloneCurrentState(elementProperties));
- notifyListeners();
- }
- void redo() {
- undoStack.add(CanvasHistory(CanvasHistoryModifyType.redo, _cloneCurrentState(elementProperties)));
-
- // apply to current state
- elementProperties = redoStack.last;
- redoStack.removeLast();
- notifyListeners();
- }
- List<ElementProperty> _cloneCurrentState(List<ElementProperty> elementProperties) {
- List<ElementProperty> clonedElementProperties = elementProperties.map((elementProperty) {
- return ElementProperty(
- id: elementProperty.id,
- valueController: TextEditingController(text: elementProperty.valueController.text),
- type: elementProperty.type,
- position: ElementPosition(top: elementProperty.position.top, left: elementProperty.position.left),
- width: elementProperty.width,
- quarterTurns: elementProperty.quarterTurns,
- elementKey: elementProperty.elementKey,
- qrScale: elementProperty.qrScale,
- fontScale: elementProperty.fontScale,
- variableType: elementProperty.variableType,
- isLocked: elementProperty.isLocked
- );
- }).toList();
- return clonedElementProperties;
- }
- // ? udpate canvas
- void updateCanvasProperty(BuildContext context, double width, double height) {
- canvasProperty.height = height;
- canvasProperty.width = width;
-
- undoStack.clear();
- redoStack.clear();
- _adjustOutOfBoundElement();
- setCanvasTransformationInitialZoom(context);
- notifyListeners();
- }
- void _adjustOutOfBoundElement() {
- for (var element in elementProperties) {
- bool isOutOfBoundFromTop = (element.position.top + 10) > canvasProperty.height;
- bool isOutOfBoundFromLeft = (element.position.left + 10) > canvasProperty.width;
- // if (isOutOfBoundFromTop | isOutOfBoundFromLeft) {
- // element.position.top = 0;
- // element.position.left = 0;
- // }
- if (isOutOfBoundFromTop) {
- element.position.top = canvasProperty.height - (element.elementKey.currentContext?.size?.height ?? 0);
- }
- if (isOutOfBoundFromLeft) {
- element.position.left = canvasProperty.width - (element.elementKey.currentContext?.size?.width ?? 0);
- }
-
- }
- }
- void populateElement(List<ElementProperty> elementProperties) {
- this.elementProperties.addAll(elementProperties);
- // _reservedIdUntil = this.elementProperties.length - 1;
- notifyListeners();
- }
- void addTextElement(){
- String id = uuid.v4();
- ElementProperty element = ElementProperty(
- id: id,
- valueController: TextEditingController(text: 'Double tap to edit text'),
- type: ElementType.text,
- position: ElementPosition(top: 0, left: 0),
- // size: ElementSize(width: 20, height: 70),
- width: 20,
- elementKey: GlobalKey(),
- quarterTurns: 0
- );
- // Save History
- addUndoEntry(CanvasHistoryModifyType.add, elementProperties);
- elementProperties.add(element);
- // _reservedIdUntil = elementProperties.length - 1;r
- notifyListeners();
- selectElmById(id);
- }
- void addTextboxElement() {
- String id = uuid.v4();
- ElementProperty element = ElementProperty(
- id: id,
- valueController: TextEditingController(text: 'Double tap to edit text'),
- type: ElementType.textbox,
- position: ElementPosition(top: 0, left: 0),
- width: 70,
- quarterTurns: 0,
- elementKey: GlobalKey()
- );
- // Save History
- addUndoEntry(CanvasHistoryModifyType.add, elementProperties);
- elementProperties.add(element);
- notifyListeners();
- selectElmById(id);
- }
- // ? Variable Element
- void addProductNameElement() {
- String id = uuid.v4();
- ElementProperty element = ElementProperty(
- id: id,
- valueController: TextEditingController(text: '{{PRODUCTNAME}}'),
- type: ElementType.textbox,
- variableType: ElementVariableType.productName,
- position: ElementPosition(top: 0, left: 0),
- width: 250,
- quarterTurns: 0,
- elementKey: GlobalKey()
- );
- // Save History
- addUndoEntry(CanvasHistoryModifyType.add, elementProperties);
- elementProperties.add(element);
- notifyListeners();
- selectElmById(id);
- }
- void addVariantNameElement() {
- String id = uuid.v4();
- ElementProperty element = ElementProperty(
- id: id,
- valueController: TextEditingController(text: '{{VARIANTNAME}}'),
- type: ElementType.textbox,
- variableType: ElementVariableType.variantName,
- position: ElementPosition(top: 0, left: 0),
- width: 250,
- quarterTurns: 0,
- elementKey: GlobalKey()
- );
- // Save History
- addUndoEntry(CanvasHistoryModifyType.add, elementProperties);
- elementProperties.add(element);
- notifyListeners();
- selectElmById(id);
- }
- void addProductionCodeElement() {
- String id = uuid.v4();
- ElementProperty element = ElementProperty(
- id: id,
- valueController: TextEditingController(text: '{{PRODUCTIONCODE}}'),
- type: ElementType.textbox,
- variableType: ElementVariableType.productionCode,
- position: ElementPosition(top: 0, left: 0),
- width: 250,
- quarterTurns: 0,
- elementKey: GlobalKey()
- );
- // Save History
- addUndoEntry(CanvasHistoryModifyType.add, elementProperties);
- elementProperties.add(element);
- notifyListeners();
- selectElmById(id);
- }
- void addProductionDateElement() {
- String id = uuid.v4();
- ElementProperty element = ElementProperty(
- id: id,
- valueController: TextEditingController(text: '{{PRODUCTIONDATE}}'),
- type: ElementType.text,
- variableType: ElementVariableType.productionDate,
- position: ElementPosition(top: 0, left: 0),
- width: 250,
- quarterTurns: 0,
- elementKey: GlobalKey()
- );
- // Save History
- addUndoEntry(CanvasHistoryModifyType.add, elementProperties);
- elementProperties.add(element);
- notifyListeners();
- selectElmById(id);
- }
- void addSerialNumberElement() {
- String id = uuid.v4();
- ElementProperty element = ElementProperty(
- id: id,
- valueController: TextEditingController(text: '{{SERIALNUMBER}}'),
- type: ElementType.text,
- variableType: ElementVariableType.serialNumber,
- position: ElementPosition(top: 0, left: 0),
- width: 250,
- quarterTurns: 0,
- elementKey: GlobalKey()
- );
- // Save History
- addUndoEntry(CanvasHistoryModifyType.add, elementProperties);
- elementProperties.add(element);
- notifyListeners();
- selectElmById(id);
- }
-
- void updateElmPosition(Offset offset) {
- ElementProperty? element = selectedElm;
- if (element == null) return;
- // element.position = ElementPosition(top: offset.dy, left: offset.dx);
- print(offset.dx);
- element.position.top += offset.dy.round();
- element.position.left += offset.dx.round();
- notifyListeners();
- }
- void updateElmWitdh(double width) {
- ElementProperty? element = selectedElm;
- if (element == null) return;
- element.width = width;
- notifyListeners();
- }
- void toggleLockElement() {
- if (selectedElm == null) return;
- // ? Save History
- addUndoEntry(CanvasHistoryModifyType.lock, elementProperties);
- selectedElm!.isLocked = !selectedElm!.isLocked;
- notifyListeners();
- }
- void selectElmById(String id) {
- selectedElmId = id;
- notifyListeners();
- }
- void enableEdit() {
- if (isVariableElement) return;
- if (selectedElm!.isLocked) {
- _showLockedToast('Cant modify locked element');
- return;
- }
- isEditing = true;
- notifyListeners();
- }
- void disableEdit() {
- isEditing = false;
- notifyListeners();
- }
- void unSelectElm() {
- selectedElmId = null;
- isEditing = false;
- notifyListeners();
- }
- bool isSelected(String id) {
- return selectedElmId == id;
- }
- // ? Getters
- String get selectedElmType {
- if (elementProperties.isNotEmpty && selectedElmId != null) {
- final selectedElm = elementProperties.firstWhere((element) => element.id == selectedElmId);
- return elementGetter(selectedElm.type);
- }
- return '';
- }
- ElementProperty? get selectedElm {
- if (elementProperties.isNotEmpty && selectedElmId != null) {
- return elementProperties.firstWhere((element) => element.id == selectedElmId);
- }
- return null;
- }
- GlobalKey? get selectedElmKey {
- if (elementProperties.isNotEmpty && selectedElmId != null) {
- return elementProperties.firstWhere((element) => element.id == selectedElmId).elementKey;
- }
- return null;
- }
- bool get shouldShowFontResizer {
- if (selectedElm == null) return false;
- if (![ElementType.text, ElementType.textbox].contains(selectedElm!.type)) return false;
- return true;
- }
- bool get shouldShowQrResizer {
- if (selectedElm == null) return false;
- if (selectedElm!.type != ElementType.qr) return false;
- return true;
- }
- bool get shouldShowDeleteElementButton {
- if (selectedElm == null) return false;
- if ([ElementType.qr].contains(selectedElm!.type)) return false;
- return true;
- }
- bool get isVariableElement {
- if (selectedElm == null) return false;
- if (selectedElm!.variableType == null) return false;
- return true;
- }
- // ? Element overlay
- bool shouldShowTextboxResizer(String elmId) {
- if (selectedElmId == null) return false;
- if (selectedElmId != elmId ) return false;
- if (selectedElm!.type != ElementType.textbox) return false;
- return true;
- }
- bool shouldShowOverlay(String elmId) {
- if (selectedElmId == null) return false;
- if (selectedElmId != elmId ) return false;
- if (selectedElm!.type == ElementType.qr) return false;
- return true;
- }
- // Toolbar
- bool get insertElementMode{
- if (selectedElm == null) return true;
- return false;
- }
- /// Can only rotate [ElementType.text, ElementType.textBox]
- void rotate() {
- ElementProperty? element = selectedElm;
- if (element == null) return;
- if (element.isLocked) {
- _showLockedToast('Cant rotate locked element');
- return;
- }
-
- if (![ElementType.text, ElementType.textbox].contains(element.type)) return;
-
- // ? Save history
- addUndoEntry(CanvasHistoryModifyType.rotate, elementProperties);
- if (element.type == ElementType.text) _rotateText(element);
- if (element.type == ElementType.textbox) _rotateTextBox(element);
- // Adjust Size
- // double currentElementHeight = selectedElmKey!.currentContext!.size!.height;
- // double currentElementWidth = selectedElmKey!.currentContext!.size!.width;
- // selectedElmKey!.currentContext!.size!.height = currentElementWidth;
- // selectedElmKey!.currentContext!.size!.width = currentElementHeight;
-
- notifyListeners();
- }
- void _rotateText(ElementProperty element) {
- if (element.quarterTurns < 3) {
- element.quarterTurns += 1;
- } else {
- element.quarterTurns = 0;
- }
- }
- void _rotateTextBox(ElementProperty element) {
- if (element.quarterTurns == 0) {
- element.quarterTurns = 3;
- } else {
- element.quarterTurns = 0;
- }
- }
- // FontSize Handler
- void changeFontSize(int? fontSize) {
- if (fontSize == null) return;
- if (selectedElm == null) return;
- if (selectedElm!.isLocked) {
- _showLockedToast('Cant modify locked element');
- return;
- }
- // ? Save History
- addUndoEntry(CanvasHistoryModifyType.resize, elementProperties);
- selectedElm!.fontScale = fontSize;
- notifyListeners();
- }
-
- void incrementFontSize() {
- if (selectedElm == null) return;
- final incrementTo = selectedElm!.fontScale + 1;
- if (selectedElm!.isLocked) {
- _showLockedToast('Cant modify locked element');
- return;
- }
- // check if value is allowed for resize
- if (CanvasStyle.fontSizeMap.containsKey(incrementTo)) {
- // ? Save History
- addUndoEntry(CanvasHistoryModifyType.resize, elementProperties);
- selectedElm!.fontScale = incrementTo;
- print('kepenjet increase');
- } else {
- print('cant increment');
- }
- notifyListeners();
- }
- void decrementFontSize() {
- if (selectedElm == null) return;
- final decrementTo = selectedElm!.fontScale - 1;
- if (selectedElm!.isLocked) {
- _showLockedToast('Cant modify locked element');
- return;
- }
- // check if value is allowed for resize
- if (CanvasStyle.fontSizeMap.containsKey(decrementTo)) {
- // ? Save History
- addUndoEntry(CanvasHistoryModifyType.resize, elementProperties);
- selectedElm!.fontScale = decrementTo;
- } else {
- print('cant decrement');
- }
- notifyListeners();
- }
- // Qr Size Handler
- void changeQrSize(int? fontSize) {
- if (fontSize == null) return;
- if (selectedElm == null) return;
- if (selectedElm!.isLocked) {
- _showLockedToast('Cant modify locked element');
- return;
- }
- selectedElm!.qrScale = fontSize;
- notifyListeners();
- }
-
- void incrementQrSize() {
- if (selectedElm == null) return;
- if (selectedElm!.type != ElementType.qr) return;
- final incrementTo = selectedElm!.qrScale! + 1;
- if (selectedElm!.isLocked) {
- _showLockedToast('Cant modify locked element');
- return;
- }
- // check if value is allowed for resize
- if (CanvasStyle.qrSizeMap.containsKey(incrementTo)) {
- selectedElm!.qrScale = incrementTo;
- } else {
- print('cant increment');
- }
- notifyListeners();
- }
- void decrementQrSize() {
- if (selectedElm == null) return;
- if (selectedElm!.type != ElementType.qr) return;
- final decrementTo = selectedElm!.qrScale! - 1;
- if (selectedElm!.isLocked) {
- _showLockedToast('Cant modify locked element');
- return;
- }
- // check if value is allowed for resize
- if (CanvasStyle.qrSizeMap.containsKey(decrementTo)) {
- selectedElm!.qrScale = decrementTo;
- } else {
- print('cant decrement');
- }
- notifyListeners();
- }
- // Delete Element
- Future<void> deleteElement(BuildContext context) async {
- if (selectedElm == null) return;
- if (selectedElm!.isLocked) {
- _showLockedToast('Cant delete locked element');
- return;
- }
- final shouldDelete = await showDialog(
- context: context,
- builder: (context) {
- return AlertDialog(
- title: Text('Delete Element ?'),
- content: Text('Are you sure want to delete this element ?'),
- actions: [
- TextButton(
- onPressed: () => Navigator.pop(context, false),
- child: Text('Cancel')
- ),
- TextButton(
- onPressed: () => Navigator.pop(context, true),
- child: Text('Delete')
- ),
- ],
- );
- },
- );
- if (!shouldDelete) return;
- // ? Save History
- addUndoEntry(CanvasHistoryModifyType.remove, elementProperties);
- elementProperties.removeWhere((e) => e.id == selectedElm!.id);
- unSelectElm();
- notifyListeners();
- }
- // ? snapping element
- // Offset? snapToElement(Offset newPosition) {
- // for (var element in elementProperties) {
- // if ((newPosition.dx - element.))
- // }
- // }
- // ? helper method
- void _showLockedToast(String titleText) {
- toastification.show(
- title: Text(titleText),
- description: Text('unlock to change element property'),
- closeButtonShowType: CloseButtonShowType.none,
- style: ToastificationStyle.minimal,
- type: ToastificationType.warning,
- autoCloseDuration: const Duration(seconds: 3),
- alignment: Alignment.bottomCenter,
- dragToClose: true
- );
- }
- // ? Template to JSON
- String buildJSON() {
- var elementsMap = [];
- var canvasResultMap = {
- 'title': 'Test',
- 'width': (canvasProperty.width / 10).round(),
- 'height': (canvasProperty.height / 10).round(),
- 'content': {
- 'version': editorVersion,
- 'elements': []
- }
- };
- for (var element in elementProperties) {
- var elementMap = {
- 'id': uuid.v4(),
- 'content': element.valueController.text,
- 'height': element.elementKey.currentContext?.size?.height.round() ?? 0,
- 'width': element.elementKey.currentContext?.size?.width.round() ?? 0,
- 'position': {
- 'top': element.position.top.round(),
- 'left': element.position.left.round()
- },
- 'size': _getElementSizeResult(element),
- 'rotation': _getElementRotationResult(element.quarterTurns),
- 'type': _getElementTypeResult(element)
- };
- elementsMap.add(elementMap);
- }
-
- (canvasResultMap['content'] as Map<String, dynamic>)['elements'] = elementsMap;
- final templateJsonResult = jsonEncode(canvasResultMap);
- print(templateJsonResult);
- log(templateJsonResult);
-
- return templateJsonResult;
- }
- String _getElementTypeResult(ElementProperty element) {
- switch (element.type) {
- case ElementType.text:
- return 'text';
- case ElementType.textbox:
- return 'textbox';
- case ElementType.qr:
- return 'qrcode';
- default:
- return '';
- }
- }
- int _getElementRotationResult(int quarterTurns) {
- switch (quarterTurns) {
- case 1:
- return 90;
- case 2:
- return 180;
- case 3:
- return -90;
- default:
- return 0;
- }
- }
- int _getElementSizeResult(ElementProperty element) {
- if (element.type == ElementType.qr) {
- return element.qrScale ?? 1;
- }
- return element.fontScale;
- }
- }
|