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 with SingleTickerProviderStateMixin { late AnimationController _controller; int _currentFrame = 0; Timer? _timer; final List _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) { _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, ), ), ); } }