Flutter开发 - 签名功能开发

819 阅读4分钟

在这里插入图片描述

就是如图所示的功能,这是一个签名页面,通过签名的内容和百度的签名验证API效验,获取用户的签名,然后用于合同等一些场景。

乍一听签名,特别还是flutter的签名,觉得好难啊,但是不用担心,flutter提供了我们强大的pub,这里推荐大家一个用于签名的pub

  signature: ^3.2.0

功能的问题解决了,那么界面的问题我们看下,要实现横屏,博主参考了网上提供的一些方法,均以失败告终,整个app只有这一个界面是支持横屏的,如果是原生,我相信大家有很多办法可以实现,在flutter里面貌似行不通,强制其它界面竖屏,此界面横屏,根据网上提供的一些方法设置,完全没有用,因为控制横竖屏是由Xcode里的设置来决定的。如果勾了Xcode中支持横屏的选项,强制横竖屏是不生效的。如果想要强制实现,可以通过向Xcode发送消息来在Xcode中设置代码来控制,这是目前博主所知根据屏幕方向支持横竖屏和强制横竖屏的一种控制方式。

那么不通过上面的方式怎么实现横屏呢?旋转,对,就是旋转。

我相信下面你已经知道我要怎么来实现这个页面了。

旋转推荐使用RotatedBox,好处是不会把里面的child截取掉而导致丢失一部分。

引入:

import 'package:signature/signature.dart';

创建画板:

  final SignatureController _controller = SignatureController(
    ///线条粗细
    penStrokeWidth: 5,
    ///线条颜色
    penColor: Colors.orange,
    ///导出图片背景色
    exportBackgroundColor: Colors.white,
  );
  var _signatureCanvas;

监听画板是否被涂鸦:

void initState() {
    // TODO: implement initState
    super.initState();
    ///画布可以设置宽高,不设置会自适应,类似Container
    _signatureCanvas = Signature(
      backgroundColor: Colors.white,
      controller: _controller,
    );
    _controller.addListener(() {
      if (isClear == true) {
        isClear = false;
        return;
      }
      hasPaint = true;
      setState(() {

      });
    });
  }

导出图片后,图片内存太大怎么办?压缩!

///推荐一个flutter图片压缩库
flutter_image_compress: ^0.7.0
///引入
import 'package:flutter_image_compress/flutter_image_compress.dart';

下面就不分开说了,直接发完整代码,方便大家直接用来演示和查看:

import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:signature/signature.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';

class SignName extends StatefulWidget {
  @override
  _SignNameState createState() => _SignNameState();
}

class _SignNameState extends State<SignName> {

