【问题标题】:How to add Signature in flutter?如何在颤振中添加签名?
【发布时间】:2020-05-14 04:07:52
【问题描述】:

我已经在我的颤振项目中实现了signature_pad,它工作正常。

不幸的是,当我把它放在SingleChildScrollView 中时,签名没有被绘制出来。它滚动而不是签名。

好像是GestureDetector,但我不知道如何修复它。

有人可以给我一些线索吗?

谢谢。

【问题讨论】:

  • 对不起,我错误地理解了这个问题的最后一个答案。包存储库中有一个related issue,它可能会帮助您解决问题。检查this link解决方案。
  • @EnzoLizama 你知道怎么解决吗?
  • @Hoo 为什么需要 SingleChildScrollView?你能分享你的代码吗?
  • @JoãoSoares 因为我在屏幕上有很多小部件
  • 请分享本课程的代码,以便我们理解和帮助您。

标签: flutter dart signature flutter-widget singlechildscrollview


【解决方案1】:

Signature Class需要修改回复VerticalDrag,我改名为Signature1

现在签名区域板不应该滚动,您可以检查下面的完整代码,因为它的行为。你会发现签名区域不再滚动SingleChildScrollView

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'dart:ui' as ui;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var color = Colors.black;
  var strokeWidth = 3.0;
  final _sign = GlobalKey<Signature1State>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body:
           SingleChildScrollView(
             child: Column(
               children: <Widget>[
                 _showCategory(),
                 SizedBox(height: 15),
                 _showCategory(),
                 SizedBox(height: 15),
                 _showCategory(),
                 SizedBox(height: 15),
                 _showCategory(),
                 SizedBox(height: 15),
                 _showCategory(),
                 _showCategory(),
                 SizedBox(height: 15),
                 _showCategory(),
                 SizedBox(height: 15),
                 _showCategory(),
                 SizedBox(height: 15),
                 _showCategory(),
                 SizedBox(height: 15),
                 _showCategory(),
                 _showSignaturePad()
               ],
             ),
           )

      ,
    );
  }

  Widget _showCategory() {
    return TextField(
        onTap: () {
          FocusScope.of(context).requestFocus(FocusNode());
        },
        style: TextStyle(fontSize: 12.0, height: 1.0),
        decoration: InputDecoration(hintText: "TextView"));
  }

  Widget _showSignaturePad() {
    return Container(
      width: double.infinity,
      height: 200,
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Container(
          height: 200,
          //color: Colors.red,
          child: Signature1(
            color: color,
            key: _sign,
            strokeWidth: strokeWidth,
          ),
        ),
      ),
      color: Colors.grey.shade300,
    );
  }
}
class Signature1 extends StatefulWidget {
  final Color color;
  final double strokeWidth;
  final CustomPainter backgroundPainter;
  final Function onSign;

  Signature1({
    this.color = Colors.black,
    this.strokeWidth = 5.0,
    this.backgroundPainter,
    this.onSign,
    Key key,
  }) : super(key: key);

  Signature1State createState() => Signature1State();

  static Signature1State of(BuildContext context) {
    return context.findAncestorStateOfType<Signature1State>();
  }
}

class _SignaturePainter extends CustomPainter {
  Size _lastSize;
  final double strokeWidth;
  final List<Offset> points;
  final Color strokeColor;
  Paint _linePaint;

  _SignaturePainter({@required this.points, @required this.strokeColor, @required this.strokeWidth}) {
    _linePaint = Paint()
      ..color = strokeColor
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;
  }

  @override
  void paint(Canvas canvas, Size size) {
    _lastSize = size;
    for (int i = 0; i < points.length - 1; i++) {
      if (points[i] != null && points[i + 1] != null) canvas.drawLine(points[i], points[i + 1], _linePaint);
    }
  }

  @override
  bool shouldRepaint(_SignaturePainter other) => other.points != points;
}

class Signature1State extends State<Signature1> {
  List<Offset> _points = <Offset>[];
  _SignaturePainter _painter;
  Size _lastSize;

  Signature1State();

