option_widget.dart
3.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import 'package:flutter/material.dart';
import 'package:wow_english/common/widgets/throttledGesture_gesture_detector.dart';
import '../../utils/log_util.dart';
/// 选择题选项组件,具备答错摇晃、答对缩放动效能力
class OptionWidget extends StatefulWidget {
final Widget child;
//正确缩放动画;错误摇晃动画;为空即非选中选项,无操作
final bool? isCorrect;
final bool isClickable;
final VoidCallback? onTap;
const OptionWidget({
Key? key,
required this.child,
this.isCorrect,
this.isClickable = true,
this.onTap,
}) : super(key: key);
@override
_OptionWidgetState createState() => _OptionWidgetState();
}
class _OptionWidgetState extends State<OptionWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _shakeAnimation;
late Animation<double> _scaleAnimation;
static const String TAG = 'OptionWidget';
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_shakeAnimation = TweenSequence([
TweenSequenceItem(
tween: Tween(begin: 0.0, end: 10.0)
.chain(CurveTween(curve: Curves.easeInOut)),
weight: 1,
),
TweenSequenceItem(
tween: Tween(begin: 10.0, end: -10.0)
.chain(CurveTween(curve: Curves.easeInOut)),
weight: 1,
),
TweenSequenceItem(
tween: Tween(begin: -10.0, end: 10.0)
.chain(CurveTween(curve: Curves.easeInOut)),
weight: 1,
),
TweenSequenceItem(
tween: Tween(begin: 10.0, end: -10.0)
.chain(CurveTween(curve: Curves.easeInOut)),
weight: 1,
),
TweenSequenceItem(
tween: Tween(begin: -10.0, end: 0.0)
.chain(CurveTween(curve: Curves.easeInOut)),
weight: 1,
),
]).animate(_controller);
_scaleAnimation = TweenSequence<double>([
TweenSequenceItem(tween: Tween(begin: 1.0, end: 0.8), weight: 1),
TweenSequenceItem(tween: Tween(begin: 0.8, end: 1.25), weight: 1),
TweenSequenceItem(tween: Tween(begin: 1.25, end: 1.0), weight: 1),
]).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
}
@override
void didUpdateWidget(OptionWidget oldWidget) {
super.didUpdateWidget(oldWidget);
Log.d(
'$TAG ${identityHashCode(this)} didUpdateWidget widget.isCorrect=${widget.isCorrect} oldWidget.isCorrect=${oldWidget.isCorrect} isAnimation=${_controller.isAnimating} status=${_controller.status}');
if (widget.isCorrect == oldWidget.isCorrect && _controller.isAnimating) {
return;
}
_controller.reset();
if (widget.isCorrect == true) {
_controller.forward();
} else if (widget.isCorrect == false) {
_controller.forward();
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
double offsetX = widget.isCorrect == false ? _shakeAnimation.value : 0;
double scale = widget.isCorrect == true ? _scaleAnimation.value : 1.0;
return ThrottledGestureDetector(
onTap: widget.isClickable ? widget.onTap : null,
child: Transform.translate(
offset: Offset(offsetX, 0),
child: Transform.scale(
scale: scale,
child: Opacity(
opacity: widget.isClickable ? 1.0 : 0.5,
child: widget.child,
),
),
),
);
},
);
}
}