签名用的signature插件
https://pub.dev/packages/signature
Signature画板需要一个控制器SignatureController
final SignatureController _signatureController = SignatureController(
penStrokeWidth: 10.w, // 线条宽度
penColor: Colors.black, // 线条颜色
exportBackgroundColor: Colors.transparent, // 导出图片背景色
);
基本用法:
Signature(
controller: _signatureController,
width: 400,
height: 300,
backgroundColor: Colors.white,
)
导出图片
signatureController.toImage();
清空画板
signatureController.clear();
我还需要橡皮擦按钮和放大按钮,以及横屏功能,所以单独写一个DrawView类:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:niuzhuang/Utils/Utils.dart';
import 'package:signature/signature.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class DrawView extends StatefulWidget {
final signatureController;
final biggerCallback; // 放大回调
final resetCallback; // 还原大小回调
final double width;
final double height;
final bool landScape; // 是否横屏
DrawView(
{Key key,
this.signatureController,
this.biggerCallback,
this.resetCallback,
@required this.width,
@required this.height,
this.landScape})
: super(key: key);
@override
_DrawViewState createState() => _DrawViewState();
}
class _DrawViewState extends State<DrawView> {
bool isEmpty;
@override
void initState() {
super.initState();
if (widget.signatureController.value.length > 0) {
isEmpty = false;
} else {
isEmpty = true;
}
// 监听画板
widget.signatureController.addListener(() {
bool tmpIsEmpty = true;
if (widget.signatureController.value.length > 0) {
tmpIsEmpty = false;
} else {
tmpIsEmpty = true;
}
if (isEmpty != tmpIsEmpty) {
if (this.mounted) {
setState(() {
isEmpty = tmpIsEmpty;
});
}
}
});
}
@override
Widget build(BuildContext context) {
return RotatedBox(
quarterTurns: widget.landScape ? 1 : 0,
child: Stack(
alignment: Alignment.center,
children: [
Signature(
controller: widget.signatureController,
width: widget.width,
height: widget.height,
backgroundColor: Colors.white,
),
// 暂无签名
Offstage(
offstage: isEmpty ? false : true,
child: Text(
'签名(必填)',
style: TextStyle(
fontSize: 44.sp,
color: hexColor('A5ACB4'),
),
),
),
// 放大按钮
Positioned(
top: 10.w,
right: widget.landScape ? 0 : 10.w,
child: IconButton(
icon: Image.asset(
'images/手写板放大.png',
width: 35.w,
height: 35.w,
),
onPressed: () {
widget.biggerCallback();
},
),
),
// 橡皮 & 完成 按钮
Positioned(
bottom: 10.w,
right: widget.landScape ? 0 : 10.w,
child: Row(
children: [
// 橡皮
IconButton(
icon: Image.asset(
'images/橡皮icon.png',
width: 45.w,
height: 45.w,
),
onPressed: () {
setState(() {
widget.signatureController.clear();
});
},
),
// 完成
Offstage(
offstage: widget.landScape ? false : true,
child: Container(
width: 80.w,
height: 50.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.w),
color: mainColor,
),
child: FlatButton(
padding: EdgeInsets.zero,
child: Text(
'完成',
style: TextStyle(
fontSize: 26.sp,
color: Colors.white,
),
),
onPressed: () {
widget.resetCallback();
},
),
),
),
],
),
),
],
),
);
}
}
使用:
@override
Widget build(BuildContext context) {
if (landScape == false) {
return Scaffold(
backgroundColor: hexColor('f7f7f9'),
appBar: AppBar(
brightness: Brightness.light,
title: Text(
nullToString(widget.type),
style: TextStyle(
fontSize: 34.sp,
),
),
),
body: SafeArea(
child: _initSubviews(),
),
);
} else {
// 手写板横屏
return Scaffold(
body: Container(
width: ScreenUtil().screenWidth,
height: ScreenUtil().screenHeight,
color: Colors.white,
child: FittedBox(
fit: BoxFit.contain,
child: DrawView(
signatureController: _signatureController,
width: 690.w,
height: 370.w,
landScape: landScape,
biggerCallback: () {
setState(() {
landScape = false;
});
},
resetCallback: () {
setState(() {
landScape = false;
});
},
),
),
),
);
}
}
用变量landScape记录画板是否在横屏状态,切换的时候修改landScape的值并重新build页面
DrawView使用RotatedBox来旋转画板,用Transform的话不会改变原组件的大小,只作用于绘制阶段,而RotatedBox则是在Layout阶段旋转
widget.signatureController.addListener给画板控制器添加监听,可以通过
signatureController.value数组的长度来判断画布是否有内容
由于横屏和普通状态我创建了两个画板,所以想保留线条数据就得使用同一个控制器signatureController,刷新DrawView的时候需要加一个mounted判断State是否已经销毁
if (this.mounted) {
setState(() {
isEmpty = tmpIsEmpty;
});
}