GithubHelp home page GithubHelp logo

fluttercandies / flutter_drawing_board Goto Github PK

View Code? Open in Web Editor NEW
140.0 5.0 51.0 3.64 MB

A new Flutter package of drawing board

License: MIT License

Java 0.10% Objective-C 0.58% Swift 0.85% Dart 67.07% HTML 1.10% CMake 11.37% C++ 17.80% C 1.04% Kotlin 0.09%

flutter_drawing_board's Introduction

Flutter Drawing Board

A Flutter package of drawing board.
Flutter artboard

English | 中文

pub package GitHub stars GitHub forks CodeFactor FlutterCandies

There are breaking changes after 0.3.0, text addition has been removed. For text addition and editing, please refer to pub package

Features

  • Basic drawing
  • Custom drawing
  • Canvas rotation, multi-touch movement, and scaling
  • Undo, redo

Preview

  • Try it online::Demo

  • Preview of basic functionalities

 

Usage

  • Create
//simple example

import 'package:flutter_drawing_board/flutter_drawing_board.dart';

DrawingBoard(
  background: Container(width: 400, height: 400, color: Colors.white),
  showDefaultActions: true, /// Enable default action options
  showDefaultTools: true,   /// Enable default toolbar
),
  • Retrieve drawing board data using DrawingController.getImageData
import 'package:flutter_drawing_board/flutter_drawing_board.dart';

final DrawingController _drawingController = DrawingController();

DrawingBoard(
  controller: _drawingController,
  background: Container(width: 400, height: 400, color: Colors.white),
  showDefaultActions: true,
  showDefaultTools: true,
),

/// Get drawing board data
Future<void> _getImageData() async {
  print((await _drawingController.getImageData()).buffer.asInt8List());
}
  • Retrieve content Json using DrawingController.getJsonList
import 'package:flutter_drawing_board/flutter_drawing_board.dart';

final DrawingController _drawingController = DrawingController();

DrawingBoard(
  controller: _drawingController,
  background: Container(width: 400, height: 400, color: Colors.white),
  showDefaultActions: true,
  showDefaultTools: true,
),

/// Get Json content
Future<void> _getJsonList() async {
  print(const JsonEncoder.withIndent('  ').convert(_drawingController.getJsonList()));
}
View Json
[
  {
    "type": "StraightLine",
    "startPoint": {
      "dx": 114.5670061088183,
      "dy": 117.50547159585983
    },
    "endPoint": {
      "dx": 252.9362813512929,
      "dy": 254.91849554320638
    },
    "paint": {
      "blendMode": 3,
      "color": 4294198070,
      "filterQuality": 3,
      "invertColors": false,
      "isAntiAlias": false,
      "strokeCap": 1,
      "strokeJoin": 1,
      "strokeWidth": 4.0,
      "style": 1
    }
  },
  {
    "type": "StraightLine",
    "startPoint": {
      "dx": 226.6379349225167,
      "dy": 152.11430225316613
    },
    "endPoint": {
      "dx": 135.67632523940733,
      "dy": 210.35948249064901
    },
    "paint": {
      "blendMode": 3,
      "color": 4294198070,
      "filterQuality": 3,
      "invertColors": false,
      "isAntiAlias": false,
      "strokeCap": 1,
      "strokeJoin": 1,
      "strokeWidth": 4.0,
      "style": 1
    }
  }
]
  • Add drawing content using Json
const Map<String, dynamic> _testLine1 = <String, dynamic>{
  'type': 'StraightLine',
  'startPoint': <String, dynamic>{'dx': 68.94337550070736, 'dy': 62.05980083656557},
  'endPoint': <String, dynamic>{'dx': 277.1373386828114, 'dy': 277.32029957032194},
  'paint': <String, dynamic>{
    'blendMode': 3,
    'color': 4294198070,
    'filterQuality': 3,
    'invertColors': false,
    'isAntiAlias': false,
    'strokeCap': 1,
    'strokeJoin': 1,
    'strokeWidth': 4.0,
    'style': 1
  }
};

