add hand_signature
This commit is contained in:
parent
fb66274bf7
commit
83475a9128
17 changed files with 3300 additions and 4 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
adaptive_number: ea9178fdd4d82ac45cf0ec966ac870dae661124f
|
adaptive_number: ea9178fdd4d82ac45cf0ec966ac870dae661124f
|
||||||
dots_indicator: 508f5883ac79bdbc10254092de3f28f571d261cd
|
dots_indicator: 508f5883ac79bdbc10254092de3f28f571d261cd
|
||||||
ed25519_edwards: 7353ba759ea9f4646cbf481c2ef949625c8ce4cf
|
ed25519_edwards: 7353ba759ea9f4646cbf481c2ef949625c8ce4cf
|
||||||
|
hand_signature: 1beedb164d093643365b0832277c377353c7464f
|
||||||
hashlib: 983cdbd5ee2529b908876b57a7217c09c6bc148a
|
hashlib: 983cdbd5ee2529b908876b57a7217c09c6bc148a
|
||||||
hashlib_codecs: 2a966c37c3b9b1f5541ae88e99ab34acf3fc968b
|
hashlib_codecs: 2a966c37c3b9b1f5541ae88e99ab34acf3fc968b
|
||||||
introduction_screen: 4a90e557630b28834479ed9c64a9d2d0185d8e48
|
introduction_screen: 4a90e557630b28834479ed9c64a9d2d0185d8e48
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,6 @@ libsignal_protocol_dart:
|
||||||
git: https://github.com/bcgit/pc-dart.git
|
git: https://github.com/bcgit/pc-dart.git
|
||||||
x25519:
|
x25519:
|
||||||
git: https://github.com/Tougee/curve25519.git
|
git: https://github.com/Tougee/curve25519.git
|
||||||
|
|
||||||
|
hand_signature:
|
||||||
|
git: https://github.com/RomanBase/hand_signature.git
|
||||||
71
dots_indicator/pubspec.lock
Normal file
71
dots_indicator/pubspec.lock
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.19.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.11.1"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.17.0"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.8.0-0 <4.0.0"
|
||||||
21
hand_signature/LICENSE
Normal file
21
hand_signature/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 RomanBase
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
5
hand_signature/lib/signature.dart
Normal file
5
hand_signature/lib/signature.dart
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
export 'src/signature_control.dart';
|
||||||
|
export 'src/signature_drawer.dart';
|
||||||
|
export 'src/signature_paint.dart';
|
||||||
|
export 'src/signature_painter.dart';
|
||||||
|
export 'src/signature_view.dart';
|
||||||
1399
hand_signature/lib/src/signature_control.dart
Normal file
1399
hand_signature/lib/src/signature_control.dart
Normal file
File diff suppressed because it is too large
Load diff
204
hand_signature/lib/src/signature_drawer.dart
Normal file
204
hand_signature/lib/src/signature_drawer.dart
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../signature.dart';
|
||||||
|
import 'utils.dart';
|
||||||
|
|
||||||
|
/// An abstract base class for custom signature drawing logic.
|
||||||
|
///
|
||||||
|
/// Subclasses must implement the [paint] method to define how a signature path is rendered on a canvas.
|
||||||
|
abstract class HandSignatureDrawer {
|
||||||
|
/// Creates a [HandSignatureDrawer] instance.
|
||||||
|
const HandSignatureDrawer();
|
||||||
|
|
||||||
|
/// Paints the given [paths] onto the [canvas].
|
||||||
|
///
|
||||||
|
/// [canvas] The canvas to draw on.
|
||||||
|
/// [size] The size of the canvas.
|
||||||
|
/// [paths] A list of [CubicPath] objects representing the signature to be drawn.
|
||||||
|
void paint(Canvas canvas, Size size, List<CubicPath> paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A concrete implementation of [HandSignatureDrawer] that draws signature as simple lines.
|
||||||
|
class LineSignatureDrawer extends HandSignatureDrawer {
|
||||||
|
/// The color used to paint the lines.
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// The stroke width of the lines.
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
/// Creates a [LineSignatureDrawer] with the specified [width] and [color].
|
||||||
|
const LineSignatureDrawer({
|
||||||
|
this.width = 1.0,
|
||||||
|
this.color = Colors.black,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
||||||
|
final paint = Paint()
|
||||||
|
..color = color
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeCap = StrokeCap.round
|
||||||
|
..strokeJoin = StrokeJoin.round
|
||||||
|
..strokeWidth = width;
|
||||||
|
|
||||||
|
for (final path in paths) {
|
||||||
|
if (path.isFilled) {
|
||||||
|
canvas.drawPath(PathUtil.toLinePath(path.lines), paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A concrete implementation of [HandSignatureDrawer] that draws signatures as arcs,
|
||||||
|
/// with varying width based on the arc's size property.
|
||||||
|
class ArcSignatureDrawer extends HandSignatureDrawer {
|
||||||
|
/// The color used to paint the arcs.
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// The minimal stroke width of the arcs.
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
/// The maximal stroke width of the arcs.
|
||||||
|
final double maxWidth;
|
||||||
|
|
||||||
|
/// Creates an [ArcSignatureDrawer] with the specified [width], [maxWidth], and [color].
|
||||||
|
const ArcSignatureDrawer({
|
||||||
|
this.width = 1.0,
|
||||||
|
this.maxWidth = 10.0,
|
||||||
|
this.color = Colors.black,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
||||||
|
final paint = Paint()
|
||||||
|
..color = color
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeCap = StrokeCap.round
|
||||||
|
..strokeJoin = StrokeJoin.round
|
||||||
|
..strokeWidth = width;
|
||||||
|
|
||||||
|
for (final path in paths) {
|
||||||
|
final arcs = path.toArcs();
|
||||||
|
for (final arc in arcs) {
|
||||||
|
paint.strokeWidth = width + (maxWidth - width) * arc.size;
|
||||||
|
canvas.drawPath(arc.path, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A concrete implementation of [HandSignatureDrawer] that draws signature as filled Path.
|
||||||
|
class ShapeSignatureDrawer extends HandSignatureDrawer {
|
||||||
|
/// The color used to fill the shapes.
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// The base width of the shape.
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
/// The maximum width of the shape.
|
||||||
|
final double maxWidth;
|
||||||
|
|
||||||
|
/// Creates a [ShapeSignatureDrawer] with the specified [width], [maxWidth], and [color].
|
||||||
|
const ShapeSignatureDrawer({
|
||||||
|
this.width = 1.0,
|
||||||
|
this.maxWidth = 10.0,
|
||||||
|
this.color = Colors.black,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
||||||
|
final paint = Paint()
|
||||||
|
..color = color
|
||||||
|
..strokeWidth = 0.0; // Stroke width is handled by the shape path itself
|
||||||
|
|
||||||
|
for (final path in paths) {
|
||||||
|
if (path.isFilled) {
|
||||||
|
if (path.isDot) {
|
||||||
|
// If it's a dot, draw a circle
|
||||||
|
canvas.drawCircle(path.lines[0], path.lines[0].startRadius(width, maxWidth), paint);
|
||||||
|
} else {
|
||||||
|
// Otherwise, draw the filled shape path
|
||||||
|
canvas.drawPath(PathUtil.toShapePath(path.lines, width, maxWidth), paint);
|
||||||
|
|
||||||
|
// Draw circles at the start and end of the path for a smoother look
|
||||||
|
final first = path.lines.first;
|
||||||
|
final last = path.lines.last;
|
||||||
|
|
||||||
|
canvas.drawCircle(first.start, first.startRadius(width, maxWidth), paint);
|
||||||
|
canvas.drawCircle(last.end, last.endRadius(width, maxWidth), paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [HandSignatureDrawer] that dynamically selects the drawing type based on
|
||||||
|
/// arguments provided in the [CubicPath]'s setup.
|
||||||
|
class DynamicSignatureDrawer extends HandSignatureDrawer {
|
||||||
|
final SignatureDrawType type;
|
||||||
|
|
||||||
|
/// The color used to paint the arcs.
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// The minimal stroke width of the arcs.
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
/// The maximal stroke width of the arcs.
|
||||||
|
final double maxWidth;
|
||||||
|
|
||||||
|
const DynamicSignatureDrawer({
|
||||||
|
this.type = SignatureDrawType.shape,
|
||||||
|
this.width = 1.0,
|
||||||
|
this.maxWidth = 10.0,
|
||||||
|
this.color = Colors.black,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
||||||
|
for (final path in paths) {
|
||||||
|
// Retrieve drawing parameters from path arguments, with fallbacks
|
||||||
|
final type = path.setup.args?['type'] ?? this.type.name;
|
||||||
|
final color = Color(path.setup.args?['color'] ?? this.color.toHex32());
|
||||||
|
final width = path.setup.args?['width'] ?? this.width;
|
||||||
|
final maxWidth = path.setup.args?['max_width'] ?? this.maxWidth;
|
||||||
|
|
||||||
|
HandSignatureDrawer drawer;
|
||||||
|
|
||||||
|
// Select the appropriate drawer based on the 'type' argument
|
||||||
|
switch (type) {
|
||||||
|
case 'line':
|
||||||
|
drawer = LineSignatureDrawer(color: color, width: width);
|
||||||
|
break;
|
||||||
|
case 'arc':
|
||||||
|
drawer = ArcSignatureDrawer(color: color, width: width, maxWidth: maxWidth);
|
||||||
|
break;
|
||||||
|
case 'shape':
|
||||||
|
drawer = ShapeSignatureDrawer(color: color, width: width, maxWidth: maxWidth);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Default to ShapeSignatureDrawer if type is unknown or not provided
|
||||||
|
drawer = ShapeSignatureDrawer(color: color, width: width, maxWidth: maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint the current path using the selected drawer
|
||||||
|
drawer.paint(canvas, size, [path]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [HandSignatureDrawer] that combines multiple drawers, allowing for complex
|
||||||
|
/// drawing effects by applying each drawer in sequence.
|
||||||
|
class MultiSignatureDrawer extends HandSignatureDrawer {
|
||||||
|
/// The collection of [HandSignatureDrawer]s to be applied.
|
||||||
|
final Iterable<HandSignatureDrawer> drawers;
|
||||||
|
|
||||||
|
/// Creates a [MultiSignatureDrawer] with the given [drawers].
|
||||||
|
const MultiSignatureDrawer({required this.drawers});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
||||||
|
for (final drawer in drawers) {
|
||||||
|
drawer.paint(canvas, size, paths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
hand_signature/lib/src/signature_paint.dart
Normal file
84
hand_signature/lib/src/signature_paint.dart
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../signature.dart';
|
||||||
|
|
||||||
|
/// A [StatefulWidget] that uses [CustomPaint] to render a hand signature.
|
||||||
|
/// It rebuilds automatically whenever the signature data managed by [HandSignatureControl] changes.
|
||||||
|
///
|
||||||
|
/// This widget is typically used internally by [HandSignature] and [HandSignatureView].
|
||||||
|
class HandSignaturePaint extends StatefulWidget {
|
||||||
|
/// The controller that manages the signature paths and notifies listeners of changes.
|
||||||
|
final HandSignatureControl control;
|
||||||
|
|
||||||
|
/// The drawer responsible for rendering the signature paths on the canvas.
|
||||||
|
final HandSignatureDrawer drawer;
|
||||||
|
|
||||||
|
/// Optional callback that is invoked when the canvas size changes.
|
||||||
|
///
|
||||||
|
/// TODO: This callback should ideally be handled within the State of this widget
|
||||||
|
/// or by the [HandSignatureControl] itself, rather than being exposed here.
|
||||||
|
final bool Function(Size size)? onSize;
|
||||||
|
|
||||||
|
/// Creates a [HandSignaturePaint] widget.
|
||||||
|
///
|
||||||
|
/// [key] Controls how one widget replaces another widget in the tree.
|
||||||
|
/// [control] The [HandSignatureControl] instance that provides the signature data.
|
||||||
|
/// [drawer] The [HandSignatureDrawer] instance that defines how the signature is painted.
|
||||||
|
/// [onSize] An optional callback for canvas size changes.
|
||||||
|
const HandSignaturePaint({
|
||||||
|
Key? key,
|
||||||
|
required this.control,
|
||||||
|
required this.drawer,
|
||||||
|
this.onSize,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_HandSignaturePaintState createState() => _HandSignaturePaintState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state class for [HandSignaturePaint].
|
||||||
|
///
|
||||||
|
/// This state subscribes to the [HandSignatureControl] to listen for changes
|
||||||
|
/// in signature data and triggers a rebuild of the widget when updates occur.
|
||||||
|
class _HandSignaturePaintState extends State<HandSignaturePaint> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
// Add a listener to the control to trigger a rebuild on data changes.
|
||||||
|
widget.control.addListener(_updateState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Callback method to trigger a widget rebuild.
|
||||||
|
void _updateState() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(HandSignaturePaint oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
// If the control instance changes, update the listener.
|
||||||
|
if (oldWidget.control != widget.control) {
|
||||||
|
oldWidget.control.removeListener(_updateState);
|
||||||
|
widget.control.addListener(_updateState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// Use CustomPaint to draw the signature using PathSignaturePainter.
|
||||||
|
return CustomPaint(
|
||||||
|
painter: PathSignaturePainter(
|
||||||
|
paths: widget.control.paths,
|
||||||
|
drawer: widget.drawer,
|
||||||
|
onSize: widget.onSize,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
// Remove the listener when the widget is disposed to prevent memory leaks.
|
||||||
|
widget.control.removeListener(_updateState);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
150
hand_signature/lib/src/signature_painter.dart
Normal file
150
hand_signature/lib/src/signature_painter.dart
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../signature.dart';
|
||||||
|
|
||||||
|
/// Defines the different types of drawing styles for a signature path.
|
||||||
|
enum SignatureDrawType {
|
||||||
|
/// Draws the signature as a simple line with a constant stroke width.
|
||||||
|
/// This is the most basic and performant drawing style.
|
||||||
|
line,
|
||||||
|
|
||||||
|
/// Draws the signature as a series of small arcs.
|
||||||
|
/// This style can produce a visually appealing result but might have
|
||||||
|
/// a higher performance cost due to the large number of individual arcs drawn.
|
||||||
|
arc,
|
||||||
|
|
||||||
|
/// Draws the signature by creating a closed shape for each segment of the line and filling it.
|
||||||
|
/// This method generally provides a good balance between visual quality and performance,
|
||||||
|
/// resulting in a smooth, filled signature appearance.
|
||||||
|
shape,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [CustomPainter] responsible for rendering [CubicPath]s onto a canvas.
|
||||||
|
/// This painter is used internally by the signature drawing widgets.
|
||||||
|
class PathSignaturePainter extends CustomPainter {
|
||||||
|
/// The list of [CubicPath]s that need to be painted.
|
||||||
|
final List<CubicPath> paths;
|
||||||
|
|
||||||
|
/// The [HandSignatureDrawer] instance that defines the actual drawing logic.
|
||||||
|
final HandSignatureDrawer drawer;
|
||||||
|
|
||||||
|
/// Optional callback that is invoked when the canvas size changes.
|
||||||
|
///
|
||||||
|
/// TODO: This callback should ideally be handled within the widget's state
|
||||||
|
/// or by the [HandSignatureControl] itself.
|
||||||
|
final bool Function(Size size)? onSize;
|
||||||
|
|
||||||
|
/// Creates a [PathSignaturePainter].
|
||||||
|
///
|
||||||
|
/// [paths] The list of signature paths to draw.
|
||||||
|
/// [drawer] The drawer that will perform the actual painting.
|
||||||
|
/// [onSize] An optional callback for canvas size changes.
|
||||||
|
const PathSignaturePainter({
|
||||||
|
required this.paths,
|
||||||
|
required this.drawer,
|
||||||
|
this.onSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
// TODO: This size handling logic should be moved to the widget/state.
|
||||||
|
if (onSize != null) {
|
||||||
|
if (onSize!.call(size)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no paths, nothing to draw.
|
||||||
|
if (paths.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate the actual painting to the provided drawer.
|
||||||
|
drawer.paint(canvas, size, paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||||
|
// Always repaint to ensure the latest signature is displayed.
|
||||||
|
// A more optimized approach might compare old and new paths.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [CustomPainter] used for debugging purposes, specifically to visualize
|
||||||
|
/// the control points and segments of a signature path.
|
||||||
|
class DebugSignaturePainterCP extends CustomPainter {
|
||||||
|
/// The [HandSignatureControl] instance providing the signature data.
|
||||||
|
final HandSignatureControl control;
|
||||||
|
|
||||||
|
/// Whether to draw all control points.
|
||||||
|
final bool cp;
|
||||||
|
|
||||||
|
/// Whether to draw control points related to the start of segments.
|
||||||
|
final bool cpStart;
|
||||||
|
|
||||||
|
/// Whether to draw control points related to the end of segments.
|
||||||
|
final bool cpEnd;
|
||||||
|
|
||||||
|
/// Whether to draw dots at the control points and segment ends.
|
||||||
|
final bool dot;
|
||||||
|
|
||||||
|
/// The color used for drawing the debug elements.
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// Creates a [DebugSignaturePainterCP].
|
||||||
|
///
|
||||||
|
/// [control] The signature control providing the data to debug.
|
||||||
|
/// [cp] Whether to draw all control points.
|
||||||
|
/// [cpStart] Whether to draw control points at the start of segments.
|
||||||
|
/// [cpEnd] Whether to draw control points at the end of segments.
|
||||||
|
/// [dot] Whether to draw dots at the control points and segment ends.
|
||||||
|
/// [color] The color for the debug drawings.
|
||||||
|
const DebugSignaturePainterCP({
|
||||||
|
required this.control,
|
||||||
|
this.cp = false,
|
||||||
|
this.cpStart = true,
|
||||||
|
this.cpEnd = true,
|
||||||
|
this.dot = true,
|
||||||
|
this.color = Colors.red,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final paint = Paint()
|
||||||
|
..color = color
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeCap = StrokeCap.round
|
||||||
|
..strokeJoin = StrokeJoin.round
|
||||||
|
..strokeWidth = 1.0;
|
||||||
|
|
||||||
|
// Iterate through each line segment in the control's paths.
|
||||||
|
control.lines.forEach((line) {
|
||||||
|
// Draw lines and dots for start control points if enabled.
|
||||||
|
if (cpStart) {
|
||||||
|
canvas.drawLine(line.start, line.cpStart, paint);
|
||||||
|
if (dot) {
|
||||||
|
canvas.drawCircle(line.cpStart, 1.0, paint);
|
||||||
|
canvas.drawCircle(line.start, 1.0, paint);
|
||||||
|
}
|
||||||
|
} else if (cp) {
|
||||||
|
// Draw only the control point dot if cpStart is false but cp is true.
|
||||||
|
canvas.drawCircle(line.cpStart, 1.0, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw lines and dots for end control points if enabled.
|
||||||
|
if (cpEnd) {
|
||||||
|
canvas.drawLine(line.end, line.cpEnd, paint);
|
||||||
|
if (dot) {
|
||||||
|
canvas.drawCircle(line.cpEnd, 1.0, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||||
|
// Always repaint to show the latest debug information.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
217
hand_signature/lib/src/signature_view.dart
Normal file
217
hand_signature/lib/src/signature_view.dart
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../signature.dart';
|
||||||
|
|
||||||
|
typedef _GestureEvent = Function(Offset position, double pressure);
|
||||||
|
|
||||||
|
/// A widget that provides a canvas for drawing hand signatures.
|
||||||
|
/// It combines [HandSignaturePaint] for rendering and [RawGestureDetector] for input handling,
|
||||||
|
/// sending gesture events to a [HandSignatureControl].
|
||||||
|
class HandSignature extends StatelessWidget {
|
||||||
|
/// The controller that manages the creation and manipulation of signature paths.
|
||||||
|
final HandSignatureControl control;
|
||||||
|
|
||||||
|
/// @Deprecated('This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the drawing type and style.')
|
||||||
|
/// The type of signature path to draw.
|
||||||
|
@Deprecated(
|
||||||
|
'This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the drawing type and style.')
|
||||||
|
final SignatureDrawType type;
|
||||||
|
|
||||||
|
/// @Deprecated('This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the drawing color.')
|
||||||
|
/// The single color used for painting the signature.
|
||||||
|
@Deprecated(
|
||||||
|
'This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the drawing color.')
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// @Deprecated('This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the minimal stroke width.')
|
||||||
|
/// The minimal stroke width of the signature path.
|
||||||
|
@Deprecated(
|
||||||
|
'This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the minimal stroke width.')
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
/// @Deprecated('This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the maximal stroke width.')
|
||||||
|
/// The maximal stroke width of the signature path.
|
||||||
|
@Deprecated(
|
||||||
|
'This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the maximal stroke width.')
|
||||||
|
final double maxWidth;
|
||||||
|
|
||||||
|
/// The [HandSignatureDrawer] responsible for rendering the signature.
|
||||||
|
/// If `null`, a default drawer will be created based on the deprecated `type`, `color`, `width`, and `maxWidth` properties.
|
||||||
|
final HandSignatureDrawer? drawer;
|
||||||
|
|
||||||
|
/// The set of [PointerDeviceKind]s that this widget should recognize.
|
||||||
|
///
|
||||||
|
/// For example, to only accept stylus input:
|
||||||
|
/// ```dart
|
||||||
|
/// supportedDevices: {
|
||||||
|
/// PointerDeviceKind.stylus,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// If `null`, it accepts input from all pointer device types.
|
||||||
|
final Set<PointerDeviceKind>? supportedDevices;
|
||||||
|
|
||||||
|
/// Optional callback function invoked when a new signature path drawing starts (pointer down event).
|
||||||
|
final VoidCallback? onPointerDown;
|
||||||
|
|
||||||
|
/// Optional callback function invoked when a signature path drawing ends (pointer up or cancel event).
|
||||||
|
final VoidCallback? onPointerUp;
|
||||||
|
|
||||||
|
/// Creates a [HandSignature] widget.
|
||||||
|
///
|
||||||
|
/// [key] Controls how one widget replaces another widget in the tree.
|
||||||
|
/// [control] The [HandSignatureControl] instance to manage the signature data.
|
||||||
|
/// [type] The deprecated drawing type for the signature.
|
||||||
|
/// [color] The deprecated color for the signature.
|
||||||
|
/// [width] The deprecated minimal width for the signature.
|
||||||
|
/// [maxWidth] The deprecated maximal width for the signature.
|
||||||
|
/// [drawer] The custom drawer to use for rendering the signature.
|
||||||
|
/// [onPointerDown] Callback for when drawing starts.
|
||||||
|
/// [onPointerUp] Callback for when drawing ends.
|
||||||
|
/// [supportedDevices] The set of pointer device types to recognize.
|
||||||
|
const HandSignature({
|
||||||
|
Key? key,
|
||||||
|
required this.control,
|
||||||
|
@Deprecated(
|
||||||
|
'This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the drawing type and style.')
|
||||||
|
this.type = SignatureDrawType.shape,
|
||||||
|
@Deprecated(
|
||||||
|
'This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the drawing color.')
|
||||||
|
this.color = Colors.black,
|
||||||
|
@Deprecated(
|
||||||
|
'This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the minimal stroke width.')
|
||||||
|
this.width = 1.0,
|
||||||
|
@Deprecated(
|
||||||
|
'This property is deprecated since 3.1.0. Use the `drawer` property instead to specify the maximal stroke width.')
|
||||||
|
this.maxWidth = 10.0,
|
||||||
|
this.drawer,
|
||||||
|
this.onPointerDown,
|
||||||
|
this.onPointerUp,
|
||||||
|
this.supportedDevices,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
void _startPath(Offset point, double pressure) {
|
||||||
|
if (!control.hasActivePath) {
|
||||||
|
onPointerDown?.call();
|
||||||
|
control.startPath(point, pressure: pressure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _endPath(Offset point, double pressure) {
|
||||||
|
if (control.hasActivePath) {
|
||||||
|
control.closePath(pressure: pressure);
|
||||||
|
onPointerUp?.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
control.params = SignaturePaintParams(
|
||||||
|
color: color,
|
||||||
|
strokeWidth: width,
|
||||||
|
maxStrokeWidth: maxWidth,
|
||||||
|
);
|
||||||
|
|
||||||
|
return ClipRRect(
|
||||||
|
child: RawGestureDetector(
|
||||||
|
gestures: <Type, GestureRecognizerFactory>{
|
||||||
|
_SingleGestureRecognizer:
|
||||||
|
GestureRecognizerFactoryWithHandlers<_SingleGestureRecognizer>(
|
||||||
|
() => _SingleGestureRecognizer(
|
||||||
|
debugOwner: this, supportedDevices: supportedDevices),
|
||||||
|
(instance) {
|
||||||
|
instance.onStart =
|
||||||
|
(position, pressure) => _startPath(position, pressure);
|
||||||
|
instance.onUpdate = (position, pressure) =>
|
||||||
|
control.alterPath(position, pressure: pressure);
|
||||||
|
instance.onEnd =
|
||||||
|
(position, pressure) => _endPath(position, pressure);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
child: HandSignaturePaint(
|
||||||
|
control: control,
|
||||||
|
drawer: drawer ??
|
||||||
|
switch (type) {
|
||||||
|
SignatureDrawType.line =>
|
||||||
|
LineSignatureDrawer(color: color, width: width),
|
||||||
|
SignatureDrawType.arc => ArcSignatureDrawer(
|
||||||
|
color: color, width: width, maxWidth: maxWidth),
|
||||||
|
SignatureDrawType.shape => ShapeSignatureDrawer(
|
||||||
|
color: color, width: width, maxWidth: maxWidth),
|
||||||
|
},
|
||||||
|
onSize: control.notifyDimension,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A custom [GestureRecognizer] that processes only a single input pointer
|
||||||
|
/// for signature drawing. It extends [OneSequenceGestureRecognizer] to ensure
|
||||||
|
/// that only one gesture is recognized at a time.
|
||||||
|
class _SingleGestureRecognizer extends OneSequenceGestureRecognizer {
|
||||||
|
@override
|
||||||
|
String get debugDescription => 'single_gesture_recognizer';
|
||||||
|
|
||||||
|
/// Callback function for when a pointer starts interacting with the widget.
|
||||||
|
_GestureEvent? onStart;
|
||||||
|
|
||||||
|
/// Callback function for when a pointer moves while interacting with the widget.
|
||||||
|
_GestureEvent? onUpdate;
|
||||||
|
|
||||||
|
/// Callback function for when a pointer stops interacting with the widget.
|
||||||
|
_GestureEvent? onEnd;
|
||||||
|
|
||||||
|
/// A flag indicating whether a pointer is currently active (down).
|
||||||
|
bool pointerActive = false;
|
||||||
|
|
||||||
|
/// Creates a [_SingleGestureRecognizer].
|
||||||
|
///
|
||||||
|
/// [debugOwner] The object that is debugging this recognizer.
|
||||||
|
/// [supportedDevices] The set of [PointerDeviceKind]s that this recognizer should respond to.
|
||||||
|
/// If `null`, it defaults to all available pointer device kinds.
|
||||||
|
_SingleGestureRecognizer({
|
||||||
|
super.debugOwner,
|
||||||
|
Set<PointerDeviceKind>? supportedDevices,
|
||||||
|
}) : super(
|
||||||
|
supportedDevices:
|
||||||
|
supportedDevices ?? PointerDeviceKind.values.toSet(),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addAllowedPointer(PointerDownEvent event) {
|
||||||
|
// Only allow a new pointer if no other pointer is currently active.
|
||||||
|
if (pointerActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Start tracking the pointer.
|
||||||
|
startTrackingPointer(event.pointer, event.transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void handleEvent(PointerEvent event) {
|
||||||
|
// Handle different types of pointer events.
|
||||||
|
if (event is PointerMoveEvent) {
|
||||||
|
// If it's a move event, call the onUpdate callback.
|
||||||
|
onUpdate?.call(event.localPosition, event.pressure);
|
||||||
|
} else if (event is PointerDownEvent) {
|
||||||
|
// If it's a down event, set pointer as active and call onStart.
|
||||||
|
pointerActive = true;
|
||||||
|
onStart?.call(event.localPosition, event.pressure);
|
||||||
|
} else if (event is PointerUpEvent) {
|
||||||
|
// If it's an up event, set pointer as inactive and call onEnd.
|
||||||
|
pointerActive = false;
|
||||||
|
onEnd?.call(event.localPosition, event.pressure);
|
||||||
|
} else if (event is PointerCancelEvent) {
|
||||||
|
// If the pointer interaction is cancelled, set pointer as inactive and call onEnd.
|
||||||
|
pointerActive = false;
|
||||||
|
onEnd?.call(event.localPosition, event.pressure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didStopTrackingLastPointer(int pointer) {
|
||||||
|
// No specific action needed when the last pointer stops tracking.
|
||||||
|
}
|
||||||
|
}
|
||||||
634
hand_signature/lib/src/utils.dart
Normal file
634
hand_signature/lib/src/utils.dart
Normal file
|
|
@ -0,0 +1,634 @@
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../signature.dart';
|
||||||
|
|
||||||
|
/// A constant representing 2 * PI (360 degrees in radians).
|
||||||
|
const pi2 = math.pi * 2.0;
|
||||||
|
|
||||||
|
/// Extension methods for [Color] to provide utility functions.
|
||||||
|
extension ColorEx on Color {
|
||||||
|
/// Returns the hexadecimal string representation of this color,
|
||||||
|
/// excluding the alpha component (e.g., '#RRGGBB').
|
||||||
|
String get hexValue =>
|
||||||
|
'#${toHex32().toRadixString(16)}'.replaceRange(1, 3, '');
|
||||||
|
|
||||||
|
/// {toARGB32} was introduced in Flutter 3.28
|
||||||
|
int toHex32() {
|
||||||
|
int floatToInt8(double x) {
|
||||||
|
return (x * 255.0).round() & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return floatToInt8(a) << 24 |
|
||||||
|
floatToInt8(r) << 16 |
|
||||||
|
floatToInt8(g) << 8 |
|
||||||
|
floatToInt8(b) << 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extension methods for [Offset] to provide vector and geometric utility functions.
|
||||||
|
extension OffsetEx on Offset {
|
||||||
|
/// Calculates the component-wise distance (difference) between this offset and [other].
|
||||||
|
/// Returns an [Offset] representing (other.dx - this.dx, other.dy - this.dy).
|
||||||
|
Offset axisDistanceTo(Offset other) => other - this;
|
||||||
|
|
||||||
|
/// Calculates the Euclidean distance between this offset and [other].
|
||||||
|
double distanceTo(Offset other) {
|
||||||
|
final len = axisDistanceTo(other);
|
||||||
|
return math.sqrt(len.dx * len.dx + len.dy * len.dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the angle (in radians) from this offset to [other] relative to the positive x-axis.
|
||||||
|
double angleTo(Offset other) {
|
||||||
|
final len = axisDistanceTo(other);
|
||||||
|
return math.atan2(len.dy, len.dx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the unit vector (direction) from this offset to [other].
|
||||||
|
/// Returns an [Offset] representing the normalized direction.
|
||||||
|
/// If the distance is zero, returns an [Offset] of (0,0).
|
||||||
|
Offset directionTo(Offset other) {
|
||||||
|
final len = axisDistanceTo(other);
|
||||||
|
final m = math.sqrt(len.dx * len.dx + len.dy * len.dy);
|
||||||
|
return Offset(m == 0 ? 0 : (len.dx / m), m == 0 ? 0 : (len.dy / m));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotates this offset by [radians] around the origin (0,0).
|
||||||
|
/// Returns a new [Offset] representing the rotated point.
|
||||||
|
Offset rotate(double radians) {
|
||||||
|
final s = math.sin(radians);
|
||||||
|
final c = math.cos(radians);
|
||||||
|
final x = dx * c - dy * s;
|
||||||
|
final y = dx * s + dy * c;
|
||||||
|
return Offset(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotates this offset by [radians] around a specified [center] point.
|
||||||
|
/// Returns a new [Offset] representing the rotated point.
|
||||||
|
Offset rotateAround(Offset center, double radians) {
|
||||||
|
return (this - center).rotate(radians) + center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extension methods for [Path] to provide simplified drawing commands.
|
||||||
|
extension PathEx on Path {
|
||||||
|
/// Moves the current point of the path to the given [offset].
|
||||||
|
void start(Offset offset) => moveTo(offset.dx, offset.dy);
|
||||||
|
|
||||||
|
/// Adds a cubic Bezier curve segment to the path.
|
||||||
|
///
|
||||||
|
/// [cpStart] The first control point.
|
||||||
|
/// [cpEnd] The second control point.
|
||||||
|
/// [end] The end point of the curve.
|
||||||
|
void cubic(Offset cpStart, Offset cpEnd, Offset end) =>
|
||||||
|
cubicTo(cpStart.dx, cpStart.dy, cpEnd.dx, cpEnd.dy, end.dx, end.dy);
|
||||||
|
|
||||||
|
/// Adds a straight line segment from the current point to the given [offset].
|
||||||
|
void line(Offset offset) => lineTo(offset.dx, offset.dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extension methods for [Size] to provide utility functions.
|
||||||
|
extension SizeExt on Size {
|
||||||
|
/// Scales this size down to fit within [other] size while maintaining aspect ratio.
|
||||||
|
/// Returns a new [Size] that fits within [other].
|
||||||
|
Size scaleToFit(Size other) {
|
||||||
|
final scale = math.min(
|
||||||
|
other.width / width,
|
||||||
|
other.height / height,
|
||||||
|
);
|
||||||
|
|
||||||
|
return this * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect toRect() => Rect.fromLTRB(0.0, 0.0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A utility class providing static methods for common path manipulation and geometric calculations.
|
||||||
|
/// This includes bounding box calculations, transformations (translate, scale, normalize),
|
||||||
|
/// and conversions between different path representations.
|
||||||
|
///
|
||||||
|
/// TODO: Consider refactoring and cleaning up this class for better organization and clarity.
|
||||||
|
class PathUtil {
|
||||||
|
/// Private constructor to prevent direct instantiation of this utility class.
|
||||||
|
const PathUtil._();
|
||||||
|
|
||||||
|
/// Calculates the bounding box (minimum [Rect]) for a list of [Offset] points.
|
||||||
|
///
|
||||||
|
/// [data] The list of [Offset] points.
|
||||||
|
/// [minSize] The minimum width/height for the bounding box. If the calculated
|
||||||
|
/// size is smaller, it will be expanded to this minimum.
|
||||||
|
/// [radius] An additional padding to add around the calculated bounds.
|
||||||
|
/// Returns a [Rect] representing the bounding box.
|
||||||
|
/// Calculates the bounding box (minimum [Rect]) for a list of [Offset] points.
|
||||||
|
///
|
||||||
|
/// [data] The list of [Offset] points.
|
||||||
|
/// [minSize] The minimum width/height for the bounding box. If the calculated
|
||||||
|
/// size is smaller, it will be expanded to this minimum.
|
||||||
|
/// [radius] An additional padding to add around the calculated bounds.
|
||||||
|
/// Returns a [Rect] representing the bounding box.
|
||||||
|
static Rect bounds(List<Offset> data,
|
||||||
|
{double minSize = 2.0, double radius = 0.0}) {
|
||||||
|
double left = data[0].dx;
|
||||||
|
double top = data[0].dy;
|
||||||
|
double right = data[0].dx;
|
||||||
|
double bottom = data[0].dy;
|
||||||
|
|
||||||
|
for (final point in data) {
|
||||||
|
final x = point.dx;
|
||||||
|
final y = point.dy;
|
||||||
|
|
||||||
|
if (x < left) {
|
||||||
|
left = x;
|
||||||
|
} else if (x > right) {
|
||||||
|
right = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < top) {
|
||||||
|
top = y;
|
||||||
|
} else if (y > bottom) {
|
||||||
|
bottom = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final hSize = right - left;
|
||||||
|
final vSize = bottom - top;
|
||||||
|
|
||||||
|
if (hSize < minSize) {
|
||||||
|
final dif = (minSize - hSize) * 0.5;
|
||||||
|
left -= dif;
|
||||||
|
right += dif;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vSize < minSize) {
|
||||||
|
final dif = (minSize - vSize) * 0.5;
|
||||||
|
top -= dif;
|
||||||
|
bottom += dif;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Rect.fromLTRB(
|
||||||
|
left - radius, top - radius, right + radius, bottom + radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the bounding box (minimum [Rect]) for a list of lists of [Offset] points.
|
||||||
|
/// This is useful for finding the overall bounds of multiple paths.
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of [Offset] points.
|
||||||
|
/// [minSize] The minimum width/height for the bounding box.
|
||||||
|
/// [radius] An additional padding to add around the calculated bounds.
|
||||||
|
/// Returns a [Rect] representing the combined bounding box.
|
||||||
|
/// Calculates the bounding box (minimum [Rect]) for a list of lists of [Offset] points.
|
||||||
|
/// This is useful for finding the overall bounds of multiple paths.
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of [Offset] points.
|
||||||
|
/// [minSize] The minimum width/height for the bounding box.
|
||||||
|
/// [radius] An additional padding to add around the calculated bounds.
|
||||||
|
/// Returns a [Rect] representing the combined bounding box.
|
||||||
|
static Rect boundsOf(List<List<Offset>> data,
|
||||||
|
{double minSize = 2.0, double radius = 0.0}) {
|
||||||
|
double left = data[0][0].dx;
|
||||||
|
double top = data[0][0].dy;
|
||||||
|
double right = data[0][0].dx;
|
||||||
|
double bottom = data[0][0].dy;
|
||||||
|
|
||||||
|
for (final set in data) {
|
||||||
|
for (final point in set) {
|
||||||
|
final x = point.dx;
|
||||||
|
final y = point.dy;
|
||||||
|
|
||||||
|
if (x < left) {
|
||||||
|
left = x;
|
||||||
|
} else if (x > right) {
|
||||||
|
right = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < top) {
|
||||||
|
top = y;
|
||||||
|
} else if (y > bottom) {
|
||||||
|
bottom = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final hSize = right - left;
|
||||||
|
final vSize = bottom - top;
|
||||||
|
|
||||||
|
if (hSize < minSize) {
|
||||||
|
final dif = (minSize - hSize) * 0.5;
|
||||||
|
left -= dif;
|
||||||
|
right += dif;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vSize < minSize) {
|
||||||
|
final dif = (minSize - vSize) * 0.5;
|
||||||
|
top -= dif;
|
||||||
|
bottom += dif;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Rect.fromLTRB(
|
||||||
|
left - radius, top - radius, right + radius, bottom + radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translates a list of [Offset] points by a given [location] offset.
|
||||||
|
///
|
||||||
|
/// [data] The list of points to translate.
|
||||||
|
/// [location] The offset by which to translate the points.
|
||||||
|
/// Returns a new list of translated points.
|
||||||
|
/// Translates a list of [Offset] points by a given [location] offset.
|
||||||
|
///
|
||||||
|
/// [data] The list of points to translate.
|
||||||
|
/// [location] The offset by which to translate the points.
|
||||||
|
/// Returns a new list of translated points.
|
||||||
|
static List<T> translate<T extends Offset>(List<T> data, Offset location) {
|
||||||
|
final output = <T>[];
|
||||||
|
for (final point in data) {
|
||||||
|
output.add(point.translate(location.dx, location.dy) as T);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translates a list of lists of [Offset] points by a given [location] offset.
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to translate.
|
||||||
|
/// [location] The offset by which to translate the points.
|
||||||
|
/// Returns a new list of lists of translated points.
|
||||||
|
/// Translates a list of lists of [Offset] points by a given [location] offset.
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to translate.
|
||||||
|
/// [location] The offset by which to translate the points.
|
||||||
|
/// Returns a new list of lists of translated points.
|
||||||
|
static List<List<T>> translateData<T extends Offset>(
|
||||||
|
List<List<T>> data, Offset location) {
|
||||||
|
final output = <List<T>>[];
|
||||||
|
for (final set in data) {
|
||||||
|
output.add(translate(set, location));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scales a list of [Offset] points by a given [ratio].
|
||||||
|
///
|
||||||
|
/// [data] The list of points to scale.
|
||||||
|
/// [ratio] The scaling factor.
|
||||||
|
/// Returns a new list of scaled points.
|
||||||
|
/// Scales a list of [Offset] points by a given [ratio].
|
||||||
|
///
|
||||||
|
/// [data] The list of points to scale.
|
||||||
|
/// [ratio] The scaling factor.
|
||||||
|
/// Returns a new list of scaled points.
|
||||||
|
static List<T> scale<T extends Offset>(List<T> data, double ratio) {
|
||||||
|
final output = <T>[];
|
||||||
|
for (final point in data) {
|
||||||
|
output.add(point.scale(ratio, ratio) as T);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scales a list of lists of [Offset] points by a given [ratio].
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to scale.
|
||||||
|
/// [ratio] The scaling factor.
|
||||||
|
/// Returns a new list of lists of scaled points.
|
||||||
|
/// Scales a list of lists of [Offset] points by a given [ratio].
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to scale.
|
||||||
|
/// [ratio] The scaling factor.
|
||||||
|
/// Returns a new list of lists of scaled points.
|
||||||
|
static List<List<T>> scaleData<T extends Offset>(
|
||||||
|
List<List<T>> data, double ratio) {
|
||||||
|
final output = <List<T>>[];
|
||||||
|
for (final set in data) {
|
||||||
|
output.add(scale(set, ratio));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalizes a list of [Offset] points to a unit square (0-1 range)
|
||||||
|
/// based on their bounding box.
|
||||||
|
///
|
||||||
|
/// [data] The list of points to normalize.
|
||||||
|
/// [bound] Optional pre-calculated bounding box. If null, it will be calculated.
|
||||||
|
/// Returns a new list of normalized points.
|
||||||
|
/// Normalizes a list of [Offset] points to a unit square (0-1 range)
|
||||||
|
/// based on their bounding box.
|
||||||
|
///
|
||||||
|
/// [data] The list of points to normalize.
|
||||||
|
/// [bound] Optional pre-calculated bounding box. If null, it will be calculated.
|
||||||
|
/// Returns a new list of normalized points.
|
||||||
|
static List<T> normalize<T extends Offset>(List<T> data, {Rect? bound}) {
|
||||||
|
bound ??= bounds(data);
|
||||||
|
return scale<T>(
|
||||||
|
translate<T>(data, -bound.topLeft),
|
||||||
|
1.0 / math.max(bound.width, bound.height),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalizes a list of lists of [Offset] points to a unit square (0-1 range)
|
||||||
|
/// based on their combined bounding box.
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to normalize.
|
||||||
|
/// [bound] Optional pre-calculated combined bounding box. If null, it will be calculated.
|
||||||
|
/// Returns a new list of lists of normalized points.
|
||||||
|
/// Normalizes a list of lists of [Offset] points to a unit square (0-1 range)
|
||||||
|
/// based on their combined bounding box.
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to normalize.
|
||||||
|
/// [bound] Optional pre-calculated combined bounding box. If null, it will be calculated.
|
||||||
|
/// Returns a new list of lists of normalized points.
|
||||||
|
static List<List<T>> normalizeData<T extends Offset>(List<List<T>> data,
|
||||||
|
{Rect? bound}) {
|
||||||
|
bound ??= boundsOf(data);
|
||||||
|
final ratio = 1.0 / math.max(bound.width, bound.height);
|
||||||
|
return scaleData<T>(
|
||||||
|
translateData<T>(data, -bound.topLeft),
|
||||||
|
ratio,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills a given [rect] with the scaled and translated [data] points,
|
||||||
|
/// ensuring they fit within the rectangle with an optional [border].
|
||||||
|
///
|
||||||
|
/// [data] The list of points to fill.
|
||||||
|
/// [rect] The target rectangle to fill.
|
||||||
|
/// [radius] An additional radius to consider for bounding box calculation.
|
||||||
|
/// [bound] Optional pre-calculated bounding box for the data.
|
||||||
|
/// [border] The border size to apply around the filled content.
|
||||||
|
/// Returns a new list of transformed points.
|
||||||
|
/// Fills a given [rect] with the scaled and translated [data] points,
|
||||||
|
/// ensuring they fit within the rectangle with an optional [border].
|
||||||
|
///
|
||||||
|
/// [data] The list of points to fill.
|
||||||
|
/// [rect] The target rectangle to fill.
|
||||||
|
/// [radius] An additional radius to consider for bounding box calculation.
|
||||||
|
/// [bound] Optional pre-calculated bounding box for the data.
|
||||||
|
/// [border] The border size to apply around the filled content.
|
||||||
|
/// Returns a new list of transformed points.
|
||||||
|
static List<T> fill<T extends Offset>(List<T> data, Rect rect,
|
||||||
|
{double radius = 0.0, Rect? bound, double border = 32.0}) {
|
||||||
|
bound ??= bounds(data, radius: radius);
|
||||||
|
border *= 2.0;
|
||||||
|
|
||||||
|
final outputSize = Size(rect.width - border, rect.height - border);
|
||||||
|
final sourceSize = Size(bound.width, bound.height);
|
||||||
|
Size destinationSize;
|
||||||
|
|
||||||
|
final wr = outputSize.width / sourceSize.width;
|
||||||
|
final hr = outputSize.height / sourceSize.height;
|
||||||
|
|
||||||
|
if (wr < hr) {
|
||||||
|
//scale by width
|
||||||
|
destinationSize = Size(outputSize.width, sourceSize.height * wr);
|
||||||
|
} else {
|
||||||
|
//scale by height
|
||||||
|
destinationSize = Size(sourceSize.width * hr, outputSize.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
final borderSize = Offset(outputSize.width - destinationSize.width + border,
|
||||||
|
outputSize.height - destinationSize.height + border) *
|
||||||
|
0.5;
|
||||||
|
|
||||||
|
return translate<T>(
|
||||||
|
scale<T>(
|
||||||
|
normalize<T>(data, bound: bound),
|
||||||
|
math.max(destinationSize.width, destinationSize.height),
|
||||||
|
),
|
||||||
|
borderSize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills a given [rect] with the scaled and translated list of lists of [data] points,
|
||||||
|
/// ensuring they fit within the rectangle with an optional [border].
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to fill.
|
||||||
|
/// [rect] The target rectangle to fill.
|
||||||
|
/// [bound] Optional pre-calculated combined bounding box for the data.
|
||||||
|
/// [border] The border size to apply around the filled content.
|
||||||
|
/// Returns a new list of lists of transformed points.
|
||||||
|
/// Fills a given [rect] with the scaled and translated list of lists of [data] points,
|
||||||
|
/// ensuring they fit within the rectangle with an optional [border].
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to fill.
|
||||||
|
/// [rect] The target rectangle to fill.
|
||||||
|
/// [bound] Optional pre-calculated combined bounding box for the data.
|
||||||
|
/// [border] The border size to apply around the filled content.
|
||||||
|
/// Returns a new list of lists of transformed points.
|
||||||
|
static List<List<T>> fillData<T extends Offset>(List<List<T>> data, Rect rect,
|
||||||
|
{Rect? bound, double? border}) {
|
||||||
|
bound ??= boundsOf(data);
|
||||||
|
border ??= 4.0;
|
||||||
|
|
||||||
|
final outputSize = rect.size;
|
||||||
|
final sourceSize = bound;
|
||||||
|
Size destinationSize;
|
||||||
|
|
||||||
|
if (outputSize.width / outputSize.height >
|
||||||
|
sourceSize.width / sourceSize.height) {
|
||||||
|
destinationSize = Size(
|
||||||
|
sourceSize.width * outputSize.height / sourceSize.height,
|
||||||
|
outputSize.height);
|
||||||
|
} else {
|
||||||
|
destinationSize = Size(outputSize.width,
|
||||||
|
sourceSize.height * outputSize.width / sourceSize.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
destinationSize = Size(destinationSize.width - border * 2.0,
|
||||||
|
destinationSize.height - border * 2.0);
|
||||||
|
final borderSize = Offset(rect.width - destinationSize.width,
|
||||||
|
rect.height - destinationSize.height) *
|
||||||
|
0.5;
|
||||||
|
|
||||||
|
return translateData<T>(
|
||||||
|
scaleData<T>(
|
||||||
|
normalizeData<T>(data, bound: bound),
|
||||||
|
math.max(destinationSize.width, destinationSize.height),
|
||||||
|
),
|
||||||
|
borderSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a list of [Offset] points into a [Path] object by connecting them with lines.
|
||||||
|
///
|
||||||
|
/// [points] The list of points to convert.
|
||||||
|
/// Returns a [Path] representing the connected points.
|
||||||
|
/// Converts a list of [Offset] points into a [Path] object by connecting them with lines.
|
||||||
|
///
|
||||||
|
/// [points] The list of points to convert.
|
||||||
|
/// Returns a [Path] representing the connected points.
|
||||||
|
static Path toPath(List<Offset> points) {
|
||||||
|
final path = Path();
|
||||||
|
if (points.isNotEmpty) {
|
||||||
|
path.moveTo(points[0].dx, points[0].dy);
|
||||||
|
for (final point in points) {
|
||||||
|
path.lineTo(point.dx, point.dy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a list of lists of [Offset] points into a list of [Path] objects.
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to convert.
|
||||||
|
/// Returns a list of [Path] objects.
|
||||||
|
/// Converts a list of lists of [Offset] points into a list of [Path] objects.
|
||||||
|
///
|
||||||
|
/// [data] The list of lists of points to convert.
|
||||||
|
/// Returns a list of [Path] objects.
|
||||||
|
static List<Path> toPaths(List<List<Offset>> data) {
|
||||||
|
final paths = <Path>[];
|
||||||
|
for (final line in data) {
|
||||||
|
paths.add(toPath(line));
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the combined bounding box for a list of [Path] objects.
|
||||||
|
///
|
||||||
|
/// [data] The list of [Path] objects.
|
||||||
|
/// Returns a [Rect] representing the combined bounding box.
|
||||||
|
/// Calculates the combined bounding box for a list of [Path] objects.
|
||||||
|
///
|
||||||
|
/// [data] The list of [Path] objects.
|
||||||
|
/// Returns a [Rect] representing the combined bounding box.
|
||||||
|
static Rect pathBounds(List<Path> data) {
|
||||||
|
Rect init = data[0].getBounds();
|
||||||
|
|
||||||
|
double left = init.left;
|
||||||
|
double top = init.top;
|
||||||
|
double right = init.right;
|
||||||
|
double bottom = init.bottom;
|
||||||
|
|
||||||
|
for (final path in data) {
|
||||||
|
final bound = path.getBounds();
|
||||||
|
|
||||||
|
left = math.min(left, bound.left);
|
||||||
|
top = math.min(top, bound.top);
|
||||||
|
right = math.max(right, bound.right);
|
||||||
|
bottom = math.max(bottom, bound.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Rect.fromLTRB(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scales a single [Path] object by a given [ratio].
|
||||||
|
///
|
||||||
|
/// [data] The [Path] to scale.
|
||||||
|
/// [ratio] The scaling factor.
|
||||||
|
/// Returns a new, scaled [Path].
|
||||||
|
static Path scalePath(Path data, double ratio) {
|
||||||
|
final transform = Matrix4.identity();
|
||||||
|
transform.scale(ratio, ratio);
|
||||||
|
return data.transform(transform.storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scales a list of [Path] objects by a given [ratio].
|
||||||
|
///
|
||||||
|
/// [data] The list of [Path]s to scale.
|
||||||
|
/// [ratio] The scaling factor.
|
||||||
|
/// Returns a new list of scaled [Path]s.
|
||||||
|
/// Scales a list of [Path] objects by a given [ratio].
|
||||||
|
///
|
||||||
|
/// [data] The list of [Path]s to scale.
|
||||||
|
/// [ratio] The scaling factor.
|
||||||
|
/// Returns a new list of scaled [Path]s.
|
||||||
|
static List<Path> scalePaths(List<Path> data, double ratio) {
|
||||||
|
final output = <Path>[];
|
||||||
|
for (final path in data) {
|
||||||
|
output.add(scalePath(path, ratio));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translates a list of [Path] objects by a given [location] offset.
|
||||||
|
///
|
||||||
|
/// [data] The list of [Path]s to translate.
|
||||||
|
/// [location] The offset by which to translate the paths.
|
||||||
|
/// Returns a new list of translated [Path]s.
|
||||||
|
/// Translates a list of [Path] objects by a given [location] offset.
|
||||||
|
///
|
||||||
|
/// [data] The list of [Path]s to translate.
|
||||||
|
/// [location] The offset by which to translate the paths.
|
||||||
|
/// Returns a new list of translated [Path]s.
|
||||||
|
static List<Path> translatePaths(List<Path> data, Offset location) {
|
||||||
|
final output = <Path>[];
|
||||||
|
final transform = Matrix4.identity();
|
||||||
|
transform.translate(location.dx, location.dy);
|
||||||
|
for (final path in data) {
|
||||||
|
output.add(path.transform(transform.storage));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a list of [CubicLine] segments into a closed [Path] representing a filled shape.
|
||||||
|
/// This is typically used for drawing thick, filled signature lines.
|
||||||
|
///
|
||||||
|
/// [lines] The list of [CubicLine] segments.
|
||||||
|
/// [size] The base stroke width for the shape.
|
||||||
|
/// [maxSize] The maximum stroke width for the shape.
|
||||||
|
/// Returns a closed [Path] representing the filled shape.
|
||||||
|
static Path toShapePath(List<CubicLine> lines, double size, double maxSize) {
|
||||||
|
assert(lines.isNotEmpty);
|
||||||
|
|
||||||
|
if (lines.length == 1) {
|
||||||
|
final line = lines[0];
|
||||||
|
if (line.isDot) {
|
||||||
|
// TODO: Consider returning null or creating a circle path directly for dots.
|
||||||
|
return Path()
|
||||||
|
..start(line.start)
|
||||||
|
..line(line.end);
|
||||||
|
}
|
||||||
|
return line.toShape(size, maxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
final path = Path();
|
||||||
|
|
||||||
|
final firstLine = lines.first;
|
||||||
|
path.start(firstLine.start + firstLine.cpsUp(size, maxSize));
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.length; i++) {
|
||||||
|
final line = lines[i];
|
||||||
|
final d1 = line.cpsUp(size, maxSize);
|
||||||
|
final d2 = line.cpeUp(size, maxSize);
|
||||||
|
|
||||||
|
path.cubic(line.cpStart + d1, line.cpEnd + d2, line.end + d2);
|
||||||
|
}
|
||||||
|
|
||||||
|
final lastLine = lines.last;
|
||||||
|
path.line(lastLine.end + lastLine.cpeDown(size, maxSize));
|
||||||
|
|
||||||
|
for (int i = lines.length - 1; i > -1; i--) {
|
||||||
|
final line = lines[i];
|
||||||
|
final d3 = line.cpeDown(size, maxSize);
|
||||||
|
final d4 = line.cpsDown(size, maxSize);
|
||||||
|
|
||||||
|
path.cubic(line.cpEnd + d3, line.cpStart + d4, line.start + d4);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.close();
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a list of [CubicLine] segments into a simple [Path] object,
|
||||||
|
/// connecting them with cubic Bezier curves.
|
||||||
|
///
|
||||||
|
/// [lines] The list of [CubicLine] segments.
|
||||||
|
/// Returns a [Path] representing the connected lines.
|
||||||
|
/// Converts a list of [CubicLine] segments into a simple [Path] object,
|
||||||
|
/// connecting them with cubic Bezier curves.
|
||||||
|
///
|
||||||
|
/// [lines] The list of [CubicLine] segments.
|
||||||
|
/// Returns a [Path] representing the connected lines.
|
||||||
|
/// Converts a list of [CubicLine] segments into a simple [Path] object,
|
||||||
|
/// connecting them with cubic Bezier curves.
|
||||||
|
///
|
||||||
|
/// [lines] The list of [CubicLine] segments.
|
||||||
|
/// Returns a [Path] representing the connected lines.
|
||||||
|
static Path toLinePath(List<CubicLine> lines) {
|
||||||
|
assert(lines.isNotEmpty);
|
||||||
|
|
||||||
|
final path = Path()..start(lines[0]);
|
||||||
|
for (final line in lines) {
|
||||||
|
path.cubic(line.cpStart, line.cpEnd, line.end);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
hand_signature/pubspec.yaml
Normal file
15
hand_signature/pubspec.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
name: hand_signature
|
||||||
|
description: The Signature Pad Widget that allows you to draw smooth signatures. With variety of draw and export settings. And also supports SVG.
|
||||||
|
homepage: https://github.com/romanbase/hand_signature
|
||||||
|
version: 3.1.0+2
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
133
hand_signature/test/signature_control_test.dart
Normal file
133
hand_signature/test/signature_control_test.dart
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:hand_signature/signature.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
final control = HandSignatureControl();
|
||||||
|
|
||||||
|
// mock curve sequence
|
||||||
|
control.startPath(OffsetPoint(dx: 0.0, dy: 0.0, timestamp: 1));
|
||||||
|
control.alterPath(OffsetPoint(dx: 10.0, dy: 10.0, timestamp: 10));
|
||||||
|
control.alterPath(OffsetPoint(dx: 20.0, dy: 20.0, timestamp: 15));
|
||||||
|
control.alterPath(OffsetPoint(dx: 30.0, dy: 20.0, timestamp: 20));
|
||||||
|
control.closePath();
|
||||||
|
|
||||||
|
// mock dot sequence
|
||||||
|
control.startPath(OffsetPoint(dx: 30.0, dy: 30.0, timestamp: 25));
|
||||||
|
control.closePath();
|
||||||
|
|
||||||
|
// json string representing above mock data
|
||||||
|
final json =
|
||||||
|
'[[{"x":0.0,"y":0.0,"t":1},{"x":10.0,"y":10.0,"t":10},{"x":20.0,"y":20.0,"t":15},{"x":30.0,"y":20.0,"t":20}],[{"x":30.0,"y":30.0,"t":25}]]';
|
||||||
|
|
||||||
|
group('IO', () {
|
||||||
|
test('points', () async {
|
||||||
|
final paths = control.paths;
|
||||||
|
final curve = paths[0];
|
||||||
|
final dot = paths[1];
|
||||||
|
|
||||||
|
expect(paths.length, 2);
|
||||||
|
expect(curve.points.length, 4);
|
||||||
|
expect(curve.lines.length, 3);
|
||||||
|
|
||||||
|
// velocity of first line should be lower because second line is drawn faster while distance is identical
|
||||||
|
expect(curve.lines[0].end - curve.lines[0].start,
|
||||||
|
equals(curve.lines[1].end - curve.lines[1].start));
|
||||||
|
expect(curve.lines[0].velocity(), lessThan(curve.lines[1].velocity()));
|
||||||
|
|
||||||
|
expect(dot.points.length, 1);
|
||||||
|
expect(dot.isDot, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('export', () async {
|
||||||
|
final paths = control.paths;
|
||||||
|
|
||||||
|
final export =
|
||||||
|
'[${paths.map((e) => '[${e.points.map((e) => '{"x":${e.dx},"y":${e.dy},"t":${e.timestamp}}').join(',')}]').join(',')}]';
|
||||||
|
final data = jsonDecode(export);
|
||||||
|
|
||||||
|
expect(data, isNotNull);
|
||||||
|
expect((data as List).length, 2);
|
||||||
|
expect((data[0] as List).length, 4);
|
||||||
|
expect((data[1] as List).length, 1);
|
||||||
|
|
||||||
|
expect(export, equals(json));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('import', () async {
|
||||||
|
final controlIn = HandSignatureControl();
|
||||||
|
|
||||||
|
final data = jsonDecode(json) as Iterable;
|
||||||
|
|
||||||
|
data.forEach((element) {
|
||||||
|
final line = List.of(element);
|
||||||
|
expect(line.length, greaterThan(0));
|
||||||
|
|
||||||
|
//start path with first point
|
||||||
|
controlIn.startPath(OffsetPoint(
|
||||||
|
dx: line[0]['x'],
|
||||||
|
dy: line[0]['y'],
|
||||||
|
timestamp: line[0]['t'],
|
||||||
|
));
|
||||||
|
|
||||||
|
//skip first point and alter path with rest of points
|
||||||
|
line.skip(1).forEach((item) {
|
||||||
|
controlIn.alterPath(OffsetPoint(
|
||||||
|
dx: item['x'],
|
||||||
|
dy: item['y'],
|
||||||
|
timestamp: item['t'],
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
//close path
|
||||||
|
controlIn.closePath();
|
||||||
|
});
|
||||||
|
|
||||||
|
final paths = controlIn.paths;
|
||||||
|
final curve = paths[0];
|
||||||
|
final dot = paths[1];
|
||||||
|
|
||||||
|
expect(paths.length, 2);
|
||||||
|
expect(curve.points.length, 4);
|
||||||
|
expect(curve.lines.length, 3);
|
||||||
|
|
||||||
|
// velocity of first line is lower because second line is drawn faster while distance is identical
|
||||||
|
expect(curve.lines[0].end - curve.lines[0].start,
|
||||||
|
equals(curve.lines[1].end - curve.lines[1].start));
|
||||||
|
expect(curve.lines[0].velocity(), lessThan(curve.lines[1].velocity()));
|
||||||
|
|
||||||
|
expect(dot.points.length, 1);
|
||||||
|
expect(dot.isDot, isTrue);
|
||||||
|
|
||||||
|
// check equality of individual OffsetPoints of CubePaths
|
||||||
|
expect(controlIn.equals(control), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('map', () async {
|
||||||
|
final controlMap = HandSignatureControl();
|
||||||
|
controlMap.import(control.toMap());
|
||||||
|
|
||||||
|
// check equality of individual OffsetPoints of CubePaths
|
||||||
|
expect(controlMap.equals(control), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('image', () async {
|
||||||
|
final controlImage = HandSignatureControl();
|
||||||
|
|
||||||
|
controlImage.import(control.toMap());
|
||||||
|
controlImage.notifyDimension(Size(1280, 720));
|
||||||
|
|
||||||
|
final image = await controlImage.toImage();
|
||||||
|
|
||||||
|
expect(image, isNotNull);
|
||||||
|
|
||||||
|
final data = image!.buffer.asUint8List();
|
||||||
|
|
||||||
|
expect(data, isNotNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
357
lottie/pubspec.lock
Normal file
357
lottie/pubspec.lock
Normal file
|
|
@ -0,0 +1,357 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
_fe_analyzer_shared:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _fe_analyzer_shared
|
||||||
|
sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "92.0.0"
|
||||||
|
analyzer:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: analyzer
|
||||||
|
sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.0"
|
||||||
|
archive:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: archive
|
||||||
|
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.7"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.7.0"
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.13.0"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.19.1"
|
||||||
|
convert:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.7"
|
||||||
|
dart_style:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: dart_style
|
||||||
|
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.3"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.0"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
glob:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: glob
|
||||||
|
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
|
http:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.0"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.2"
|
||||||
|
leak_tracker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker
|
||||||
|
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.2"
|
||||||
|
leak_tracker_flutter_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_flutter_testing
|
||||||
|
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.10"
|
||||||
|
leak_tracker_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_testing
|
||||||
|
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.0"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.17"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.11.1"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.17.0"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
path:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
posix:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: posix
|
||||||
|
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.3"
|
||||||
|
pub_semver:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: pub_semver
|
||||||
|
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.1"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.12.1"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.7"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
vector_math:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
vm_service:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service
|
||||||
|
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "15.0.2"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web
|
||||||
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
yaml:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.9.0 <4.0.0"
|
||||||
|
flutter: ">=3.35.0"
|
||||||
|
|
@ -445,10 +445,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: watcher
|
name: watcher
|
||||||
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
|
sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.4"
|
version: "1.2.0"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -420,10 +420,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: watcher
|
name: watcher
|
||||||
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
|
sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.4"
|
version: "1.2.0"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ dependency_overrides:
|
||||||
path: ./dependencies/dots_indicator
|
path: ./dependencies/dots_indicator
|
||||||
ed25519_edwards:
|
ed25519_edwards:
|
||||||
path: ./dependencies/ed25519_edwards
|
path: ./dependencies/ed25519_edwards
|
||||||
|
hand_signature:
|
||||||
|
path: ./dependencies/hand_signature
|
||||||
hashlib:
|
hashlib:
|
||||||
path: ./dependencies/hashlib
|
path: ./dependencies/hashlib
|
||||||
hashlib_codecs:
|
hashlib_codecs:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue