Commit 56b412f55b434738abfb155887550af442202b7f

Authored by 吴启风
1 parent 32b3acf4

feat:扬声器播放动画组件封装

assets/images/ic_speaker_play0.png 0 → 100644

12.7 KB

assets/images/ic_speaker_play1.png 0 → 100644

13.6 KB

assets/images/ic_speaker_play2.png 0 → 100644

15 KB

lib/common/widgets/speaker_widget.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'dart:async';
  3 +
  4 +import 'package:wow_english/common/extension/string_extension.dart';
  5 +
  6 +/// 扬声器播放组件,帧动画
  7 +class SpeakerWidget extends StatefulWidget {
  8 + final bool isPlaying;
  9 + final bool isClickable;
  10 + final double width;
  11 + final double height;
  12 + final VoidCallback? onTap;
  13 +
  14 + SpeakerWidget({
  15 + super.key,
  16 + this.isPlaying = false,
  17 + this.isClickable = true,
  18 + required this.width,
  19 + required this.height,
  20 + this.onTap,
  21 + });
  22 +
  23 + @override
  24 + _SpeakerWidgetState createState() => _SpeakerWidgetState();
  25 +}
  26 +
  27 +class _SpeakerWidgetState extends State<SpeakerWidget>
  28 + with SingleTickerProviderStateMixin {
  29 + late AnimationController _controller;
  30 + int _currentFrame = 0;
  31 + Timer? _timer;
  32 + final List<String> _speakerImagePaths = [
  33 + 'ic_speaker_play2'.assetPng,
  34 + 'ic_speaker_play0'.assetPng,
  35 + 'ic_speaker_play1'.assetPng,
  36 + ];
  37 +
  38 + @override
  39 + void initState() {
  40 + super.initState();
  41 + _controller = AnimationController(
  42 + duration: const Duration(milliseconds: 300), // 每帧的持续时间
  43 + vsync: this,
  44 + );
  45 +
  46 + if (widget.isPlaying) {
  47 + _startAnimation();
  48 + }
  49 + }
  50 +
  51 + @override
  52 + void didUpdateWidget(SpeakerWidget oldWidget) {
  53 + super.didUpdateWidget(oldWidget);
  54 + if (widget.isPlaying != oldWidget.isPlaying) {
  55 + if (widget.isPlaying) {
  56 + _startAnimation();
  57 + } else {
  58 + _stopAnimation();
  59 + }
  60 + }
  61 + }
  62 +
  63 + void _startAnimation() {
  64 + _timer = Timer.periodic(const Duration(milliseconds: 300), (Timer timer) {
  65 + setState(() {
  66 + _currentFrame = ((_currentFrame + 1) % 3);
  67 + });
  68 + });
  69 + }
  70 +
  71 + void _stopAnimation() {
  72 + _timer?.cancel();
  73 + setState(() {
  74 + _currentFrame = 0;
  75 + });
  76 + }
  77 +
  78 + @override
  79 + void dispose() {
  80 + _controller.dispose();
  81 + _timer?.cancel();
  82 + super.dispose();
  83 + }
  84 +
  85 + @override
  86 + Widget build(BuildContext context) {
  87 + return GestureDetector(
  88 + onTap: widget.isClickable ? widget.onTap : null,
  89 + child: Image.asset(
  90 + _speakerImagePaths[_currentFrame],
  91 + width: widget.width,
  92 + height: widget.height,
  93 + fit: BoxFit.cover,
  94 + ),
  95 + );
  96 + }
  97 +}
... ...
lib/pages/practice/topic_picture_page.dart
... ... @@ -11,7 +11,9 @@ import &#39;package:wow_english/pages/practice/widgets/shake_widget.dart&#39;;
11 11 import 'package:wow_english/route/route.dart';
12 12 import 'package:wow_english/utils/toast_util.dart';
13 13  
  14 +import '../../common/widgets/speaker_widget.dart';
14 15 import '../../common/widgets/throttledGesture_gesture_detector.dart';
  16 +import '../../utils/log_util.dart';
15 17 import 'bloc/topic_picture_bloc.dart';
16 18 import 'widgets/practice_header_widget.dart';
17 19  
... ... @@ -513,12 +515,14 @@ class _TopicPicturePage extends StatelessWidget {
513 515 },
514 516 child: Row(
515 517 children: [
516   - Image.asset(
517   - bloc.voicePlayState == VoicePlayState.playing
518   - ? 'reade_answer'.assetGif
519   - : 'voice'.assetPng,
520   - height: 45.h,
  518 + SpeakerWidget(
  519 + isPlaying: bloc.voicePlayState == VoicePlayState.playing, // 控制动画播放
  520 + isClickable: true, // 控制是否可点击
521 521 width: 45.w,
  522 + height: 45.w,
  523 + onTap: () {
  524 + Log.d("Speaker tapped");
  525 + },
522 526 ),
523 527 10.horizontalSpace,
524 528 Text(topics?.word ?? '')
... ...
lib/pages/practice/widgets/shake_widget.dart
... ... @@ -2,6 +2,7 @@ import &#39;package:flutter/material.dart&#39;;
2 2  
3 3 import '../../../utils/log_util.dart';
4 4  
  5 +///左右晃动组件,用于答错场景
5 6 class ShakeWidget extends StatefulWidget {
6 7 final Widget child;
7 8 final int shakeCount;
... ...