const Map<String, dynamic> _testLine2 = <String, dynamic>{
  'type': 'StraightLine',
  'startPoint': <String, dynamic>{'dx': 106.35164817830423, 'dy': 255.9575653134524},
  'endPoint': <String, dynamic>{'dx': 292.76034659254094, 'dy': 92.125586665872},
  'paint': <String, dynamic>{
    'blendMode': 3,
    'color': 4294198070,
    'filterQuality': 3,
    'invertColors': false,
    'isAntiAlias': false,
    'strokeCap': 1,
    'strokeJoin': 1,
    'strokeWidth': 4.0,
    'style': 1
  }
};

  ...

/// Add Json test content
void _addTestLine() {
  _drawingController.addContent(StraightLine.fromJson(_testLine1));
  _drawingController.addContents(<PaintContent>[StraightLine.fromJson(_testLine2)]);
}
  • Set drawing content
/// Set drawing content as a simple line
_drawingController.setPaintContent = SimpleLine();
Built-in Shape Description Parameters
SimpleLine Simple line /
SmoothLine Stroke line double brushPrecision = 0.4
StraightLine Straight line /
Rectangle Rectangle /
Circle Ellipse bool isEllipse = false
bool startFromCenter = true
Eraser Eraser /
  • Set Paint
_drawingController.setStyle();

/// Optional parameters
void setStyle({
  BlendMode? blendMode,
  Color? color,
  ColorFilter? colorFilter,
  FilterQuality? filterQuality,
  ui.ImageFilter? imageFilter,
  bool? invertColors,
  bool? isAntiAlias,
  MaskFilter? maskFilter,
  Shader? shader,
  StrokeCap? strokeCap,
  StrokeJoin? strokeJoin,
  double? strokeMiterLimit,
  double? strokeWidth,
  PaintingStyle? style,
})
  • Canvas operations
/// Undo
_drawingController.undo();

/// Redo
_drawingController.redo();

/// Rotate canvas
_drawingController.turn();

/// Clear canvas
_drawingController.clear();

Custom Drawing

  • Create a custom drawing class that inherits from PaintContent (using a triangle as an example)
/// Custom drawing of a triangle
class Triangle extends PaintContent {
  Triangle();

  Triangle.data({
    required this.startPoint,
    required this.A,
    required this.B,
    required this.C,
    required Paint paint,
  }) : super.paint(paint);

  factory Triangle.fromJson(Map<String, dynamic> data) {
    return Triangle.data(
      startPoint: jsonToOffset(data['startPoint'] as Map<String, dynamic>),
      A: jsonToOffset(data['A'] as Map<String, dynamic>),
      B: jsonToOffset(data['B'] as Map<String, dynamic>),
      C: jsonToOffset(data['C'] as Map<String, dynamic>),
      paint: jsonToPaint(data['paint'] as Map<String, dynamic>),
    );
  }

  Offset startPoint = Offset.zero;

  Offset A = Offset.zero;
  Offset B = Offset.zero;
  Offset C = Offset.zero;

  @override
  void startDraw(Offset startPoint) => this.startPoint = startPoint;

  @override
  void drawing(Offset nowPoint) {
    A = Offset(startPoint.dx + (nowPoint.dx - startPoint.dx) / 2, startPoint.dy);
    B = Offset(startPoint.dx, nowPoint.dy);
    C = nowPoint;
  }

  @override
  void draw(Canvas canvas, Size size, bool deeper) {
    final Path path = Path()
      ..moveTo(A.dx, A.dy)
      ..lineTo(B.dx, B.dy)
      ..lineTo(C.dx, C.dy)
      ..close();

    canvas.drawPath(path, paint);
  }

  @override
  Triangle copy() => Triangle();

  @override
  Map<String, dynamic> toJson() {
    return <String, dynamic>{
      'startPoint': startPoint.toJson(),
      'A': A.toJson(),
      'B': B.toJson(),
      'C': C.toJson(),
      'paint': paint.toJson(),
    };
  }
}
  • Add a tool to the default toolbar (optional)

showDefaultTools must be true otherwise the default toolbar won't display

List<DefToolItem> DrawingBoard.defaultTools(Type currType, DrawingController controller); is the default toolset

Use defaultToolsBuilder to rewrite the default drawing tools, or insert DefToolItem custom tools directly into defaultTools

DrawingBoard(
  ...
  showDefaultTools: true,
  defaultToolsBuilder: (Type t, _) {
    return DrawingBoard.defaultTools(t, _drawingController)
      ..insert(   /// Insert the triangle tool into the second position of the default toolbar
        1,
        DefToolItem(
          icon: Icons.change_history_rounded,
          isActive: t == Triangle,
          onTap: () => _drawingController.setPaintContent = Triangle(),
      ),
    );
  },
)
  • Preview

