speaker_widget.dart 3.67 KB
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,
        ),
      ),
    );
  }
}