option_widget.dart
3.62 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
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 didUpdateWidget widget.isCorrect=${widget.isCorrect} oldWidget.isCorrect=${oldWidget.isCorrect} isAnimation=${_controller.isAnimating} status=${_controller.status}');
    if (widget.isCorrect != oldWidget.isCorrect) {
      _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,
              ),
            ),
          ),
        );
      },
    );
  }
}