  bool hasPaint = false;
  bool isClear = false;
  final SignatureController _controller = SignatureController(
    ///线条粗细
    penStrokeWidth: 5,
    ///线条颜色
    penColor: Colors.orange,
    ///导出图片背景色,通过百度API验证的时候要设置,不设置默认透明,百度无法识别
    exportBackgroundColor: Colors.white,
  );
  var _signatureCanvas;
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _signatureCanvas = Signature(
      backgroundColor: Colors.white,
      controller: _controller,
    );
    _controller.addListener(() {
      ///这里为什么这么写是因为清除的时候也被监听到了,会被误认为是刚开始画,目的是刚开始画的时候不显示画板中间的提示
      if (isClear == true) {
        isClear = false;
        return;
      }
      hasPaint = true;
      setState(() {

      });
    });
  }

  @override
  void dispose() {
    super.dispose();
    _controller.removeListener(() { });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Container(
        child: Stack(
          children: [
            Row(
              children: <Widget>[
                Container(
                  width: 80,
                  margin: EdgeInsets.only(top: 0, bottom: 0, left: 0),
                  child: RotatedBox(
                    quarterTurns: 1,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Container(
                          width: 160,
                          height: 32,
                          decoration: BoxDecoration(
                            borderRadius: const BorderRadius.all(Radius.circular(5)),
                            gradient: LinearGradient(
                              colors: hasPaint ? [
                                Color(0xFFFF4416),
                                Color(0xFFFF4416),
                                Color(0xFFFF7E2B),
                                Color(0xFFFFAD2D),
                              ] : [Color(0xFFF5F5F5), Color(0xFFF5F5F5)],
                              begin: Alignment.centerLeft,
                              end: Alignment.centerRight,
                            ),
                          ),
                          child: InkWell(
                            onTap: () {
                              hasPaint = false;
                              isClear = true;
                              setState(() {
                                _controller.clear();
                              });
                            },
                            child: Center(
                              child: Text(
                                '重写',
                                maxLines: 1,
                                style: TextStyle(fontSize: 14, color: hasPaint ? Colors.white : Color(0xFFC9CCCF)),
                              ),
                            ),
                          ),
                        ),
                        Container(width: 32,),
                        Container(
                          width: 160,
                          height: 32,
                          decoration: BoxDecoration(
                            borderRadius: const BorderRadius.all(Radius.circular(5)),
                            gradient: LinearGradient(
                              colors: hasPaint ? [
                                Color(0xFFFF4416),
                                Color(0xFFFF4416),
                                Color(0xFFFF7E2B),
                                Color(0xFFFFAD2D),
                              ] : [Color(0xFFF5F5F5), Color(0xFFF5F5F5)],
                              begin: Alignment.centerLeft,
                              end: Alignment.centerRight,
                            ),
                          ),
                          child: InkWell(
                            onTap: () async {
                              if (hasPaint) {
                                Uint8List byteData = await _controller.toPngBytes();
                                ///压缩图片
                                Uint8List pngBytes = await FlutterImageCompress.compressWithList(
                                    byteData,
                                    minHeight: 200,
                                    minWidth: 200,
                                    quality: 10,
                                    rotate: -90,
                                    format: CompressFormat.png
                                );
                                ///图片base64编码
                                // String imgStr = base64Encode(pngBytes);
                                // imgStr.replaceAll(' ', '');

                                showDialog(
                                    context: context,
                                    builder: (context) {
                                      return RotatedBox(
                                        quarterTurns: 1,
                                        child: AlertDialog(
                                          contentPadding:
                                          EdgeInsets.symmetric(horizontal: 54, vertical: 32),
                                          shape: RoundedRectangleBorder(
                                              borderRadius: BorderRadius.circular(8)),
                                          content: Column(
                                            mainAxisSize: MainAxisSize.min,
                                            children: [
                                              Image.memory(
                                                pngBytes,
                                                // width: 100.0,
                                                // height: 100,
                                              )
                                            ],
                                          ),
                                        ),
                                      );
                                    }
                                );
                              }
                              Future.delayed(Duration(seconds: 2), () {
                                Navigator.pop(context);
                              });
                            },
                            child: Center(
                              child: Text(
                                '完成',
                                maxLines: 1,
                                style: TextStyle(fontSize: 14, color: hasPaint ? Colors.white : Color(0xFFC9CCCF)),
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
                //SIGNATURE CANVAS
                _signatureCanvas,
                // OK AND CLEAR BUTTONS
                Container(
                    width: 50,
                    padding: EdgeInsets.all(0),
                    margin: EdgeInsets.only(top: 0, bottom: 0, right: 0),
                    child: Row(
                      children: <Widget>[
                        Container(
                          padding: EdgeInsets.all(0),
                          margin: EdgeInsets.only(top: 0, bottom: 0, left: 0),
                          color: Color(0xFFF5F5F5),
                          width: 0.5,
                        ),
                        Container(
                          padding: EdgeInsets.all(0),
                          width: 49.5,
                          margin: EdgeInsets.all(0),
                          child: Stack(
                            children: [
                              Positioned(
                                top: 40,
                                child: InkWell(
                                  onTap: () {
                                    Navigator.pop(context);
                                  },
                                  child: Container(
                                    margin: EdgeInsets.only(left: 10),
                                    child: Image.asset(
                                      "images/back_arrow.png",
                                      width: 30,
                                      height: 30,
                                    ),
                                  ),
                                ),
                              ),
                              Center(
                                child: RotatedBox(
                                  quarterTurns: 1,
                                  child: Text(
                                    '签名页',
                                    maxLines: 1,
                                    style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    )
                ),
              ],
            ),
            Offstage(
              offstage: hasPaint,
              child: Center(
                child: RotatedBox(
                  quarterTurns: 1,
                  child: Text(
                    '请用楷体书写',
                    maxLines: 1,
                    style: TextStyle(fontSize: 16, color: Color(0xFFC9CCCF)),
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

通过旋转的方式模拟横屏,这样就完美实现了签名的功能,如果你们项目已经做了横竖屏的支持,你可以直接正常UI写,不需要旋转,如果不想额外再去支持横竖屏,可以通过旋转的方式signature。