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
|
||||
dots_indicator: 508f5883ac79bdbc10254092de3f28f571d261cd
|
||||
ed25519_edwards: 7353ba759ea9f4646cbf481c2ef949625c8ce4cf
|
||||
hand_signature: 1beedb164d093643365b0832277c377353c7464f
|
||||
hashlib: 983cdbd5ee2529b908876b57a7217c09c6bc148a
|
||||
hashlib_codecs: 2a966c37c3b9b1f5541ae88e99ab34acf3fc968b
|
||||
introduction_screen: 4a90e557630b28834479ed9c64a9d2d0185d8e48
|
||||
|
|
|
|||
|
|
@ -44,3 +44,6 @@ libsignal_protocol_dart:
|
|||
git: https://github.com/bcgit/pc-dart.git
|
||||
x25519:
|
||||
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
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
|
||||
sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.4"
|
||||
version: "1.2.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -420,10 +420,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
|
||||
sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.4"
|
||||
version: "1.2.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ dependency_overrides:
|
|||
path: ./dependencies/dots_indicator
|
||||
ed25519_edwards:
|
||||
path: ./dependencies/ed25519_edwards
|
||||
hand_signature:
|
||||
path: ./dependencies/hand_signature
|
||||
hashlib:
|
||||
path: ./dependencies/hashlib
|
||||
hashlib_codecs:
|
||||
|
|
|
|||
Loading…
Reference in a new issue