Commit d4d91cb0f04764f3a01d6d8138ae3a24e4ba8410

Authored by 吴启风
1 parent aefec95d

feat:lottie动效组件封装&语音跟读页动效

lib/common/widgets/record_playback_widget.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:lottie/lottie.dart';
  3 +import 'package:wow_english/common/widgets/throttledGesture_gesture_detector.dart';
  4 +
  5 +import '../../utils/log_util.dart';
  6 +
  7 +/// 录音组件
  8 +class _RecorderPlaybackWidget extends StatefulWidget {
  9 + final bool isClickable;
  10 + final bool isPlaying;
  11 + final VoidCallback? onTap;
  12 + final double width;
  13 + final double height;
  14 +
  15 + const _RecorderPlaybackWidget({
  16 + Key? key,
  17 + required this.isClickable,
  18 + required this.isPlaying,
  19 + required this.onTap,
  20 + required this.width,
  21 + required this.height,
  22 + }) : super(key: key);
  23 +
  24 + @override
  25 + __RecorderPlaybackWidgetState createState() =>
  26 + __RecorderPlaybackWidgetState();
  27 +}
  28 +
  29 +class __RecorderPlaybackWidgetState extends State<_RecorderPlaybackWidget>
  30 + with SingleTickerProviderStateMixin {
  31 + late final AnimationController _controller;
  32 + late final LottieComposition _composition;
  33 + static const String TAG = "_RecorderPlaybackWidget";
  34 +
  35 + bool _isPlaying = false;
  36 +
  37 + @override
  38 + void initState() {
  39 + super.initState();
  40 + _controller = AnimationController(vsync: this);
  41 + _loadComposition();
  42 +
  43 + _controller.addListener(() {
  44 + // Log.d("$TAG addListener _controller=${_controller.status}");
  45 + });
  46 + }
  47 +
  48 + Future<void> _loadComposition() async {
  49 + final composition =
  50 + await AssetLottie('assets/lotties/recorder_back.zip').load();
  51 + setState(() {
  52 + _composition = composition;
  53 + _controller.duration = _composition.duration;
  54 + });
  55 +
  56 + if (widget.isPlaying) {
  57 + _playAnimation();
  58 + }
  59 + }
  60 +
  61 + void _playAnimation() {
  62 + _controller.reset();
  63 + _controller.repeat(
  64 + min: 2 / _composition.endFrame,
  65 + max: 22 / _composition.endFrame,
  66 + );
  67 + }
  68 +
  69 + void _stopAnimation() {
  70 + _controller.stop();
  71 + _resetAnimation();
  72 + }
  73 +
  74 + void _resetAnimation() {
  75 + setState(() {
  76 + _isPlaying = false;
  77 + _controller.value = 1;
  78 + // _controller.repeat(
  79 + // min: 1 / _composition.endFrame,
  80 + // max: 1 / _composition.endFrame,
  81 + // );
  82 + });
  83 + }
  84 +
  85 + void _displayAnimation(bool clickable) {
  86 + if (clickable) {
  87 + _controller.value = 1;
  88 + // _controller.repeat(
  89 + // min: 1,
  90 + // max: 1,
  91 + // );
  92 + } else {
  93 + _controller.value = 0;
  94 + // _controller.repeat(
  95 + // min: 0,
  96 + // max: 0,
  97 + // );
  98 + }
  99 + }
  100 +
  101 + @override
  102 + void didUpdateWidget(_RecorderPlaybackWidget oldWidget) {
  103 + super.didUpdateWidget(oldWidget);
  104 + Log.d(
  105 + "$TAG didUpdateWidget widget=${widget.isPlaying} oldWidget=${oldWidget.isPlaying} _isPlaying=$_isPlaying");
  106 + if (widget.isPlaying && !_isPlaying) {
  107 + setState(() {
  108 + _isPlaying = true;
  109 + });
  110 + _playAnimation();
  111 + } else if (!widget.isPlaying && _isPlaying) {
  112 + _stopAnimation();
  113 + }
  114 +
  115 + if (!_isPlaying) {
  116 + _displayAnimation(widget.isClickable);
  117 + }
  118 + }
  119 +
  120 + @override
  121 + void dispose() {
  122 + _controller.dispose();
  123 + super.dispose();
  124 + }
  125 +
  126 + @override
  127 + Widget build(BuildContext context) {
  128 + return ThrottledGestureDetector(
  129 + onTap: widget.isClickable ? widget.onTap : null,
  130 + child: Opacity(
  131 + opacity: widget.isClickable ? 1.0 : 0.5, // 设置透明度
  132 + child: Lottie(
  133 + composition: _composition,
  134 + controller: _controller,
  135 + renderCache: RenderCache.raster,
  136 + width: widget.width,
  137 + height: widget.height,
  138 + ),
  139 + ),
  140 + );
  141 + }
  142 +}