flutter_drawing_board's People

Contributors

jxweijun avatar marsadmaqsood avatar sm-shifat avatar xsilencex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

flutter_drawing_board's Issues

[Feature request] Should have a function to reset the canvas to its initial size and position.

Platforms

Android

Description

Need a function to call which reset the size and position to its initial size and position. Like back to the position where it was when its initialized. Otherwise can you stop it from totally going out of screen boundary?

Why

When dragging the canvas , some times it goes out of the sceen thats looks odd. so on a press of a button it should be back to its original position.

擦除时颜色是黑色

1647793867620161.mp4

Version information

Device: iPhone iPhone6s plus
OS: iOS 14.7.1
Package Version: 0.2.7
Flutter Version:2.10.1

support addListener

please consider adding support for:
controller.addListener,
so we can change external UI based on user's drawing actions.

for example: change the AppBar leading to either "done" or "back" based on if user has drawn

工具栏目怎样浮动到画板上

Platforms

dart

Description

是永远固定在画板下面的
content = Column(
children: [
Expanded(child: content),
if (widget.showDefaultActions) _buildDefaultActions,
if (widget.showDefaultTools) _buildDefaultTools,
],
);

My code

No response

Try do it

No response

请教一个问题

比如我想在一个图片上涂鸦,想用你的库怎么把图片搞到画板上,没有思路。

support canRedo / canUndo / canClear

please consider support
controller.canRedo()
controller.canUndo()
controller.canClear()

so we can set the UI icons inactive/active based on state. Thanks~

Select & edit shape support

Like rectangles, circles, ellipses, polygons, etc.

Like a circle, when selected, show the center and four points(top, bottom, left, right). Select the center point to move the shape, and select top or other to change the radius.

[Feature request] Fix the canvas

Platforms

Android, iOS, macOS, Web, Windows, Linux

Description

I am trying to make the canvas fixed but there is no option currently available.

Why

I am trying to make the canvas fixed but there is no option currently available.

更新版本的时候,无法运行程序

提示错误

/C:/__ENV__/flutter/.pub-cache/hosted/pub.flutter-io.cn/flutter_drawing_board-0.4.1+4/lib/src/drawing_board.dart:154:11: Error: No named parameter with the name 'scaleFactor'.
          scaleFactor: widget.boardScaleFactor,
          ^^^^^^^^^^^