  void _onDragStart(DragStartDetails details){
    RenderBox referenceBox = context.findRenderObject();
    Offset localPostion = referenceBox.globalToLocal(details.globalPosition);
    setState(() {
      _points = List.from(_points)
        ..add(localPostion)
        ..add(localPostion);
    });
  }
  void _onDragUpdate (DragUpdateDetails details) {
    RenderBox referenceBox = context.findRenderObject();
    Offset localPosition = referenceBox.globalToLocal(details.globalPosition);

    setState(() {
      _points = List.from(_points)..add(localPosition);
      if (widget.onSign != null) {
        widget.onSign();
      }
    });
  }
  void _onDragEnd (DragEndDetails details) => _points.add(null);

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((_) => afterFirstLayout(context));
    _painter = _SignaturePainter(points: _points, strokeColor: widget.color, strokeWidth: widget.strokeWidth);
    return ClipRect(
      child: CustomPaint(
        painter: widget.backgroundPainter,
        foregroundPainter: _painter,
        child: GestureDetector(

          onVerticalDragStart: _onDragStart,
          onVerticalDragUpdate: _onDragUpdate,
          onVerticalDragEnd: _onDragEnd,

          onPanStart: _onDragStart,
          onPanUpdate: _onDragUpdate,
          onPanEnd: _onDragEnd
        ),
      ),
    );
  }

  Future<ui.Image> getData() {
    var recorder = ui.PictureRecorder();
    var origin = Offset(0.0, 0.0);
    var paintBounds = Rect.fromPoints(_lastSize.topLeft(origin), _lastSize.bottomRight(origin));
    var canvas = Canvas(recorder, paintBounds);
    if(widget.backgroundPainter != null) {
      widget.backgroundPainter.paint(canvas, _lastSize);
    }
    _painter.paint(canvas, _lastSize);
    var picture = recorder.endRecording();
    return picture.toImage(_lastSize.width.round(), _lastSize.height.round());
  }

  void clear() {
    setState(() {
      _points = [];
    });
  }

  bool get hasPoints => _points.length > 0;

  List<Offset> get points => _points;

  afterFirstLayout(BuildContext context) {
    _lastSize = context.size;
  }
}

