123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- import 'package:defer_pointer/defer_pointer.dart';
- import 'package:flutter/gestures.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_canvas_editor/providers/editor.dart';
- import 'package:provider/provider.dart';
- import 'dart:math' as math;
- import '../main.dart';
- import '../style/canvas_style.dart';
- enum ElementType {text, textbox, qr, image}
- String elementGetter(ElementType type) {
- switch (type) {
- case ElementType.text:
- return 'Text';
- case ElementType.textbox:
- return 'TextBox';
- case ElementType.qr:
- return 'QR';
- case ElementType.image:
- return 'Image';
- default:
- return '';
- }
- }
- class ElementPosition {
- double top;
- double left;
- ElementPosition({required this.top, required this.left});
- }
- class ElementSize {
- double width;
- double height;
- ElementSize({required this.width, required this.height});
- }
- class ElementProperty {
- String id;
- TextEditingController valueController;
- ElementType type;
- ElementPosition position;
- // ElementSize size;
- double width;
- int quarterTurns;
- GlobalKey elementKey;
- Color? color;
- int fontScale;
- int? qrScale;
- bool isLocked;
- ElementProperty({
- required this.id,
- required this.valueController,
- required this.type,
- required this.position,
- // required this.size,
- required this.width,
- required this.quarterTurns,
- required this.elementKey,
- this.color,
- this.fontScale = 3,
- this.qrScale,
- this.isLocked = false
- });
- }
- class ElementWidget extends StatefulWidget {
- final ElementProperty elementProperty;
- final CanvasProperty canvasProperty;
- final GlobalKey globalKey;
-
- const ElementWidget({
- required this.elementProperty,
- required this.canvasProperty,
- required this.globalKey,
- super.key
- });
- @override
- State<ElementWidget> createState() => _ElementWidgetState();
- }
- class _ElementWidgetState extends State<ElementWidget> {
- Offset? _dragStartPosition;
- // dragable container
- final double _resizerHeight = 36;
- final double _resizerWidth = 36;
- double _height = 0;
- double _currentScale = 1.0;
- late TransformationController _transformController;
- @override
- void initState() {
- super.initState();
-
- WidgetsBinding.instance.addPostFrameCallback((_) => _updateScale);
- _transformController = Provider.of<Editor>(context, listen: false).canvasTransformationController;
- _setInitialScale();
- _listenTransformationChanges();
- }
- void _updateScale() {
- _currentScale = Provider.of<Editor>(context, listen: false).canvasTransformationController.value.getMaxScaleOnAxis();
-
- setState(() {});
- }
- void _setInitialScale() {
- _updateScale();
- }
- void _listenTransformationChanges() {
- _transformController.addListener(_updateScale);
- }
- void _removeTransformationListener() {
- _transformController.removeListener(_updateScale);
- }
- @override
- void dispose() {
- _removeTransformationListener();
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- final editorProvider = Provider.of<Editor>(context);
- ElementProperty element = widget.elementProperty;
- final CanvasProperty canvas = widget.canvasProperty;
- WidgetsBinding.instance.addPostFrameCallback((_) {
- _height = element.elementKey.currentContext!.size!.height;
- });
- return Positioned(
- top: element.position.top,
- left: element.position.left,
- child: GestureDetector(
- onDoubleTap: () {
- print('double tap detected');
- Provider.of<Editor>(context, listen: false).selectElmById(element.id);
- Provider.of<Editor>(context, listen: false).enableEdit();
- },
- onTap: () {
- print('Element Gesture Detector Tapped!');
- Provider.of<Editor>(context, listen: false).selectElmById(element.id);
- },
- onPanUpdate: (details) {
- if (!Provider.of<Editor>(context, listen: false).isSelected(element.id)) {
- return;
- }
- if (element.isLocked) return;
-
- final elmKeyContext = Provider.of<Editor>(context, listen: false).selectedElmKey?.currentContext;
-
- if (elmKeyContext == null) {
- debugPrint('WARNING, elmKeyContext not found');
- }
-
- bool isElmRotatedVertically = [1,3].contains(element.quarterTurns);
-
- double width = isElmRotatedVertically ? elmKeyContext!.size!.height : elmKeyContext!.size!.width;
- double height = isElmRotatedVertically ? elmKeyContext.size!.width : elmKeyContext.size!.height;
-
- double right = element.position.left + width;
- double bottom = element.position.top + height;
-
-
- bool isElmWidthExceedCanvas = width > canvas.width;
- bool isElmHeightExceedCanvas = height > canvas.height;
-
- // Check if the object is out of the canvas
- if (element.position.top < 0) {
- setState(() {
- element.position.top = 0;
- });
-
- print('object is out of canvas');
- return;
- }
-
- if (element.position.left < 0) {
- setState(() {
- element.position.left = 0;
- });
-
- print('object is out of canvas');
- return;
- }
-
- if (!isElmHeightExceedCanvas && bottom > canvas.height) {
- setState(() {
- element.position.top = canvas.height - height;
- });
-
- print('object is out of canvas');
- return;
- }
-
-
- if (!isElmWidthExceedCanvas && right > canvas.width) {
- setState(() {
- element.position.left = canvas.width - width;
- });
-
- print('object is out of canvas');
- return;
- }
-
- Provider.of<Editor>(context, listen: false).updateElmPosition(details.delta);
- },
- child: Stack(
- clipBehavior: Clip.none,
- children: [
- RotatedBox(
- // angle: element.quarterTurns * (math.pi / 2),
- quarterTurns: element.quarterTurns,
- child: Stack(
- clipBehavior: Clip.none,
- children: [
- Container(
- width: element.type == ElementType.text
- ? null
-
- : element.type == ElementType.qr
- ? CanvasStyle.getQrSize(element.qrScale!)
-
- : element.width ,
- height: element.type == ElementType.qr
- ? CanvasStyle.getQrSize(element.qrScale!)
- : null,
- // child: Text('Top: ${element.position.top}, Left: ${element.position.left}, isSelected: ${Provider.of<Editor>(context, listen: false).isSelected(element.id)}'),
- key: widget.globalKey,
- decoration: BoxDecoration(
- // color: element.color ?? Colors.blue,
- border: Provider.of<Editor>(context, listen: true).isSelected(element.id) ? Border.all(
- color:Colors.red,
- width: 2,
- strokeAlign: BorderSide.strokeAlignOutside,
- ) : null,
- ),
- child:element.type == ElementType.qr ? const Image(image: AssetImage('asset/images/qr_template.png'),) : Provider.of<Editor>(context, listen: true).isEditing && (Provider.of<Editor>(context, listen: true).selectedElmId == element.id) ?
- IntrinsicWidth(
- child: TextField(
- controller: Provider.of<Editor>(context).selectedElm!.valueController,
- autofocus: true,
- keyboardType: TextInputType.multiline,
- enableSuggestions: false,
- autocorrect: false,
- maxLines: null,
- style: CanvasStyle.getTextStyle(element.fontScale),
- decoration: const InputDecoration(
- isDense: true,
- contentPadding: EdgeInsets.zero,
- border: InputBorder.none,
- ),
- onChanged: (_) {
- setState(() {
-
- });
- }
- ),
- ):
- Text(
- element.valueController.text,
- style: CanvasStyle.getTextStyle(element.fontScale),
- ),
- ),
-
- // ? Textbox Resizer
- if (editorProvider.shouldShowTextboxResizer(element.id)) Positioned(
- right: _resizerWidth / -2,
- top:_height / 2 - (_resizerHeight / 2),
- child: DeferPointer(
- link: editorProvider.textboxResizerDeferredPointerHandlerLink,
- // paintOnTop: true,
- child: Transform.scale(
- scale: 1 / _currentScale,
- child: Listener(
- onPointerDown: (details) {
- _dragStartPosition = details.position;
- },
- onPointerMove: (details) {
- if (element.isLocked) return;
-
- setState(() {
- final selectedElm = Provider.of<Editor>(context, listen: false).selectedElm;
-
- if (selectedElm == null) return;
-
-
- final elmKeyContext = selectedElm.elementKey.currentContext;
-
- double width = elmKeyContext!.size!.width;
-
- print(MediaQuery.of(context).devicePixelRatio);
-
-
- var delta;
- double canvasWidth = 1.0;
-
- print (_dragStartPosition!.dx);
- print (_dragStartPosition!.dy);
-
- // adjust width based on rotation
- print('quarter turn: ${element.quarterTurns}');
- switch(element.quarterTurns) {
- case 0:
- delta = details.position.dx - _dragStartPosition!.dx;
- canvasWidth = Provider.of<Editor>(context, listen: false).canvasProperty.width;
- case 3:
- delta = _dragStartPosition!.dy - details.position.dy;
- element.position.top -= delta / _currentScale;
- canvasWidth = Provider.of<Editor>(context, listen: false).canvasProperty.height;
- }
-
- element.width += delta / _currentScale; // Adjust width
- print('current scale $_currentScale');
-
- // Enforce minimum size
- element.width = element.width.clamp(75.0, canvasWidth);
-
- _dragStartPosition = details.position;
- if (width <= 0) return;
-
- Provider.of<Editor>(context, listen: false).updateElmWitdh(element.width);
- });
- },
- onPointerUp: (details) {
- _dragStartPosition = null;
- },
- child: Container(
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: Colors.blue.withOpacity(0.7),
- ),
- width: _resizerWidth,
- height: _resizerHeight,
- child: const RotatedBox(
- quarterTurns: 1,
- child: Icon(
- Icons.height,
- size: 24,
- color: Colors.white,
- ),
- ),
- ),
- ),
- ),
- ),
- ),
-
-
-
-
-
-
- // if (Provider.of<Editor>(context).selectedElmId == element.id && element.type != ElementType.qr) ... [
-
-
- // ]
- ],
- ),
- ),
- //? Overlay Button
- if (editorProvider.shouldShowOverlay(element.id)) Positioned(
- top: -60 / _currentScale,
- left: 0,
- child: DeferPointer(
- paintOnTop: true,
- child: Transform.scale(
- scale: 1 / _currentScale,
- alignment: Alignment.topLeft,
- child: Row(
- children: [
- IconButton.filled(
- onPressed: () {
- print('delete overlay tapped');
- editorProvider.deleteElement(context);
- },
- icon: const Icon(Icons.delete),
- color: Theme.of(context).colorScheme.error,
- style: ButtonStyle(
- backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer)
- ),
- ),
- IconButton.filled(
- onPressed: () {
- print('rotate overlay tapped');
- editorProvider.rotate();
-
- // test
- var getBox = element.elementKey.currentContext!.findRenderObject();
- },
- icon: const Icon(Icons.rotate_90_degrees_cw),
- // color: Theme.of(context).colorScheme.error,
- style: const ButtonStyle(
- // backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer)
- ),
- ),
- IconButton.filled(
- onPressed: () {
- print('lock overlay tapped');
- editorProvider.toggleLockElement();
- },
- icon: Icon(element.isLocked ? Icons.lock_outline : Icons.lock_open),
- // color: element.isLocked ? Theme.of(context).colorScheme.error,
- style: ButtonStyle(
- backgroundColor: element.isLocked ? WidgetStatePropertyAll(Theme.of(context).colorScheme.error) : null
- ),
- ),
- ],
- ),
- ),
- )
- )
- ],
- ),
- ),
- );
- }
- }
|