/C:/__ENV__/flutter/packages/flutter/lib/src/widgets/interactive_viewer.dart:60:3: Context: Found this candidate, but the arguments don't match.
  InteractiveViewer({
  ^^^^^^^^^^^^^^^^^
/C:/__ENV__/flutter/.pub-cache/hosted/pub.flutter-io.cn/flutter_drawing_board-0.4.1+4/lib/src/paint_extension/ex_image_filter.dart:37:24: Error: Member not found: 'ImageFilter.dilate'.
    return ImageFilter.dilate(
                       ^^^^^^
/C:/__ENV__/flutter/.pub-cache/hosted/pub.flutter-io.cn/flutter_drawing_board-0.4.1+4/lib/src/paint_extension/ex_image_filter.dart:45:24: Error: Member not found: 'ImageFilter.erode'.
    return ImageFilter.erode(
                       ^^^^^
/C:/__ENV__/flutter/.pub-cache/hosted/pub.flutter-io.cn/flutter_drawing_board-0.4.1+4/lib/src/paint_extension/ex_image_filter.dart:67:11: Error: 'Float64List' isn't a type.
    final Float64List matrixList = Float64List.fromList(matrixListData);
          ^^^^^^^^^^^
/C:/__ENV__/flutter/.pub-cache/hosted/pub.flutter-io.cn/flutter_drawing_board-0.4.1+4/lib/src/paint_extension/ex_image_filter.dart:67:36: Error: Undefined name 'Float64List'.
    final Float64List matrixList = Float64List.fromList(matrixListData);
                                   ^^^^^^^^^^^


FAILURE: Build failed with an exception.

* Where:
Script 'C:\__ENV__\flutter\packages\flutter_tools\gradle\flutter.gradle' line: 1102

* What went wrong:
Execution failed for task ':app:compileFlutterBuildDebug'.
> Process 'command 'C:\__ENV__\flutter\bin\flutter.bat'' finished with non-zero exit value 1

How to convert to String

Platforms

Android, iOS

Description

how to convert to String

My code

No response

Try do it

No response

lags and save feature not exist !?

hello FlutterCandies team,
I really love the package even if it not providing a way to custom the board, but it 's good with the default settings,

1- the first problem is that I can clearly see some lags when I draw a lot !
2- the second problem , is the lack of the save button, when the user finish the draw, how they can save the draws?

hope you will update the package , bonus > you can add the feature of changing the board icons like the trash,pen.. icons!

thanks !

[Discussions] When I try to save the file and trigger `_getImageData()`, the `data` always turns out to be null

Content

Hello, I used your package in FlutterFlow to create a customized widget, made some modifications, and then downloaded it as a Flutter project. I successfully ran the app on the simulator, and everything else seems to be working fine.

However, the image-saving functionality of the check button doesn't work. When I click it, the terminal prints "Null check operator used on a null value." I suspect that the issue is caused by the code segment "(await _drawingController.getImageData())" returning null, but I'm not sure how to resolve it.
截圖 2023-11-03 下午1 30 58

Here's the complete code for my widget below. I hope to receive your assistance. Thank you.

// Automatic FlutterFlow imports
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import 'index.dart'; // Imports other custom widgets
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'index.dart'; // Imports other custom widgets

import 'dart:convert';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_drawing_board/flutter_drawing_board.dart';
import 'package:flutter_drawing_board/paint_contents.dart';
import 'package:flutter_drawing_board/paint_extension.dart';

// 建構工具列的引入
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter_drawing_board/src/drawing_controller.dart';
import 'package:flutter_drawing_board/src/helper/ex_value_builder.dart';
import 'package:flutter_drawing_board/src/helper/get_size.dart';
import 'package:flutter_drawing_board/src/paint_contents/eraser.dart';
import 'package:flutter_drawing_board/src/paint_contents/simple_line.dart';
import 'package:flutter_drawing_board/src/painter.dart';
import 'package:http/http.dart' as http; // 導入 HTTP 庫

final List<Map<String, dynamic>> tData = <Map<String, dynamic>>[
<String, dynamic>{
'type': 'SimpleLine',
'path': <String, dynamic>{
'fillType': 0,
'steps': <Map<String, dynamic>>[
<String, dynamic>{'type': 'moveTo', 'x': 163.0, 'y': 122.0},
// ... (省略部分步骤)
<String, dynamic>{'type': 'lineTo', 'x': 304.0, 'y': 191.0}
]
},
'paint': <String, dynamic>{
'blendMode': 3,
'color': 4294198070,
'filterQuality': 3,
'invertColors': false,
'isAntiAlias': false,
'strokeCap': 1,
'strokeJoin': 1,
'strokeWidth': 33.375,
'style': 1
}
},
// 添加更多绘图数据
];

const Map<String, dynamic> _testLine1 = <String, dynamic>{
'type': 'StraightLine',
'startPoint': <String, dynamic>{
'dx': 68.94337550070736,
'dy': 62.05980083656557
},
'endPoint': <String, dynamic>{
'dx': 277.1373386828114,
'dy': 277.32029957032194
},
'paint': <String, dynamic>{
'blendMode': 3,
'color': 4294198070,
'filterQuality': 3,
'invertColors': false,
'isAntiAlias': false,
'strokeCap': 1,
'strokeJoin': 1,
'strokeWidth': 4.0,
'style': 1
}
};

const Map<String, dynamic> _testLine2 = <String, dynamic>{
'type': 'StraightLine',
'startPoint': <String, dynamic>{
'dx': 106.35164817830423,
'dy': 255.9575653134524
},
'endPoint': <String, dynamic>{
'dx': 292.76034659254094,
'dy': 92.125586665872
},
'paint': <String, dynamic>{
'blendMode': 3,
'color': 4294198070,
'filterQuality': 3,
'invertColors': false,
'isAntiAlias': false,
'strokeCap': 1,
'strokeJoin': 1,
'strokeWidth': 4.0,
'style': 1
}
};

class Drawingboard extends StatefulWidget {
const Drawingboard({
Key? key,
this.width,
this.height,
this.url,
}) : super(key: key);

final double? width;
final double? height;
final String? url;

@OverRide
_DrawingboardState createState() => _DrawingboardState();
}

// 新引入的建構工具列程式碼
/// 默认工具栏构建器
typedef DefaultToolsBuilder = List Function(
Type currType,
DrawingController controller,
);

/// 画板
class DrawingBoard extends StatefulWidget {
Color selectedColor;

DrawingBoard({
super.key,
required this.background,
this.controller,
this.showDefaultActions = false,
this.showDefaultTools = false,
this.onPointerDown,
this.onPointerMove,
this.onPointerUp,
this.clipBehavior = Clip.antiAlias,
this.defaultToolsBuilder,
this.boardClipBehavior = Clip.hardEdge,
this.panAxis = PanAxis.free,
this.boardBoundaryMargin,
this.boardConstrained = false,
this.maxScale = 20,
this.minScale = 0.2,
this.boardPanEnabled = true,
this.boardScaleEnabled = true,
this.boardScaleFactor = 200.0,
this.onInteractionEnd,
this.onInteractionStart,
this.onInteractionUpdate,
this.transformationController,
this.alignment = Alignment.topCenter,
this.selectedColor = Colors.red,
});

/// 画板背景控件
final Widget background;

/// 画板控制器
final DrawingController? controller;

/// 显示默认样式的操作栏
final bool showDefaultActions;

/// 显示默认样式的工具栏
final bool showDefaultTools;

/// 开始拖动
final Function(PointerDownEvent pde)? onPointerDown;

/// 正在拖动
final Function(PointerMoveEvent pme)? onPointerMove;

/// 结束拖动
final Function(PointerUpEvent pue)? onPointerUp;

/// 边缘裁剪方式
final Clip clipBehavior;

/// 默认工具栏构建器
final DefaultToolsBuilder? defaultToolsBuilder;

/// 缩放板属性
final Clip boardClipBehavior;
final PanAxis panAxis;
final EdgeInsets? boardBoundaryMargin;
final bool boardConstrained;
final double maxScale;
final double minScale;
final void Function(ScaleEndDetails)? onInteractionEnd;
final void Function(ScaleStartDetails)? onInteractionStart;
final void Function(ScaleUpdateDetails)? onInteractionUpdate;
final bool boardPanEnabled;
final bool boardScaleEnabled;
final double boardScaleFactor;
final TransformationController? transformationController;
final AlignmentGeometry alignment;

/// 默认工具项列表
static List defaultTools(
Type currType, DrawingController controller) {
return [
DefToolItem(
isActive: currType == SimpleLine,
icon: CupertinoIcons.pencil,
onTap: () => controller.setPaintContent(SimpleLine())),
DefToolItem(
isActive: currType == Eraser,
icon: CupertinoIcons.bandage,
onTap: () => controller.setPaintContent(Eraser(color: Colors.white))),
];
}

@OverRide
State createState() => _DrawingBoardState();
}

class _DrawingBoardState extends State {
// 绘制控制器
final DrawingController _drawingController = DrawingController();

// 获取画板数据 getImageData()
Future _getImageData() async {
final Uint8List? data =
(await _drawingController.getImageData())?.buffer.asUint8List();
if (data == null) {
debugPrint('獲取圖片資料失敗');
return;
}

if (mounted) {
  showDialog<void>(
    context: context,
    builder: (BuildContext c) {
      return Material(
        color: Colors.transparent,
        child: InkWell(
          onTap: () => Navigator.pop(c),
          child: Image.memory(data),
        ),
      );
    },
  );
}

}

late final DrawingController _controller =
widget.controller ?? DrawingController();

@OverRide
void dispose() {
if (widget.controller == null) {
_controller.dispose();
}
super.dispose();
}

@OverRide
Widget build(BuildContext context) {
Widget content = InteractiveViewer(
maxScale: widget.maxScale,
minScale: widget.minScale,
boundaryMargin: widget.boardBoundaryMargin ??
EdgeInsets.all(MediaQuery.of(context).size.width),
clipBehavior: widget.boardClipBehavior,
panAxis: widget.panAxis,
constrained: widget.boardConstrained,
onInteractionStart: widget.onInteractionStart,
onInteractionUpdate: widget.onInteractionUpdate,
onInteractionEnd: widget.onInteractionEnd,
scaleFactor: widget.boardScaleFactor,
panEnabled: widget.boardPanEnabled,
scaleEnabled: widget.boardScaleEnabled,
transformationController: widget.transformationController,
child: Align(alignment: widget.alignment, child: _buildBoard),
);

if (widget.showDefaultActions || widget.showDefaultTools) {
  content = Column(
    children: <Widget>[
      Expanded(child: content),
      if (widget.showDefaultActions) _buildDefaultActions,
      if (widget.showDefaultTools) _buildDefaultTools,
    ],
  );
}

return Listener(
  onPointerDown: (PointerDownEvent pde) =>
      _controller.addFingerCount(pde.localPosition),
  onPointerUp: (PointerUpEvent pue) =>
      _controller.reduceFingerCount(pue.localPosition),
  child: content,
);

}

/// 构建画板
Widget get _buildBoard {
return RepaintBoundary(
key: _controller.painterKey,
child: ExValueBuilder(
valueListenable: controller.drawConfig,
shouldRebuild: (DrawConfig p, DrawConfig n) =>
p.angle != n.angle || p.size != n.size,
builder: (
, DrawConfig dc, Widget? child) {
Widget c = child!;

      if (dc.size != null) {
        final bool isHorizontal = dc.angle.toDouble() % 2 == 0;
        final double max = dc.size!.longestSide;

        if (!isHorizontal) {
          c = SizedBox(
            width: max,
            height: max,
            child: c,
          );
        }
      }

      return Transform.rotate(
        angle: dc.angle * pi / 2,
        child: c,
      );
    },
    child: Center(
      child: Stack(
        alignment: Alignment.center,
        children: <Widget>[_buildImage, _buildPainter],
      ),
    ),
  ),
);

}

/// 构建背景
Widget get _buildImage => GetSize(
onChange: (Size? size) => _controller.setBoardSize(size),
child: widget.background,
);

/// 构建绘制层
Widget get _buildPainter {
return ExValueBuilder(
valueListenable: controller.drawConfig,
shouldRebuild: (DrawConfig p, DrawConfig n) => p.size != n.size,
builder: (
, DrawConfig dc, Widget? child) {
return SizedBox(
width: dc.size?.width,
height: dc.size?.height,
child: child,
);
},
child: Painter(
drawingController: _controller,
onPointerDown: widget.onPointerDown,
onPointerMove: widget.onPointerMove,
onPointerUp: widget.onPointerUp,
),
);
}

/// 构建默认操作栏
Widget get _buildDefaultActions {
return Container(
width: 530,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.0),
),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
child: Material(
color: Colors.white,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: Row(
children: [
DropdownButton(
value: widget.selectedColor, // 当前所选颜色
onChanged: (Color? newColor) {
if (newColor != null) {
setState(() {
widget.selectedColor = newColor;
});
_controller.setStyle(color: newColor); // 设置所选颜色
}
},
items: <DropdownMenuItem>[
DropdownMenuItem(
value: Colors.black,
child: Text("黑"),
),
DropdownMenuItem(
value: Colors.red,
child: Text("紅"),
),
DropdownMenuItem(
value: Colors.blue,
child: Text("藍"),
),
DropdownMenuItem(
value: Colors.yellow,
child: Text("黄"),
),
],
),

          SizedBox(
            height: 24,
            width: 160,
            child: ExValueBuilder<DrawConfig>(
              valueListenable: _controller.drawConfig,
              shouldRebuild: (DrawConfig p, DrawConfig n) =>
                  p.strokeWidth != n.strokeWidth,
              builder: (_, DrawConfig dc, ___) {
                return Slider(
                  value: dc.strokeWidth,
                  max: 50,
                  min: 1,
                  onChanged: (double v) =>
                      _controller.setStyle(strokeWidth: v),
                );
              },
            ),
          ),
          // 工具栏
          ExValueBuilder<DrawConfig>(
            valueListenable: _controller.drawConfig,
            shouldRebuild: (DrawConfig p, DrawConfig n) =>
                p.contentType != n.contentType,
            builder: (_, DrawConfig dc, ___) {
              final Type currType = dc.contentType;
              return Row(
                children: (widget.defaultToolsBuilder
                            ?.call(currType, _controller) ??
                        DrawingBoard.defaultTools(currType, _controller))
                    .map((DefToolItem item) =>
                        _DefToolItemWidget(item: item))
                    .toList(),
              );
            },
          ),
          IconButton(
              icon: const Icon(CupertinoIcons.arrow_turn_up_left),
              onPressed: () => _controller.undo()),
          IconButton(
              icon: const Icon(CupertinoIcons.arrow_turn_up_right),
              onPressed: () => _controller.redo()),
          IconButton(
              icon: const Icon(CupertinoIcons.trash),
              onPressed: () => _controller.clear()),
          IconButton(
            icon: const Icon(Icons.check),
            onPressed: _getImageData,
          ),
        ],
      ),
    ),
  ),
);

}

/// 构建默认工具栏
Widget get _buildDefaultTools {
return Material(
color: Colors.white,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: ExValueBuilder(
valueListenable: controller.drawConfig,
shouldRebuild: (DrawConfig p, DrawConfig n) =>
p.contentType != n.contentType,
builder: (
, DrawConfig dc, ___) {
final Type currType = dc.contentType;

        return Row(
          children:
              (widget.defaultToolsBuilder?.call(currType, _controller) ??
                      DrawingBoard.defaultTools(currType, _controller))
                  .map((DefToolItem item) => _DefToolItemWidget(item: item))
                  .toList(),
        );
      },
    ),
  ),
);

}
}

