Flutter实现滑动块验证码功能,供大家参考,具体内容如下
本文实现的是一个用于登录时,向右滑动滑动块到最右边完成验证的一个功能。当滑动未到最右边时,滑动块回弹回左边起始位置。
import 'package:flutter/material.dart';
class SlideVerifyWidget extends StatefulWidget{
/// 背景色
final Color backgroundColor;
/// 滑动过的颜色
final Color slideColor;
/// 边框颜色
final Color borderColor;
final double height;
final double width;
final VoidCallback verifySuccessListener;
const SlideVerifyWidget({
Key key,
this.backgroundColor = Colors.blueGrey,
this.slideColor = Colors.green,
this.borderColor = Colors.grey,
this.height = 44,
this.width = 240,
this.verifySuccessListener
}) : super(key: key);
@override
State<StatefulWidget> createState() {
return SlideVerifyState();
}
}
class SlideVerifyState extends State<SlideVerifyWidget> with TickerProviderStateMixin{
double height;
double width ;
double sliderDistance = 0;
double initial = 0.0;
/// 滑动块宽度
double sliderWidth = 64;
/// 验证是否通过,滑动到最右方为通过
bool verifySuccess = false;
/// 是否允许拖动
bool enableSlide = true;
AnimationController _animationController;
Animation _curve;
@override
void initState() {
super.initState();
this.width = widget.width;
this.height = widget.height;
_initAnimation();
}
@override
void dispose() {
_animationController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onHorizontalDragStart: (DragStartDetails details){
if(!enableSlide){
return;
}
initial = details.globalPosition.dx;
},
onHorizontalDragUpdate: (DragUpdateDetails details){
if(!enableSlide){
return;
}
sliderDistance = details.globalPosition.dx - initial;
if(sliderDistance < 0){
sliderDistance = 0;
}
/// 当滑动到最右边时,通知验证成功,并禁止滑动
if(sliderDistance > width - sliderWidth){
sliderDistance = width - sliderWidth;
enableSlide = false;
verifySuccess = true;
if(widget.verifySuccessListener != null){
widget.verifySuccessListener();
}
}
setState(() {
});
},
onHorizontalDragEnd: (DragEndDetails details){
/// 滑动松开时,如果未达到最右边,启动回弹动画
if(enableSlide){
enableSlide = false;
_animationController.forward();
}
},
child: Container(
height: height,
width: width,
decoration: BoxDecoration(
color: widget.backgroundColor,
border: Border.all(color: widget.borderColor),
/// 圆角实现
borderRadius: BorderRadius.all(new Radius.circular(height))
),
child: Stack(
children: <Widget>[
Positioned(
top: 0,
left: 0,
child: Container(
height: height - 2,
/// 当slider滑动到距左边只有两三像素距离时,已滑动背景会有一点点渲染出边框范围,
/// 因此当滑动距离小于1时,直接将宽度设置为0,解决滑动块返回左边时导致的绿色闪动,但如果是缓慢滑动到左边该问题仍没解决
width: sliderDistance < 1? 0 : sliderDistance + sliderWidth / 2,
decoration: BoxDecoration(
color: widget.slideColor,
/// 圆角实现
borderRadius: BorderRadius.all(new Radius.circular(height / 2))
),
),
),
Center(
child: Text(verifySuccess?"验证成功":"请按住滑块,拖动到最右边", style: TextStyle(color: verifySuccess?Colors.white:Colors.black54, fontSize: 14),),
),
Positioned(
top: 0,
/// 此处将sliderDistance距离往左偏2是解决当滑动块滑动到最右边时遮挡外部边框
left: sliderDistance > sliderWidth ? sliderDistance - 2 : sliderDistance,
child: Container(
width: sliderWidth,
height: height - 2 ,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: widget.borderColor),
/// 圆角实现
borderRadius: BorderRadius.all(new Radius.circular(height))
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(width: 6,),
Image.asset("assets/images/ic_safety.png", height: 24, width: 24,),
Image.asset("assets/images/ic_next_primary.png", height: 16, width: 16,),
/// 因为向右箭头有透明边距导致两个箭头间隔过大,因此将第二个箭头向左偏移,如果切图无边距则不用偏移
Transform(
transform: Matrix4.translationValues(-8, 0, 0),
child: Image.asset("assets/images/ic_next_primary.png", height: 16, width: 16,),
),
],
),
),
)
],
),
),
);
}
/// 回弹动画
void _initAnimation(){
_animationController = AnimationController(
duration: const Duration(milliseconds: 300), vsync: this);
_curve = CurvedAnimation(parent: _animationController, curve: Curves.easeOut);
_curve.addListener(() {
setState(() {
sliderDistance = sliderDistance - sliderDistance * _curve.value;
if(sliderDistance <= 0){
sliderDistance = 0;
}
});
});
_animationController.addStatusListener((status) {
if(status == AnimationStatus.completed){
enableSlide = true;
_animationController.reset();
}
});
}
}