speaker_widget.dart
3.67 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
128
129
130
131
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:wow_english/common/extension/string_extension.dart';
import 'package:wow_english/common/widgets/throttledGesture_gesture_detector.dart';
import '../../utils/log_util.dart';
/// 扬声器播放组件,帧动画
class SpeakerWidget extends StatefulWidget {
final bool isPlaying;
final bool isClickable;
final double width;
final double height;
final VoidCallback? onTap;
SpeakerWidget({
super.key,
this.isPlaying = false,
this.isClickable = true,
required this.width,
required this.height,
this.onTap,
});
@override
_SpeakerWidgetState createState() => _SpeakerWidgetState();
}
class _SpeakerWidgetState extends State<SpeakerWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
int _currentFrame = 0;
Timer? _timer;
final List<String> _speakerImagePaths = [
'ic_speaker_play2'.assetPng,
'ic_speaker_play0'.assetPng,
'ic_speaker_play1'.assetPng,
];
static const String TAG = "SpeakerWidget";
bool _isPlaying = false;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 300), // 每帧的持续时间
vsync: this,
);
Log.d(
"$TAG ${identityHashCode(this)} initState widget=${widget.isPlaying} _isPlaying=$_isPlaying _controller=${identityHashCode(_controller)}");
if (widget.isPlaying) {
///fixme 增加200毫秒延迟,避免一进入就已经开始播了,效果有待观察
Future.delayed(const Duration(milliseconds: 200), () {
_startAnimation();
});
}
}
@override
void didUpdateWidget(SpeakerWidget oldWidget) {
super.didUpdateWidget(oldWidget);
Log.d(
"$TAG ${identityHashCode(this)} didUpdateWidget widget=${widget.isPlaying} oldWidget=${oldWidget.isPlaying} _isPlaying=$_isPlaying");
if (widget.isPlaying && !_isPlaying) {
setState(() {
_isPlaying = true;
});
_startAnimation();
} else if (!widget.isPlaying && _isPlaying) {
_stopAnimation();
}
}
void _startAnimation() {
Log.d(
"$TAG ${identityHashCode(this)} _startAnimation widget=${widget.isPlaying} _isPlaying=$_isPlaying isAnimating=${_controller.isAnimating}");
_timer = Timer.periodic(const Duration(milliseconds: 300), (Timer timer) {
if (mounted) {
setState(() {
_currentFrame = ((_currentFrame + 1) % 3);
});
}
});
}
void _stopAnimation() {
Log.d(
"$TAG ${identityHashCode(this)} _stopAnimation widget=${widget.isPlaying} _isPlaying=$_isPlaying isAnimating=${_controller.isAnimating}");
_timer?.cancel();
_controller.stop();
_controller.reset();
if (mounted) {
setState(() {
_isPlaying = false;
_currentFrame = 0;
});
}
}
@override
void dispose() {
Log.d(
"$TAG ${identityHashCode(this)} dispose widget=${widget.isPlaying} _isPlaying=$_isPlaying isAnimating=${_controller.isAnimating}");
_controller.dispose();
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ThrottledGestureDetector(
onTap: widget.isClickable ? widget.onTap : null,
child: Opacity(
opacity: widget.isClickable ? 1.0 : 0.5,
child: Image.asset(
_speakerImagePaths[_currentFrame],
width: widget.width,
height: widget.height,
fit: BoxFit.cover,
color: widget.isClickable ? null : Colors.transparent,
// 灰色遮罩
colorBlendMode: widget.isClickable ? null : BlendMode.saturation,
),
),
);
}
}