lib/common/widgets/recorder_widget.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:lottie/lottie.dart';
  3 +import 'package:wow_english/common/widgets/throttledGesture_gesture_detector.dart';
  4 +
  5 +import '../../utils/log_util.dart';
  6 +
  7 +/// 录音组件
  8 +class RecorderWidget extends StatefulWidget {
  9 + final bool isClickable;
  10 + final bool isPlaying;
  11 + final VoidCallback? onTap;
  12 + final double width;
  13 + final double height;
  14 +
  15 + const RecorderWidget({
  16 + Key? key,
  17 + required this.isClickable,
  18 + required this.isPlaying,
  19 + required this.onTap,
  20 + required this.width,
  21 + required this.height,
  22 + }) : super(key: key);
  23 +
  24 + @override
  25 + _RecorderWidgetState createState() => _RecorderWidgetState();
  26 +}
  27 +
  28 +class _RecorderWidgetState extends State<RecorderWidget>
  29 + with SingleTickerProviderStateMixin {
  30 + late final AnimationController _controller;
  31 + late final LottieComposition _composition;
  32 + static const String TAG = "RecorderWidget";
  33 +
  34 + bool _isPlaying = false;
  35 +
  36 + @override
  37 + void initState() {
  38 + super.initState();
  39 + _controller = AnimationController(vsync: this);
  40 + _loadComposition();
  41 +
  42 + _controller.addListener(() {
  43 + // Log.d("$TAG addListener _controller=${_controller.status}");
  44 + });
  45 + }
  46 +
  47 + Future<void> _loadComposition() async {
  48 + final composition = await AssetLottie('assets/lotties/recorder_input.zip').load();
  49 + setState(() {
  50 + _composition = composition;
  51 + _controller.duration = _composition.duration;
  52 + });
  53 +
  54 + if (widget.isPlaying) {
  55 + _playInitialAnimation();
  56 + }
  57 + }
  58 +
  59 + void _playInitialAnimation() {
  60 + _controller.reset();
  61 + _controller
  62 + .animateTo(22 / _composition.endFrame)
  63 + .whenComplete(() => _loopMiddleAnimation());
  64 + }
  65 +
  66 + void _loopMiddleAnimation() {
  67 + _controller.repeat(
  68 + min: 22 / _composition.endFrame,
  69 + max: 37 / _composition.endFrame,
  70 + );
  71 + }
  72 +
  73 + void _playFinalAnimation() {
  74 + _controller.stop();
  75 + _controller
  76 + .animateTo(50 / _composition.endFrame)
  77 + .whenComplete(() => _resetAnimation());
  78 + }
  79 +
  80 + void _resetAnimation() {
  81 + setState(() {
  82 + _isPlaying = false;
  83 + _controller.value = 0;
  84 + });
  85 + }
  86 +
  87 + @override
  88 + void didUpdateWidget(RecorderWidget oldWidget) {
  89 + super.didUpdateWidget(oldWidget);
  90 + Log.d("$TAG didUpdateWidget widget=${widget.isPlaying} oldWidget=${oldWidget.isPlaying} _isPlaying=$_isPlaying");
  91 + if (widget.isPlaying && !_isPlaying) {
  92 + setState(() {
  93 + _isPlaying = true;
  94 + });
  95 + _playInitialAnimation();
  96 + } else if (!widget.isPlaying && _isPlaying) {
  97 + _playFinalAnimation();
  98 + }
  99 + }
  100 +
  101 + @override
  102 + void dispose() {
  103 + _controller.dispose();
  104 + super.dispose();
  105 + }
  106 +
  107 + @override
  108 + Widget build(BuildContext context) {
  109 + return ThrottledGestureDetector(
  110 + onTap: widget.isClickable ? widget.onTap : null,
  111 + child: Opacity(
  112 + opacity: widget.isClickable ? 1.0 : 0.5, // 设置透明度
  113 + child: Lottie(
  114 + composition: _composition,
  115 + controller: _controller,
  116 + renderCache: RenderCache.raster,
  117 + width: widget.width,
  118 + height: widget.height,
  119 + ),
  120 + ),
  121 + );
  122 + }
  123 +}
