Commit a506beff7f4ef20d2016b98e901c8aeda51af822

Authored by 吴启风
1 parent 608c05b4

feat:先声sdk方法找不到问题修复;绘本接口&逻辑

android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/MainActivity.kt
@@ -16,6 +16,8 @@ class MainActivity : FlutterActivity() { @@ -16,6 +16,8 @@ class MainActivity : FlutterActivity() {
16 override fun onCreate(savedInstanceState: Bundle?) { 16 override fun onCreate(savedInstanceState: Bundle?) {
17 super.onCreate(savedInstanceState) 17 super.onCreate(savedInstanceState)
18 Log.i("WowEnglish", "MainActivity onCreate") 18 Log.i("WowEnglish", "MainActivity onCreate")
  19 +
  20 + flutterEngine?.let { SingSoungMethodChannel(this, it) }
19 } 21 }
20 22
21 override fun onResume() { 23 override fun onResume() {
@@ -46,9 +48,4 @@ class MainActivity : FlutterActivity() { @@ -46,9 +48,4 @@ class MainActivity : FlutterActivity() {
46 // 打开沉浸式 48 // 打开沉浸式
47 WindowCompat.setDecorFitsSystemWindows(window, false)*/ 49 WindowCompat.setDecorFitsSystemWindows(window, false)*/
48 } 50 }
49 -  
50 - override fun configureFlutterEngine(flutterEngine: FlutterEngine) {  
51 - super.configureFlutterEngine(flutterEngine)  
52 - SingSoungMethodChannel(this, flutterEngine)  
53 - }  
54 } 51 }
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/methodChannels/SingSoungMethodChannel.kt
1 package com.kouyuxingqiu.wow_english.methodChannels 1 package com.kouyuxingqiu.wow_english.methodChannels
2 2
  3 +import android.util.Log
  4 +import com.kouyuxingqiu.wow_english.singsound.SingEngineHelper
3 import io.flutter.embedding.android.FlutterActivity 5 import io.flutter.embedding.android.FlutterActivity
4 import io.flutter.embedding.engine.FlutterEngine 6 import io.flutter.embedding.engine.FlutterEngine
5 import io.flutter.plugin.common.MethodChannel 7 import io.flutter.plugin.common.MethodChannel
@@ -30,10 +32,18 @@ class SingSoungMethodChannel(val activity: FlutterActivity, val flutterEngine: F @@ -30,10 +32,18 @@ class SingSoungMethodChannel(val activity: FlutterActivity, val flutterEngine: F
30 ) 32 )
31 methodChannel?.setMethodCallHandler { call, result -> 33 methodChannel?.setMethodCallHandler { call, result ->
32 when (call.method) { 34 when (call.method) {
33 - "startRecord" -> {  
34 - val jsonStr = call.arguments as? String ?: return@setMethodCallHandler 35 + "initVoiceSdk" -> {
  36 + SingEngineHelper.init(activity)
  37 + }
  38 + "startVoice" -> {
  39 + val paramMap = call.arguments as HashMap<String, String>
  40 + Log.d("WQF", "SingSoungMethodChannel startVoice=${call.arguments.javaClass} paramMap=$paramMap")
  41 + paramMap["word"]?.let { SingEngineHelper.startRecord(it) }
35 //do nothing 42 //do nothing
36 } 43 }
  44 + "stopVoice" -> {
  45 + Log.d("WQF", "SingSoungMethodChannel stopVoice")
  46 + }
37 else -> { 47 else -> {
38 result.notImplemented() 48 result.notImplemented()
39 } 49 }
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/singsound/SingEngineHelper.kt
@@ -18,7 +18,7 @@ import org.json.JSONObject @@ -18,7 +18,7 @@ import org.json.JSONObject
18 import java.util.* 18 import java.util.*
19 19
20 20
21 -class SingEngineHelper private constructor() : 21 +object SingEngineHelper :
22 AudioErrorCallback, EvalReturnRequestIdCallback, OnRealTimeResultListener { 22 AudioErrorCallback, EvalReturnRequestIdCallback, OnRealTimeResultListener {
23 23
24 private val TAG = "SingEngineManager" 24 private val TAG = "SingEngineManager"
@@ -54,57 +54,59 @@ class SingEngineHelper private constructor() : @@ -54,57 +54,59 @@ class SingEngineHelper private constructor() :
54 mListeners = mutableListOf() 54 mListeners = mutableListOf()
55 if (mSingEngine == null) { 55 if (mSingEngine == null) {
56 mSingEngine = SingEngine.newInstance(context) 56 mSingEngine = SingEngine.newInstance(context)
57 - }  
58 - Thread {  
59 - try {  
60 - mSingEngine?.run {  
61 - // 设置测评结果监听器  
62 - setListener(this@SingEngineHelper)  
63 - // 设置录音器初始化错误的回调  
64 - setAudioErrorCallback(this@SingEngineHelper)  
65 - setEvalReturnRequestIdCallback(this@SingEngineHelper) 57 + Thread {
  58 + try {
  59 + mSingEngine?.run {
  60 + // 设置测评结果监听器
  61 + setListener(this@SingEngineHelper)
  62 + // 设置录音器初始化错误的回调
  63 + setAudioErrorCallback(this@SingEngineHelper)
  64 + setEvalReturnRequestIdCallback(this@SingEngineHelper)
66 // // 设置音频格式 65 // // 设置音频格式
67 // setAudioType(AudioTypeEnum.WAV) 66 // setAudioType(AudioTypeEnum.WAV)
68 - // 设置引擎类型。引擎类型(在线CLOUD、 离线NATIVE、混合AUTO),默认使用在线引擎。  
69 - setServerType(CoreProvideTypeEnum.CLOUD)  
70 - // 设置log日志级别  
71 - setLogLevel(4)  
72 - // 禁用实时音量返回  
73 - disableVolume()  
74 - // 设置录音音频路径  
75 - wavPath = AiUtil.getFilesDir(context).path + "/userdata/sound_record/"  
76 - // 设置是否开启 VAD 功能  
77 - setOpenVad(true, "vad.0.1.bin")  
78 - //setOpenVad(false, null);  
79 - // 设置 VAD 前置超时时间  
80 - setFrontVadTime(3000) 67 + // 设置引擎类型。引擎类型(在线CLOUD、 离线NATIVE、混合AUTO),默认使用在线引擎。
  68 + setServerType(CoreProvideTypeEnum.CLOUD)
  69 + // 设置log日志级别
  70 + setLogLevel(4)
  71 + // 禁用实时音量返回
  72 + disableVolume()
  73 + // 设置录音音频路径
  74 + wavPath = AiUtil.getFilesDir(context).path + "/userdata/sound_record/"
  75 + // 设置是否开启 VAD 功能
  76 + setOpenVad(true, "vad.0.1.bin")
  77 + //setOpenVad(false, null);
  78 + // 设置 VAD 前置超时时间
  79 + setFrontVadTime(3000)
81 // setServerTimeout(10000) 80 // setServerTimeout(10000)
82 - // 开启错误日志保存到本地,发生错误时文件中会保存到android/data/包名/files/SSError.txt中 81 + // 开启错误日志保存到本地,发生错误时文件中会保存到android/data/包名/files/SSError.txt中
83 // setOpenWriteLog(true) 82 // setOpenWriteLog(true)
84 - // 设置在线服务器地址和账号  
85 - setServerAPI("wss://api.cloud.ssapi.cn") 83 + // 设置在线服务器地址和账号
  84 + setServerAPI("wss://api.cloud.ssapi.cn")
86 // // 设置评测语言(针对离线评测) 85 // // 设置评测语言(针对离线评测)
87 // setOffLineSource(OffLineSourceEnum.SOURCE_EN) 86 // setOffLineSource(OffLineSourceEnum.SOURCE_EN)
88 - // 设置引擎初始化参数  
89 - setNewCfg(  
90 - buildInitJson(  
91 - SingSoundConfig.APPKEY,  
92 - SingSoundConfig.SECERTKEY 87 + // 设置引擎初始化参数
  88 + setNewCfg(
  89 + buildInitJson(
  90 + SingSoundConfig.APPKEY,
  91 + SingSoundConfig.SECERTKEY
  92 + )
93 ) 93 )
94 - )  
95 - // 引擎初始化  
96 - createEngine()  
97 - } 94 + // 引擎初始化
  95 + createEngine("1")
98 96
99 - getSymbolsMap()  
100 - } catch (e: Exception) {  
101 - e.printStackTrace()  
102 - }  
103 - }.start() 97 + Log.w(TAG, "createEngine")
  98 + }
  99 +
  100 + getSymbolsMap()
  101 + } catch (e: Exception) {
  102 + e.printStackTrace()
  103 + }
  104 + }.start()
  105 + }
104 } 106 }
105 107
106 // 开始语音评测 108 // 开始语音评测
107 - fun startRecord(originText: String, @EvalTargetType evalTargetType: Int?) { 109 + fun startRecord(originText: String, @EvalTargetType evalTargetType: Int? = EvalTargetType.SENTENCE) {
108 try { 110 try {
109 val request = JSONObject() 111 val request = JSONObject()
110 when (evalTargetType) { 112 when (evalTargetType) {
lib/generated/json/course_process_entity.g.dart
@@ -38,9 +38,9 @@ Map&lt;String, dynamic&gt; $CourseProcessEntityToJson(CourseProcessEntity entity) { @@ -38,9 +38,9 @@ Map&lt;String, dynamic&gt; $CourseProcessEntityToJson(CourseProcessEntity entity) {
38 38
39 CourseProcessReadings $CourseProcessReadingsFromJson(Map<String, dynamic> json) { 39 CourseProcessReadings $CourseProcessReadingsFromJson(Map<String, dynamic> json) {
40 final CourseProcessReadings courseProcessReadings = CourseProcessReadings(); 40 final CourseProcessReadings courseProcessReadings = CourseProcessReadings();
41 - final String? auditUrl = jsonConvert.convert<String>(json['auditUrl']);  
42 - if (auditUrl != null) {  
43 - courseProcessReadings.auditUrl = auditUrl; 41 + final String? audioUrl = jsonConvert.convert<String>(json['audioUrl']);
  42 + if (audioUrl != null) {
  43 + courseProcessReadings.audioUrl = audioUrl;
44 } 44 }
45 final int? courseLessonId = jsonConvert.convert<int>(json['courseLessonId']); 45 final int? courseLessonId = jsonConvert.convert<int>(json['courseLessonId']);
46 if (courseLessonId != null) { 46 if (courseLessonId != null) {
@@ -83,7 +83,7 @@ CourseProcessReadings $CourseProcessReadingsFromJson(Map&lt;String, dynamic&gt; json) @@ -83,7 +83,7 @@ CourseProcessReadings $CourseProcessReadingsFromJson(Map&lt;String, dynamic&gt; json)
83 83
84 Map<String, dynamic> $CourseProcessReadingsToJson(CourseProcessReadings entity) { 84 Map<String, dynamic> $CourseProcessReadingsToJson(CourseProcessReadings entity) {
85 final Map<String, dynamic> data = <String, dynamic>{}; 85 final Map<String, dynamic> data = <String, dynamic>{};
86 - data['auditUrl'] = entity.auditUrl; 86 + data['audioUrl'] = entity.audioUrl;
87 data['courseLessonId'] = entity.courseLessonId; 87 data['courseLessonId'] = entity.courseLessonId;
88 data['createTime'] = entity.createTime; 88 data['createTime'] = entity.createTime;
89 data['deleted'] = entity.deleted; 89 data['deleted'] = entity.deleted;
lib/models/course_process_entity.dart
@@ -24,7 +24,7 @@ class CourseProcessEntity { @@ -24,7 +24,7 @@ class CourseProcessEntity {
24 24
25 @JsonSerializable() 25 @JsonSerializable()
26 class CourseProcessReadings { 26 class CourseProcessReadings {
27 - String? auditUrl; 27 + String? audioUrl;
28 int? courseLessonId; 28 int? courseLessonId;
29 String? createTime; 29 String? createTime;
30 String? deleted; 30 String? deleted;
lib/pages/practice/bloc/topic_picture_bloc.dart
@@ -142,7 +142,7 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; { @@ -142,7 +142,7 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
142 void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async { 142 void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async {
143 EasyLoading.show(status: '录音中....'); 143 EasyLoading.show(status: '录音中....');
144 methodChannel.invokeMethod( 144 methodChannel.invokeMethod(
145 - 'starVoice', 145 + 'startVoice',
146 {'word':event.testWord,'type':event.type,'userId':event.userId.toString()} 146 {'word':event.testWord,'type':event.type,'userId':event.userId.toString()}
147 ); 147 );
148 _isVoicing = true; 148 _isVoicing = true;
lib/pages/reading/bloc/reading_bloc.dart
  1 +import 'package:audioplayers/audioplayers.dart';
1 import 'package:flutter/cupertino.dart'; 2 import 'package:flutter/cupertino.dart';
  3 +import 'package:flutter/foundation.dart';
  4 +import 'package:flutter/services.dart';
2 import 'package:flutter_bloc/flutter_bloc.dart'; 5 import 'package:flutter_bloc/flutter_bloc.dart';
  6 +import 'package:flutter_easyloading/flutter_easyloading.dart';
3 import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart'; 7 import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart';
4 8
  9 +import '../../../common/request/dao/listen_dao.dart';
  10 +import '../../../common/request/exception.dart';
  11 +import '../../../models/course_process_entity.dart';
  12 +import '../../../utils/loading.dart';
  13 +
5 part 'reading_event.dart'; 14 part 'reading_event.dart';
6 part 'reading_state.dart'; 15 part 'reading_state.dart';
7 16
8 class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { 17 class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> {
9 -  
10 final PageController pageController; 18 final PageController pageController;
11 19
12 ///当前页索引 20 ///当前页索引
@@ -19,26 +27,68 @@ class ReadingPageBloc extends Bloc&lt;ReadingPageEvent, ReadingPageState&gt; { @@ -19,26 +27,68 @@ class ReadingPageBloc extends Bloc&lt;ReadingPageEvent, ReadingPageState&gt; {
19 27
20 ReadingModeType get currentMode => _currentMode; 28 ReadingModeType get currentMode => _currentMode;
21 29
  30 + CourseProcessEntity? _entity;
  31 +
  32 + CourseProcessEntity? get entity => _entity;
  33 +
  34 + ///正在评测
  35 + bool _isRecording = false;
  36 +
  37 + bool get isRecording => _isRecording;
  38 +
  39 + late MethodChannel methodChannel;
  40 +
  41 + late AudioPlayer audioPlayer;
  42 +
22 ReadingPageBloc(this.pageController) : super(ReadingPageInitial()) { 43 ReadingPageBloc(this.pageController) : super(ReadingPageInitial()) {
23 on<CurrentPageIndexChangeEvent>(_pageControllerChange); 44 on<CurrentPageIndexChangeEvent>(_pageControllerChange);
24 on<CurrentModeChangeEvent>(_selectItemLoad); 45 on<CurrentModeChangeEvent>(_selectItemLoad);
25 // pageController.addListener(() { 46 // pageController.addListener(() {
26 // _currentPage = pageController.page!.round(); 47 // _currentPage = pageController.page!.round();
27 // }); 48 // });
  49 + on<RequestDataEvent>(_requestData);
  50 + on<ReadingPageEvent>((event, emit) {
  51 + //音频播放器
  52 + audioPlayer = AudioPlayer();
  53 + audioPlayer.onPlayerStateChanged.listen((event) {
  54 + if (event == PlayerState.completed) {
  55 + if (kDebugMode) {
  56 + print('绘本播放完成');
  57 +
  58 + }
  59 + }
  60 + });
  61 +
  62 + methodChannel = const MethodChannel('sing_sound_method_channel');
  63 + methodChannel.invokeMethod('initVoiceSdk',{});
  64 + methodChannel.setMethodCallHandler((call) async {
  65 + if (call.method == 'voiceResult') {//评测结束
  66 + // add(XSVoiceResultEvent(call.arguments));
  67 + }
  68 + });
  69 + });
  70 + on<PlayOriginalAudioEvent>(_playOriginalAudio);
  71 + on<XSVoiceTestEvent>(_voiceXsTest);
  72 + on<XSVoiceResultEvent>(_voiceXsResult);
28 } 73 }
29 74
30 @override 75 @override
31 Future<void> close() { 76 Future<void> close() {
32 pageController.dispose(); 77 pageController.dispose();
  78 + audioPlayer.release();
  79 + audioPlayer.dispose();
33 return super.close(); 80 return super.close();
34 } 81 }
35 82
36 - void _pageControllerChange(CurrentPageIndexChangeEvent event, Emitter<ReadingPageState> emitter) async { 83 + void _pageControllerChange(CurrentPageIndexChangeEvent event,
  84 + Emitter<ReadingPageState> emitter) async {
37 _currentPage = event.pageIndex; 85 _currentPage = event.pageIndex;
  86 + _playOriginVoice(null);
38 emitter(CurrentPageIndexState()); 87 emitter(CurrentPageIndexState());
39 } 88 }
40 89
41 - void _selectItemLoad(CurrentModeChangeEvent event, Emitter<ReadingPageState> emitter) async { 90 + void _selectItemLoad(
  91 + CurrentModeChangeEvent event, Emitter<ReadingPageState> emitter) async {
42 if (_currentMode == ReadingModeType.auto) { 92 if (_currentMode == ReadingModeType.auto) {
43 _currentMode = ReadingModeType.manual; 93 _currentMode = ReadingModeType.manual;
44 } else { 94 } else {
@@ -46,4 +96,77 @@ class ReadingPageBloc extends Bloc&lt;ReadingPageEvent, ReadingPageState&gt; { @@ -46,4 +96,77 @@ class ReadingPageBloc extends Bloc&lt;ReadingPageEvent, ReadingPageState&gt; {
46 } 96 }
47 emitter(CurrentModeState()); 97 emitter(CurrentModeState());
48 } 98 }
  99 +
  100 + ///请求数据
  101 + void _requestData(
  102 + RequestDataEvent event, Emitter<ReadingPageState> emitter) async {
  103 + try {
  104 + await loading(() async {
  105 + _entity = await ListenDao.process('1');
  106 + print("reading page entity: ${_entity!.toJson()}");
  107 + emitter(RequestDataState());
  108 + });
  109 + } catch (e) {
  110 + if (e is ApiException) {
  111 + EasyLoading.showToast(e.message ?? '请求失败,请检查网络连接');
  112 + }
  113 + }
  114 + }
  115 +
  116 + void _playOriginalAudio(PlayOriginalAudioEvent event, Emitter<ReadingPageState> emitter) async {
  117 + print("_playOriginalAudio");
  118 + _playOriginVoice(event.url);
  119 + }
  120 +
  121 + /// 播放绘本原音
  122 + void _playOriginVoice(String? audioUrl) async {
  123 + audioPlayer.stop();
  124 + final readingData = currentPageData();
  125 + if (readingData?.audioUrl != null) {
  126 + final urlStr = audioUrl ?? readingData?.audioUrl ?? '';
  127 + if (urlStr.isNotEmpty) {
  128 + audioPlayer.play(UrlSource(urlStr));
  129 + }
  130 + }
  131 + }
  132 +
  133 + int dataCount() {
  134 + // print("dataCount=${_entity?.readings?.length ?? 0}");
  135 + return _entity?.readings?.length ?? 0;
  136 + }
  137 +
  138 + CourseProcessReadings? currentPageData() {
  139 + return _entity?.readings?[_currentPage];
  140 + }
  141 +
  142 + ///先声测试
  143 + void _voiceXsTest(XSVoiceTestEvent event, Emitter<ReadingPageState> emitter) async {
  144 + startRecord(event.content);
  145 + emitter(XSVoiceTestState());
  146 + }
  147 +
  148 + void startRecord(String content) async {
  149 + if (_isRecording == true) {
  150 + return;
  151 + }
  152 + EasyLoading.show(status: '录音中....');
  153 + methodChannel.invokeMethod(
  154 + 'startVoice',
  155 + {'word':'how old are you','type':'0','userId':'1'}
  156 + );
  157 + _isRecording = true;
  158 + }
  159 +
  160 + void _voiceXsResult(XSVoiceResultEvent event,Emitter<ReadingPageState> emitter) async {
  161 + final Map args = event.message as Map;
  162 + final result = args['result'] as String;
  163 + if (result == '1') {
  164 + final overall = args['overall'].toString();
  165 + EasyLoading.showToast('测评成功,分数是$overall',duration: const Duration(seconds: 10));
  166 + } else {
  167 + EasyLoading.showToast('测评失败',duration: const Duration(seconds: 10));
  168 + }
  169 + _isRecording = false;
  170 + emitter(XSVoiceTestState());
  171 + }
49 } 172 }
lib/pages/reading/bloc/reading_event.dart
@@ -8,4 +8,33 @@ class CurrentPageIndexChangeEvent extends ReadingPageEvent { @@ -8,4 +8,33 @@ class CurrentPageIndexChangeEvent extends ReadingPageEvent {
8 CurrentPageIndexChangeEvent(this.pageIndex); 8 CurrentPageIndexChangeEvent(this.pageIndex);
9 } 9 }
10 10
11 -class CurrentModeChangeEvent extends ReadingPageEvent {}  
12 \ No newline at end of file 11 \ No newline at end of file
  12 +class CurrentModeChangeEvent extends ReadingPageEvent {}
  13 +
  14 +///请求接口获取数据
  15 +class RequestDataEvent extends ReadingPageEvent {}
  16 +
  17 +///播放原音频
  18 +class PlayOriginalAudioEvent extends ReadingPageEvent {
  19 + final String? url;
  20 + PlayOriginalAudioEvent(this.url);
  21 +}
  22 +
  23 +///初始化先声SDK
  24 +class XSVoiceInitEvent extends ReadingPageEvent {
  25 + final Map data;
  26 + XSVoiceInitEvent(this.data);
  27 +}
  28 +
  29 +///评测结果
  30 +class XSVoiceResultEvent extends ReadingPageEvent {
  31 + final dynamic message;
  32 + XSVoiceResultEvent(this.message);
  33 +}
  34 +
  35 +///先声测试
  36 +class XSVoiceTestEvent extends ReadingPageEvent {
  37 + final String content;
  38 + final String type;
  39 + final String userId;
  40 + XSVoiceTestEvent(this.content,this.type,this.userId);
  41 +}
13 \ No newline at end of file 42 \ No newline at end of file
lib/pages/reading/bloc/reading_state.dart
@@ -9,3 +9,7 @@ class CurrentPageIndexState extends ReadingPageState {} @@ -9,3 +9,7 @@ class CurrentPageIndexState extends ReadingPageState {}
9 9
10 /// 手动or自动播放 10 /// 手动or自动播放
11 class CurrentModeState extends ReadingPageState {} 11 class CurrentModeState extends ReadingPageState {}
  12 +
  13 +class RequestDataState extends ReadingPageState {}
  14 +
  15 +class XSVoiceTestState extends ReadingPageState {}
lib/pages/reading/reading_page.dart
@@ -4,6 +4,8 @@ import &#39;package:flutter_screenutil/flutter_screenutil.dart&#39;; @@ -4,6 +4,8 @@ import &#39;package:flutter_screenutil/flutter_screenutil.dart&#39;;
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/pages/reading/widgets/ReadingModeType.dart'; 5 import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart';
6 6
  7 +import '../../common/core/user_util.dart';
  8 +import '../../models/course_process_entity.dart';
7 import 'bloc/reading_bloc.dart'; 9 import 'bloc/reading_bloc.dart';
8 10
9 class ReadingPage extends StatelessWidget { 11 class ReadingPage extends StatelessWidget {
@@ -12,7 +14,7 @@ class ReadingPage extends StatelessWidget { @@ -12,7 +14,7 @@ class ReadingPage extends StatelessWidget {
12 @override 14 @override
13 Widget build(BuildContext context) { 15 Widget build(BuildContext context) {
14 return BlocProvider( 16 return BlocProvider(
15 - create: (_) => ReadingPageBloc(PageController()), 17 + create: (_) => ReadingPageBloc(PageController())..add(RequestDataEvent()),
16 child: _ReadingPage(), 18 child: _ReadingPage(),
17 ); 19 );
18 } 20 }
@@ -22,7 +24,15 @@ class _ReadingPage extends StatelessWidget { @@ -22,7 +24,15 @@ class _ReadingPage extends StatelessWidget {
22 @override 24 @override
23 Widget build(BuildContext context) { 25 Widget build(BuildContext context) {
24 return BlocListener<ReadingPageBloc, ReadingPageState>( 26 return BlocListener<ReadingPageBloc, ReadingPageState>(
25 - listener: (context, state) {}, 27 + listener: (context, state) {
  28 + if (state is RequestDataState) {
  29 + // context.read<TopicPictureBloc>().add(CurrentPageIndexChangeEvent(0));
  30 + print('reading RequestDataState=$state');
  31 +
  32 + ///刷新页面
  33 + context.read<ReadingPageBloc>().add(CurrentPageIndexChangeEvent(0));
  34 + }
  35 + },
26 child: _readingPageView(), 36 child: _readingPageView(),
27 ); 37 );
28 } 38 }
@@ -36,13 +46,13 @@ class _ReadingPage extends StatelessWidget { @@ -36,13 +46,13 @@ class _ReadingPage extends StatelessWidget {
36 child: Stack( 46 child: Stack(
37 children: [ 47 children: [
38 PageView.builder( 48 PageView.builder(
39 - itemCount: 10, 49 + itemCount: bloc.dataCount(),
40 controller: bloc.pageController, 50 controller: bloc.pageController,
41 onPageChanged: (int index) { 51 onPageChanged: (int index) {
42 bloc.add(CurrentPageIndexChangeEvent(index)); 52 bloc.add(CurrentPageIndexChangeEvent(index));
43 }, 53 },
44 itemBuilder: (context, int index) { 54 itemBuilder: (context, int index) {
45 - return _readingPagerItem(); 55 + return _readingPagerItem(bloc.entity!.readings![index]);
46 }), 56 }),
47 Container( 57 Container(
48 color: Colors.transparent, 58 color: Colors.transparent,
@@ -76,15 +86,14 @@ class _ReadingPage extends StatelessWidget { @@ -76,15 +86,14 @@ class _ReadingPage extends StatelessWidget {
76 ), 86 ),
77 alignment: Alignment.center, 87 alignment: Alignment.center,
78 child: Text( 88 child: Text(
79 - '${bloc.currentPage}/10',  
80 -  
81 - ///todo 分母需要替换成数据数组长度 89 + '${bloc.currentPage}/${bloc.dataCount()}',
82 style: TextStyle(fontSize: 20.sp, color: Colors.white), 90 style: TextStyle(fontSize: 20.sp, color: Colors.white),
83 ), 91 ),
84 ), 92 ),
85 93
86 Padding( 94 Padding(
87 - padding: EdgeInsets.only(right: 15.w + ScreenUtil().bottomBarHeight), 95 + padding: EdgeInsets.only(
  96 + right: 15.w + ScreenUtil().bottomBarHeight),
88 child: GestureDetector( 97 child: GestureDetector(
89 onTap: () { 98 onTap: () {
90 bloc.add(CurrentModeChangeEvent()); 99 bloc.add(CurrentModeChangeEvent());
@@ -121,17 +130,26 @@ class _ReadingPage extends StatelessWidget { @@ -121,17 +130,26 @@ class _ReadingPage extends StatelessWidget {
121 margin: EdgeInsets.symmetric(horizontal: 10.w), 130 margin: EdgeInsets.symmetric(horizontal: 10.w),
122 child: Row( 131 child: Row(
123 children: [ 132 children: [
124 - Image.asset(  
125 - 'voice'.assetPng,  
126 - height: 40.h,  
127 - width: 45.w, 133 + GestureDetector(
  134 + onTap: () {
  135 + if (bloc.isRecording) {
  136 + return;
  137 + }
  138 + print("voice tap");
  139 + bloc.add(PlayOriginalAudioEvent(null));
  140 + },
  141 + child: Image.asset(
  142 + 'voice'.assetPng,
  143 + height: 40.h,
  144 + width: 45.w,
  145 + ),
128 ), 146 ),
129 SizedBox( 147 SizedBox(
130 width: 10.w, 148 width: 10.w,
131 ), 149 ),
132 Expanded( 150 Expanded(
133 child: Text( 151 child: Text(
134 - "HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld", 152 + bloc.currentPageData()?.word ?? '',
135 style: TextStyle( 153 style: TextStyle(
136 color: const Color(0xFF333333), fontSize: 21.sp), 154 color: const Color(0xFF333333), fontSize: 21.sp),
137 maxLines: 2, 155 maxLines: 2,
@@ -140,11 +158,18 @@ class _ReadingPage extends StatelessWidget { @@ -140,11 +158,18 @@ class _ReadingPage extends StatelessWidget {
140 SizedBox( 158 SizedBox(
141 width: 10.w, 159 width: 10.w,
142 ), 160 ),
143 - Image.asset(  
144 - 'micro_phone'.assetPng,  
145 - height: 47.h,  
146 - width: 47.w,  
147 - ), 161 + GestureDetector(
  162 + onTap: () {
  163 + if (bloc.isRecording) {
  164 + return;
  165 + }
  166 + bloc.add(XSVoiceTestEvent(bloc.currentPageData()?.word??'', '0',UserUtil.getUser()!.id.toString()));
  167 + },
  168 + child: Image.asset(
  169 + 'micro_phone'.assetPng,
  170 + height: 47.h,
  171 + width: 47.w,
  172 + )),
148 SizedBox( 173 SizedBox(
149 width: 10.w, 174 width: 10.w,
150 ), 175 ),
@@ -169,14 +194,12 @@ class _ReadingPage extends StatelessWidget { @@ -169,14 +194,12 @@ class _ReadingPage extends StatelessWidget {
169 ); 194 );
170 }); 195 });
171 196
172 - Widget _readingPagerItem() => 197 + Widget _readingPagerItem(CourseProcessReadings readings) =>
173 BlocBuilder<ReadingPageBloc, ReadingPageState>(builder: (context, state) { 198 BlocBuilder<ReadingPageBloc, ReadingPageState>(builder: (context, state) {
174 return Stack( 199 return Stack(
175 children: [ 200 children: [
176 - Image.network(  
177 - 'https://img.liblibai.com/web/648331d5a2cb5.png?image_process=format,webp&x-oss-process=image/resize,w_2980,m_lfit/format,webp',  
178 - height: double.infinity,  
179 - width: double.infinity), 201 + Image.network(readings.picUrl ?? '',
  202 + height: double.infinity, width: double.infinity),
180 ], 203 ],
181 ); 204 );
182 }); 205 });