/// 默认工具项配置文件
class DefToolItem {
DefToolItem({
required this.icon,
required this.isActive,
this.onTap,
this.color,
this.activeColor = Colors.blue,
this.iconSize,
});

final Function()? onTap;
final bool isActive;

final IconData icon;
final double? iconSize;
final Color? color;
final Color activeColor;
}

/// 默认工具项 Widget
class _DefToolItemWidget extends StatelessWidget {
const _DefToolItemWidget({
required this.item,
});

final DefToolItem item;

@OverRide
Widget build(BuildContext context) {
return IconButton(
onPressed: item.onTap,
icon: Icon(
item.icon,
color: item.isActive ? item.activeColor : item.color,
size: item.iconSize,
),
);
}
}

class _DrawingboardState extends State {
// 绘制控制器
final DrawingController _drawingController = DrawingController();

@OverRide
void dispose() {
_drawingController.dispose();
super.dispose();
}

// 获取画板内容 Json getJsonList()
Future _getJson() async {
showDialog(
context: context,
builder: (BuildContext c) {
return Center(
child: Material(
color: Colors.white,
child: InkWell(
onTap: () => Navigator.pop(c),
child: Container(
constraints:
const BoxConstraints(maxWidth: 500, maxHeight: 800),
padding: const EdgeInsets.all(20.0),
child: SelectableText(
const JsonEncoder.withIndent(' ')
.convert(_drawingController.getJsonList()),
),
),
),
),
);
},
);
}

// 添加Json测试内容
void _addTestLine() {
_drawingController.addContent(StraightLine.fromJson(_testLine1));
_drawingController
.addContents([StraightLine.fromJson(_testLine2)]);
_drawingController.addContent(SimpleLine.fromJson(tData[0]));
// 添加更多绘图数据
}

@OverRide
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Column(
children: [
Expanded(
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return DrawingBoard(
boardPanEnabled: false,
boardScaleEnabled: false,
controller: _drawingController,
background: Container(
width: 800,
height: 600,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(widget.url ?? ''),
fit: BoxFit.cover,
),
),
),
showDefaultActions: true,
showDefaultTools: false,
);
},
),
),
const Padding(
padding: EdgeInsets.all(8.0),
child: SelectableText(
'https://github.com/fluttercandies/flutter_drawing_board',
style: TextStyle(fontSize: 10, color: Colors.white),
),
),
],
),
);
}
}