lib/common/widgets/speaker_widget.dart
@@ -2,6 +2,9 @@ import &#39;package:flutter/material.dart&#39;; @@ -2,6 +2,9 @@ import &#39;package:flutter/material.dart&#39;;
2 import 'dart:async'; 2 import 'dart:async';
3 3
4 import 'package:wow_english/common/extension/string_extension.dart'; 4 import 'package:wow_english/common/extension/string_extension.dart';
  5 +import 'package:wow_english/common/widgets/throttledGesture_gesture_detector.dart';
  6 +
  7 +import '../../utils/log_util.dart';
5 8
6 /// 扬声器播放组件,帧动画 9 /// 扬声器播放组件,帧动画
7 class SpeakerWidget extends StatefulWidget { 10 class SpeakerWidget extends StatefulWidget {
@@ -34,6 +37,9 @@ class _SpeakerWidgetState extends State&lt;SpeakerWidget&gt; @@ -34,6 +37,9 @@ class _SpeakerWidgetState extends State&lt;SpeakerWidget&gt;
34 'ic_speaker_play0'.assetPng, 37 'ic_speaker_play0'.assetPng,
35 'ic_speaker_play1'.assetPng, 38 'ic_speaker_play1'.assetPng,
36 ]; 39 ];
  40 + static const String TAG = "SpeakerWidget";
  41 +
  42 + bool _isPlaying = false;