【讨论】:

    【解决方案2】:

    您需要创建一个 CustomGestureDetector。

    查看我刚刚更改为您的Signature 的更新版本:

    
        import 'dart:async';
        import 'dart:ui' as ui;
    
        import 'package:flutter/gestures.dart';
        import 'package:flutter/material.dart';
    
        class Signature extends StatefulWidget {
          final Color color;
          final double strokeWidth;
          final CustomPainter backgroundPainter;
          final Function onSign;
    
          Signature({
            this.color = Colors.black,
            this.strokeWidth = 5.0,
            this.backgroundPainter,
            this.onSign,
            Key key,
          }) : super(key: key);
    
          SignatureState createState() => SignatureState();
    
          static SignatureState of(BuildContext context) {
            return context.findAncestorStateOfType<SignatureState>();
          }
        }
    
        class CustomPanGestureRecognizer extends OneSequenceGestureRecognizer {
          final Function onPanStart;
          final Function onPanUpdate;
          final Function onPanEnd;
    
          CustomPanGestureRecognizer({@required this.onPanStart, @required this.onPanUpdate, @required this.onPanEnd});
    
          @override
          void addPointer(PointerEvent event) {
            onPanStart(event.position);
            startTrackingPointer(event.pointer);
            resolve(GestureDisposition.accepted);
          }
    
          @override
          void handleEvent(PointerEvent event) {
            if (event is PointerMoveEvent) {
              onPanUpdate(event.position);
            }
            if (event is PointerUpEvent) {
              onPanEnd(event.position);
              stopTrackingPointer(event.pointer);
            }
          }
    
          @override
          String get debugDescription => 'customPan';
    
          @override
          void didStopTrackingLastPointer(int pointer) {}
        }
    
        class _SignaturePainter extends CustomPainter {
          Size _lastSize;
          final double strokeWidth;
          final List<Offset> points;
          final Color strokeColor;
          Paint _linePaint;
    
          _SignaturePainter({@required this.points, @required this.strokeColor, @required this.strokeWidth}) {
            _linePaint = Paint()
              ..color = strokeColor
              ..strokeWidth = strokeWidth
              ..strokeCap = StrokeCap.round;
          }
    
          @override
          void paint(Canvas canvas, Size size) {
            _lastSize = size;
            for (int i = 0; i < points.length - 1; i++) {
              if (points[i] != null && points[i + 1] != null) canvas.drawLine(points[i], points[i + 1], _linePaint);
            }
          }
    
          @override
          bool shouldRepaint(_SignaturePainter other) => other.points != points;
        }
    
        class SignatureState extends State<Signature> {
          List<Offset> _points = <Offset>[];
          _SignaturePainter _painter;
          Size _lastSize;
    
          SignatureState();
    
          @override
          Widget build(BuildContext context) {
            WidgetsBinding.instance.addPostFrameCallback((_) => afterFirstLayout(context));
            _painter = _SignaturePainter(points: _points, strokeColor: widget.color, strokeWidth: widget.strokeWidth);
            return ClipRect(
              child: CustomPaint(
                painter: widget.backgroundPainter,
                foregroundPainter: _painter,
                child: RawGestureDetector(
                  gestures: {
                    CustomPanGestureRecognizer: GestureRecognizerFactoryWithHandlers<CustomPanGestureRecognizer>(
                      () => CustomPanGestureRecognizer(
                        onPanStart: (position) {
                          RenderBox referenceBox = context.findRenderObject();
                          Offset localPostion = referenceBox.globalToLocal(position);
                          setState(() {
                            _points = List.from(_points)..add(localPostion)..add(localPostion);
                          });
                          return true;
                        },
                        onPanUpdate: (position) {
                          RenderBox referenceBox = context.findRenderObject();
                          Offset localPosition = referenceBox.globalToLocal(position);
    
                          setState(() {
                            _points = List.from(_points)..add(localPosition);
                            if (widget.onSign != null) {
                              widget.onSign();
                            }
                          });
                        },
                        onPanEnd: (position) {
                          _points.add(null);
                        },
                      ),
                      (CustomPanGestureRecognizer instance) {},
                    ),
                  },
                ),
              ),
            );
          }
    
          Future<ui.Image> getData() {
            var recorder = ui.PictureRecorder();
            var origin = Offset(0.0, 0.0);
            var paintBounds = Rect.fromPoints(_lastSize.topLeft(origin), _lastSize.bottomRight(origin));
            var canvas = Canvas(recorder, paintBounds);
            if (widget.backgroundPainter != null) {
              widget.backgroundPainter.paint(canvas, _lastSize);
            }
            _painter.paint(canvas, _lastSize);
            var picture = recorder.endRecording();
            return picture.toImage(_lastSize.width.round(), _lastSize.height.round());
          }
    
          void clear() {
            setState(() {
              _points = [];
            });
          }
    
          bool get hasPoints => _points.length > 0;
    
          List<Offset> get points => _points;
    
          afterFirstLayout(BuildContext context) {
            _lastSize = context.size;
          }
        }
    
    
    

    特别关注CustomPanGestureRecognizer

    您可以阅读更多内容:

    Gesture Disambiguation

    【讨论】:

    • 干得好,我推荐这个答案,因为它也能捕捉到前几个像素。
    【解决方案3】:

    Flutter 有两个类可以帮助您绘制到画布上:CustomPaintCustomPainter,后者实现了您绘制到画布上的算法。

    像这样在 Flutter 中实现签名画家

    import 'package:flutter/material.dart';
    
    void main() => runApp(MaterialApp(home: DemoApp()));
    
    class DemoApp extends StatelessWidget {
      Widget build(BuildContext context) => Scaffold(body: Signature());
    }
    
    class Signature extends StatefulWidget {
      SignatureState createState() => SignatureState();
    }
    
    class SignatureState extends State<Signature> {
      List<Offset> _points = <Offset>[];
      Widget build(BuildContext context) {
        return GestureDetector(
          onPanUpdate: (DragUpdateDetails details) {
            setState(() {
              RenderBox referenceBox = context.findRenderObject();
              Offset localPosition =
                  referenceBox.globalToLocal(details.globalPosition);
              _points = List.from(_points)..add(localPosition);
            });
          },
          onPanEnd: (DragEndDetails details) => _points.add(null),
          child: CustomPaint(
            painter: SignaturePainter(_points),
            size: Size.infinite,
          ),
        );
      }
    }
    
    class SignaturePainter extends CustomPainter {
      SignaturePainter(this.points);
      final List<Offset> points;
      void paint(Canvas canvas, Size size) {
        var paint = Paint()
          ..color = Colors.black
          ..strokeCap = StrokeCap.round
          ..strokeWidth = 5.0;
        for (int i = 0; i < points.length - 1; i++) {
          if (points[i] != null && points[i + 1] != null)
            canvas.drawLine(points[i], points[i + 1], paint);
        }
      }
    
      bool shouldRepaint(SignaturePainter other) => other.points != points;
    }
    

    【讨论】:

      【解决方案4】:

      这是因为来自SingleChildScrollView 的手势覆盖了您的签名小部件的手势,因为SingleChildScrollView 是父级。与此线程中的其他响应一样,解决它的方法很少。但最简单的方法是使用现有的包。您可以简单地使用下面的 Syncfusion 的 Flutter SignaturePad 小部件,我现在将其用于我的应用程序。此小部件适用于 Android、iOS 和网络平台。

      包 - https://pub.dev/packages/syncfusion_flutter_signaturepad

      功能 - https://www.syncfusion.com/flutter-widgets/flutter-signaturepad

      文档 - https://help.syncfusion.com/flutter/signaturepad/getting-started

      【讨论】:

        猜你喜欢
        • 2021-06-05
        • 2023-03-18
        • 1970-01-01
        • 2021-10-12
        • 2019-03-20
        • 2021-11-09
        • 2022-11-04
        • 1970-01-01
        • 2022-01-04
        相关资源
        最近更新 更多