support pan & zoom control

please consider support adding
controller.zoom(Double scale)
and
controller.pan(Offset position)

so we can reset to original position & zoom easily

Export to PDF or SVG

Platforms

Android, iOS, Web

Description

I want to be able to save the drawing as a vector file such as PDF or SVG

Why

To be able to create and print high resolution graphics

[Feature request] load from json

Platforms

dart

Description

as there is a getJsonList function on the DrawingController it would be also good to reuse that json data to overhand as initial json payload. there is now no way to do that

Why

allows to save a snapshot of the drawing and reuse later

[Bug report]

Version

0.5.0

Platforms

dart, Android, iOS

Device Model

Android 11

flutter info

I used this package and modify it,s functionality according to my requirnments but one thing i am not able to modify is the moveable canvas. Canvas should be fixed at it,s position while drawing or writing anything onit kindly do these changes in the latest version

How to reproduce?

I used this package and modify it,s functionality according to my requirnments but one thing i am not able to modify is the moveable canvas. Canvas should be fixed at it,s position while drawing or writing anything onit kindly do these changes in the latest version

Logs

I used this package and modify it,s functionality according to my requirnments but one thing i am not able to modify is the moveable canvas. Canvas should be fixed at it,s position while drawing or writing anything onit kindly do these changes in the latest version

