speaker_widget.dart 2.11 KB
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<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,
  ];

  @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,
      ),
    );
  }
}