37 43
38 @override 44 @override
39 void initState() { 45 void initState() {
@@ -51,12 +57,15 @@ class _SpeakerWidgetState extends State&lt;SpeakerWidget&gt; @@ -51,12 +57,15 @@ class _SpeakerWidgetState extends State&lt;SpeakerWidget&gt;
51 @override 57 @override
52 void didUpdateWidget(SpeakerWidget oldWidget) { 58 void didUpdateWidget(SpeakerWidget oldWidget) {
53 super.didUpdateWidget(oldWidget); 59 super.didUpdateWidget(oldWidget);
54 - if (widget.isPlaying != oldWidget.isPlaying) {  
55 - if (widget.isPlaying) {  
56 - _startAnimation();  
57 - } else {  
58 - _stopAnimation();  
59 - } 60 + Log.d(
  61 + "$TAG didUpdateWidget widget=${widget.isPlaying} oldWidget=${oldWidget.isPlaying} _isPlaying=$_isPlaying");
  62 + if (widget.isPlaying && !_isPlaying) {
  63 + setState(() {
  64 + _isPlaying = true;
  65 + });
  66 + _startAnimation();
  67 + } else if (!widget.isPlaying && _isPlaying) {
  68 + _stopAnimation();
60 } 69 }
61 } 70 }
62 71
@@ -71,6 +80,7 @@ class _SpeakerWidgetState extends State&lt;SpeakerWidget&gt; @@ -71,6 +80,7 @@ class _SpeakerWidgetState extends State&lt;SpeakerWidget&gt;
71 void _stopAnimation() { 80 void _stopAnimation() {
72 _timer?.cancel(); 81 _timer?.cancel();
73 setState(() { 82 setState(() {
  83 + _isPlaying = false;
74 _currentFrame = 0; 84 _currentFrame = 0;
75 }); 85 });
76 } 86 }
@@ -84,13 +94,19 @@ class _SpeakerWidgetState extends State&lt;SpeakerWidget&gt; @@ -84,13 +94,19 @@ class _SpeakerWidgetState extends State&lt;SpeakerWidget&gt;
84 94
85 @override 95 @override
86 Widget build(BuildContext context) { 96 Widget build(BuildContext context) {
87 - return GestureDetector( 97 + return ThrottledGestureDetector(
88 onTap: widget.isClickable ? widget.onTap : null, 98 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, 99 + child: Opacity(
  100 + opacity: widget.isClickable ? 1.0 : 0.5,
  101 + child: Image.asset(
  102 + _speakerImagePaths[_currentFrame],
  103 + width: widget.width,
  104 + height: widget.height,
  105 + fit: BoxFit.cover,
  106 + color: widget.isClickable ? null : Colors.transparent,
  107 + // 灰色遮罩
  108 + colorBlendMode: widget.isClickable ? null : BlendMode.saturation,
  109 + ),
94 ), 110 ),
95 ); 111 );
96 } 112 }
lib/common/widgets/throttledGesture_gesture_detector.dart
@@ -4,7 +4,7 @@ import &#39;package:flutter/material.dart&#39;; @@ -4,7 +4,7 @@ import &#39;package:flutter/material.dart&#39;;
4 ///带节流功能的GestureDetector 4 ///带节流功能的GestureDetector
5 class ThrottledGestureDetector extends StatefulWidget { 5 class ThrottledGestureDetector extends StatefulWidget {
6 final Widget child; 6 final Widget child;
7 - final VoidCallback onTap; 7 + final VoidCallback? onTap;
8 final int throttleTime; 8 final int throttleTime;
9 9
10 const ThrottledGestureDetector({ 10 const ThrottledGestureDetector({
@@ -24,7 +24,9 @@ class _ThrottledGestureDetectorState extends State&lt;ThrottledGestureDetector&gt; { @@ -24,7 +24,9 @@ class _ThrottledGestureDetectorState extends State&lt;ThrottledGestureDetector&gt; {
24 24
25 void _handleTap() { 25 void _handleTap() {
26 if (!_isThrottled) { 26 if (!_isThrottled) {
27 - widget.onTap(); 27 + if (widget.onTap != null) {
  28 + widget.onTap!();
  29 + }
28 _isThrottled = true; 30 _isThrottled = true;
29 Timer(Duration(milliseconds: widget.throttleTime), () { 31 Timer(Duration(milliseconds: widget.throttleTime), () {
30 _isThrottled = false; 32 _isThrottled = false;
lib/pages/practice/bloc/topic_picture_bloc.dart
@@ -53,7 +53,7 @@ class TopicPictureBloc @@ -53,7 +53,7 @@ class TopicPictureBloc
53 CourseProcessEntity? get entity => _entity; 53 CourseProcessEntity? get entity => _entity;
54 54
55 ///正在评测 55 ///正在评测
56 - bool _isVoicing = false; 56 + bool _isRecording = false;
57 57
58 ///正在播放音频 58 ///正在播放音频
59 VoicePlayState _voicePlayState = VoicePlayState.unKnow; 59 VoicePlayState _voicePlayState = VoicePlayState.unKnow;
@@ -72,7 +72,7 @@ class TopicPictureBloc @@ -72,7 +72,7 @@ class TopicPictureBloc
72 72
73 int get selectItem => _selectItem; 73 int get selectItem => _selectItem;
74 74
75 - bool get isVoicing => _isVoicing; 75 + bool get isRecording => _isRecording;
76 76
77 VoicePlayState get voicePlayState => _voicePlayState; 77 VoicePlayState get voicePlayState => _voicePlayState;
78 78
@@ -277,7 +277,7 @@ class TopicPictureBloc @@ -277,7 +277,7 @@ class TopicPictureBloc
277 'type': event.type, 277 'type': event.type,
278 'userId': event.userId.toString() 278 'userId': event.userId.toString()
279 }); 279 });
280 - _isVoicing = true; 280 + _isRecording = true;
281 emitter(XSVoiceTestState()); 281 emitter(XSVoiceTestState());
282 } 282 }
283 } 283 }
@@ -300,7 +300,7 @@ class TopicPictureBloc @@ -300,7 +300,7 @@ class TopicPictureBloc
300 final result = args['result'] as Map; 300 final result = args['result'] as Map;
301 final overall = result['overall'].toString(); 301 final overall = result['overall'].toString();
302 showToast('测评成功,分数是$overall', duration: const Duration(seconds: 5)); 302 showToast('测评成功,分数是$overall', duration: const Duration(seconds: 5));
303 - _isVoicing = false; 303 + _isRecording = false;
304 emitter(XSVoiceTestState()); 304 emitter(XSVoiceTestState());
305 if (isLastPage()) { 305 if (isLastPage()) {
306 showStepPage(); 306 showStepPage();
lib/pages/practice/topic_picture_page.dart
@@ -11,9 +11,8 @@ import &#39;package:wow_english/pages/practice/widgets/shake_widget.dart&#39;; @@ -11,9 +11,8 @@ import &#39;package:wow_english/pages/practice/widgets/shake_widget.dart&#39;;
11 import 'package:wow_english/route/route.dart'; 11 import 'package:wow_english/route/route.dart';
12 import 'package:wow_english/utils/toast_util.dart'; 12 import 'package:wow_english/utils/toast_util.dart';
13 13
  14 +import '../../common/widgets/recorder_widget.dart';
14 import '../../common/widgets/speaker_widget.dart'; 15 import '../../common/widgets/speaker_widget.dart';
15 -import '../../common/widgets/throttledGesture_gesture_detector.dart';  
16 -import '../../utils/log_util.dart';  
17 import 'bloc/topic_picture_bloc.dart'; 16 import 'bloc/topic_picture_bloc.dart';
18 import 'widgets/practice_header_widget.dart'; 17 import 'widgets/practice_header_widget.dart';
19 18
@@ -167,10 +166,10 @@ class _TopicPicturePage extends StatelessWidget { @@ -167,10 +166,10 @@ class _TopicPicturePage extends StatelessWidget {
167 builder: (context, state) { 166 builder: (context, state) {
168 final bloc = BlocProvider.of<TopicPictureBloc>(context); 167 final bloc = BlocProvider.of<TopicPictureBloc>(context);
169 final isAnswerOption = bloc.selectItem == index; 168 final isAnswerOption = bloc.selectItem == index;
170 - final answerCorrect = isAnswerOption &&  
171 - bloc.checkAnswerRight(index) == true;  
172 - final answerIncorrect = isAnswerOption &&  
173 - bloc.checkAnswerRight(index) == false; 169 + final answerCorrect =
  170 + isAnswerOption && bloc.checkAnswerRight(index) == true;
  171 + final answerIncorrect =
  172 + isAnswerOption && bloc.checkAnswerRight(index) == false;
174 return Container( 173 return Container(
175 padding: EdgeInsets.symmetric(horizontal: 10.w), 174 padding: EdgeInsets.symmetric(horizontal: 10.w),
176 child: GestureDetector( 175 child: GestureDetector(
@@ -239,10 +238,10 @@ class _TopicPicturePage extends StatelessWidget { @@ -239,10 +238,10 @@ class _TopicPicturePage extends StatelessWidget {
239 builder: (context, state) { 238 builder: (context, state) {
240 final bloc = BlocProvider.of<TopicPictureBloc>(context); 239 final bloc = BlocProvider.of<TopicPictureBloc>(context);
241 final isAnswerOption = bloc.selectItem == index; 240 final isAnswerOption = bloc.selectItem == index;
242 - final answerCorrect = isAnswerOption &&  
243 - bloc.checkAnswerRight(index) == true;  
244 - final answerIncorrect = isAnswerOption &&  
245 - bloc.checkAnswerRight(index) == false; 241 + final answerCorrect =
  242 + isAnswerOption && bloc.checkAnswerRight(index) == true;
  243 + final answerIncorrect =
  244 + isAnswerOption && bloc.checkAnswerRight(index) == false;
246 return Container( 245 return Container(
247 padding: EdgeInsets.symmetric(horizontal: 10.w), 246 padding: EdgeInsets.symmetric(horizontal: 10.w),
248 child: GestureDetector( 247 child: GestureDetector(
@@ -348,10 +347,10 @@ class _TopicPicturePage extends StatelessWidget { @@ -348,10 +347,10 @@ class _TopicPicturePage extends StatelessWidget {
348 builder: (context, state) { 347 builder: (context, state) {
349 final bloc = BlocProvider.of<TopicPictureBloc>(context); 348 final bloc = BlocProvider.of<TopicPictureBloc>(context);
350 final isAnswerOption = bloc.selectItem == index; 349 final isAnswerOption = bloc.selectItem == index;
351 - final answerCorrect = isAnswerOption &&  
352 - bloc.checkAnswerRight(index) == true;  
353 - final answerIncorrect = isAnswerOption &&  
354 - bloc.checkAnswerRight(index) == false; 350 + final answerCorrect =
  351 + isAnswerOption && bloc.checkAnswerRight(index) == true;
  352 + final answerIncorrect =
  353 + isAnswerOption && bloc.checkAnswerRight(index) == false;
355 return ShakeWidget( 354 return ShakeWidget(
356 shouldShake: answerIncorrect, 355 shouldShake: answerIncorrect,
357 child: Container( 356 child: Container(
@@ -425,10 +424,10 @@ class _TopicPicturePage extends StatelessWidget { @@ -425,10 +424,10 @@ class _TopicPicturePage extends StatelessWidget {
425 builder: (context, state) { 424 builder: (context, state) {
426 final bloc = BlocProvider.of<TopicPictureBloc>(context); 425 final bloc = BlocProvider.of<TopicPictureBloc>(context);
427 final isAnswerOption = bloc.selectItem == index; 426 final isAnswerOption = bloc.selectItem == index;
428 - final answerCorrect = isAnswerOption &&  
429 - bloc.checkAnswerRight(index) == true;  
430 - final answerIncorrect = isAnswerOption &&  
431 - bloc.checkAnswerRight(index) == false; 427 + final answerCorrect =
  428 + isAnswerOption && bloc.checkAnswerRight(index) == true;
  429 + final answerIncorrect =
  430 + isAnswerOption && bloc.checkAnswerRight(index) == false;
432 return GestureDetector( 431 return GestureDetector(
433 onTap: () => bloc.add(SelectItemEvent(index)), 432 onTap: () => bloc.add(SelectItemEvent(index)),
434 child: ShakeWidget( 433 child: ShakeWidget(
@@ -505,40 +504,31 @@ class _TopicPicturePage extends StatelessWidget { @@ -505,40 +504,31 @@ class _TopicPicturePage extends StatelessWidget {
505 Column( 504 Column(
506 mainAxisAlignment: MainAxisAlignment.center, 505 mainAxisAlignment: MainAxisAlignment.center,
507 children: [ 506 children: [
508 - GestureDetector(  
509 - onTap: () {  
510 - if (bloc.isVoicing) {  
511 - showToast('正在录音,不能终止');  
512 - return;  
513 - }  
514 - bloc.add(VoicePlayEvent());  
515 - },  
516 - child: Row(  
517 - children: [  
518 - SpeakerWidget(  
519 - isPlaying: bloc.voicePlayState == VoicePlayState.playing, // 控制动画播放  
520 - isClickable: true, // 控制是否可点击  
521 - width: 45.w,  
522 - height: 45.w,  
523 - onTap: () {  
524 - Log.d("Speaker tapped");  
525 - },  
526 - ),  
527 - 10.horizontalSpace,  
528 - Text(topics?.word ?? '')  
529 - ],  
530 - ), 507 + Row(
  508 + children: [
  509 + SpeakerWidget(
  510 + isPlaying: bloc.voicePlayState == VoicePlayState.playing,
  511 + // 控制动画播放
  512 + isClickable: !bloc.isRecording,
  513 + // 控制是否可点击
  514 + width: 45.w,
  515 + height: 45.w,
  516 + onTap: () {
  517 + bloc.add(VoicePlayEvent());
  518 + },
  519 + ),
  520 + 10.horizontalSpace,
  521 + Text(topics?.word ?? '')
  522 + ],
531 ), 523 ),
532 70.verticalSpace, 524 70.verticalSpace,
533 - ThrottledGestureDetector(  
534 - throttleTime: 1000, 525 + RecorderWidget(
  526 + isPlaying: bloc.isRecording,
  527 + isClickable: bloc.voicePlayState != VoicePlayState.playing,
  528 + width: 72.w,
  529 + height: 72.w,
535 onTap: () { 530 onTap: () {
536 - if (bloc.voicePlayState == VoicePlayState.playing) {  
537 - showToast('正在播放音频,不能终止');  
538 - return;  
539 - }  
540 -  
541 - if (bloc.isVoicing) { 531 + if (bloc.isRecording) {
542 bloc.add(XSVoiceStopEvent()); 532 bloc.add(XSVoiceStopEvent());
543 return; 533 return;
544 } 534 }
@@ -551,14 +541,7 @@ class _TopicPicturePage extends StatelessWidget { @@ -551,14 +541,7 @@ class _TopicPicturePage extends StatelessWidget {
551 UserUtil.getUser()!.id.toString())); 541 UserUtil.getUser()!.id.toString()));
552 } 542 }
553 }, 543 },
554 - child: Image.asset(  
555 - bloc.isVoicing  
556 - ? 'micro_phone'.assetGif  
557 - : 'micro_phone'.assetPng,  
558 - height: 46.h,  
559 - width: 46.w,  
560 - ),  
561 - ) 544 + ),
562 ], 545 ],
563 ) 546 )
564 ], 547 ],