import 'package:flutter/material.dart'; import 'dart:async'; import 'package:wow_english/common/extension/string_extension.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, ]; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 300), // 每帧的持续时间 vsync: this, ); if (widget.isPlaying) { _startAnimation(); } } @override void didUpdateWidget(SpeakerWidget oldWidget) { super.didUpdateWidget(oldWidget); if (widget.isPlaying != oldWidget.isPlaying) { if (widget.isPlaying) { _startAnimation(); } else { _stopAnimation(); } } } void _startAnimation() { _timer = Timer.periodic(const Duration(milliseconds: 300), (Timer timer) { setState(() { _currentFrame = ((_currentFrame + 1) % 3); }); }); } void _stopAnimation() { _timer?.cancel(); setState(() { _currentFrame = 0; }); } @override void dispose() { _controller.dispose(); _timer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return GestureDetector( onTap: widget.isClickable ? widget.onTap : null, child: Image.asset( _speakerImagePaths[_currentFrame], width: widget.width, height: widget.height, fit: BoxFit.cover, ), ); } }