Example code (optional)

I used this package and modify it,s functionality according to my requirnments but one thing i am not able to modify is the moveable canvas. Canvas should be fixed at it,s position while drawing or writing anything onit kindly do these changes in the latest version

Contact

saddamafzal07gmail.com

[Bug report] can't draw in some cases, because of the false finger count

Version

0.5.0

Platforms

Android, iOS

Device Model

Galaxy note 9 (android 13)

flutter info

[✓] Flutter (Channel stable, 3.13.2, on Linux Mint 21.2 5.15.0-87-generic,
    locale en_US.UTF-8)
    • Flutter version 3.13.2 on channel stable at
      /home/hasan/development/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision ff5b5b5fa6 (10 weeks ago), 2023-08-24 08:12:28 -0500
    • Engine revision b20183e040
    • Dart version 3.1.0
    • DevTools version 2.25.0

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
    • Android SDK at /home/hasan/Android/Sdk
    • Platform android-33-ext4, build-tools 33.0.2
    • Java binary at: /home/hasan/development/android-studio/jbr/bin/java
    • Java version OpenJDK Runtime Environment (build
      11.0.15+0-b2043.56-8887301)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop
    • Ubuntu clang version 14.0.0-1ubuntu1.1
    • cmake version 3.22.1
    • ninja version 1.10.1
    • pkg-config version 0.29.2

[✓] Android Studio (version 2022.1)
    • Android Studio at /home/hasan/development/android-studio
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build
      11.0.15+0-b2043.56-8887301)

[✓] VS Code (version 1.83.1)
    • VS Code at /usr/share/code
    • Flutter extension version 3.74.0

[✓] Connected device (3 available)
    • SM N960F (mobile) • 2b32ca38291c7ece • android-arm64  • Android 13 (API
      33)
    • Linux (desktop)   • linux            • linux-x64      • Linux Mint 21.2
      5.15.0-87-generic
    • Chrome (web)      • chrome           • web-javascript • Google Chrome
      118.0.5993.117

[✓] Network resources
    • All expected network resources are available.

• No issues found!

How to reproduce?

I recorded this video from the example of this repo.

2023-11-01_11-55-35.mp4

After some debugging, the issue is with the finger count. Somehow it will stuck with a value that makes the board in the zoom mode only (because it indicates more than one finger, but it is actually only one finger).
Some of my users have this issue as well when they try to zoom in, then the same issue happens.
Adding onPointerCancel that reduces the finger count to the listener in the source code as suggested in this issue solves the issue.
But I am using your great library in an important app so I am not sure if I should fork it to apply this solution or it could cause other issues.

Logs

No response

Example code (optional)

No response

Contact

No response

Load drawing content of Eraser type not work

First I draw my first line then I erase it in center of the line like image as below.

Screen Shot 2023-02-07 at 4 58 19 PM

I store all my draw in one list for loading after I re-open whiteboard, but it didn't work with eraser type.

_drawingController.addContent(SmoothLine.fromJson(data)); // It works
_drawingController.addContent(Eraser.fromJson(data)); // Not work

How can I move the background image with a left click?

Platforms

Windows

Description

When I set the paint content to EmptyContent I want to be able to move the image freely using a left click instead of a right click. How could I do that?

My code

No response

Try do it

No response

画笔绘制的时候感觉不灵敏

所有的工具都有一个问题, 就是按下去移动一点点位置还不能生效, 需要移动更多的距离才能有效果(不灵敏)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.