Commit a506beff7f4ef20d2016b98e901c8aeda51af822
1 parent
608c05b4
feat:先声sdk方法找不到问题修复;绘本接口&逻辑
Showing
10 changed files
with
269 additions
and
81 deletions
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/MainActivity.kt
| ... | ... | @@ -16,6 +16,8 @@ class MainActivity : FlutterActivity() { |
| 16 | 16 | override fun onCreate(savedInstanceState: Bundle?) { |
| 17 | 17 | super.onCreate(savedInstanceState) |
| 18 | 18 | Log.i("WowEnglish", "MainActivity onCreate") |
| 19 | + | |
| 20 | + flutterEngine?.let { SingSoungMethodChannel(this, it) } | |
| 19 | 21 | } |
| 20 | 22 | |
| 21 | 23 | override fun onResume() { |
| ... | ... | @@ -46,9 +48,4 @@ class MainActivity : FlutterActivity() { |
| 46 | 48 | // 打开沉浸式 |
| 47 | 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 | 1 | package com.kouyuxingqiu.wow_english.methodChannels |
| 2 | 2 | |
| 3 | +import android.util.Log | |
| 4 | +import com.kouyuxingqiu.wow_english.singsound.SingEngineHelper | |
| 3 | 5 | import io.flutter.embedding.android.FlutterActivity |
| 4 | 6 | import io.flutter.embedding.engine.FlutterEngine |
| 5 | 7 | import io.flutter.plugin.common.MethodChannel |
| ... | ... | @@ -30,10 +32,18 @@ class SingSoungMethodChannel(val activity: FlutterActivity, val flutterEngine: F |
| 30 | 32 | ) |
| 31 | 33 | methodChannel?.setMethodCallHandler { call, result -> |
| 32 | 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 | 42 | //do nothing |
| 36 | 43 | } |
| 44 | + "stopVoice" -> { | |
| 45 | + Log.d("WQF", "SingSoungMethodChannel stopVoice") | |
| 46 | + } | |
| 37 | 47 | else -> { |
| 38 | 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 | 18 | import java.util.* |
| 19 | 19 | |
| 20 | 20 | |
| 21 | -class SingEngineHelper private constructor() : | |
| 21 | +object SingEngineHelper : | |
| 22 | 22 | AudioErrorCallback, EvalReturnRequestIdCallback, OnRealTimeResultListener { |
| 23 | 23 | |
| 24 | 24 | private val TAG = "SingEngineManager" |
| ... | ... | @@ -54,57 +54,59 @@ class SingEngineHelper private constructor() : |
| 54 | 54 | mListeners = mutableListOf() |
| 55 | 55 | if (mSingEngine == null) { |
| 56 | 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 | 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 | 80 | // setServerTimeout(10000) |
| 82 | - // 开启错误日志保存到本地,发生错误时文件中会保存到android/data/包名/files/SSError.txt中 | |
| 81 | + // 开启错误日志保存到本地,发生错误时文件中会保存到android/data/包名/files/SSError.txt中 | |
| 83 | 82 | // setOpenWriteLog(true) |
| 84 | - // 设置在线服务器地址和账号 | |
| 85 | - setServerAPI("wss://api.cloud.ssapi.cn") | |
| 83 | + // 设置在线服务器地址和账号 | |
| 84 | + setServerAPI("wss://api.cloud.ssapi.cn") | |
| 86 | 85 | // // 设置评测语言(针对离线评测) |
| 87 | 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 | 110 | try { |
| 109 | 111 | val request = JSONObject() |
| 110 | 112 | when (evalTargetType) { | ... | ... |
lib/generated/json/course_process_entity.g.dart
| ... | ... | @@ -38,9 +38,9 @@ Map<String, dynamic> $CourseProcessEntityToJson(CourseProcessEntity entity) { |
| 38 | 38 | |
| 39 | 39 | CourseProcessReadings $CourseProcessReadingsFromJson(Map<String, dynamic> json) { |
| 40 | 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 | 45 | final int? courseLessonId = jsonConvert.convert<int>(json['courseLessonId']); |
| 46 | 46 | if (courseLessonId != null) { |
| ... | ... | @@ -83,7 +83,7 @@ CourseProcessReadings $CourseProcessReadingsFromJson(Map<String, dynamic> json) |
| 83 | 83 | |
| 84 | 84 | Map<String, dynamic> $CourseProcessReadingsToJson(CourseProcessReadings entity) { |
| 85 | 85 | final Map<String, dynamic> data = <String, dynamic>{}; |
| 86 | - data['auditUrl'] = entity.auditUrl; | |
| 86 | + data['audioUrl'] = entity.audioUrl; | |
| 87 | 87 | data['courseLessonId'] = entity.courseLessonId; |
| 88 | 88 | data['createTime'] = entity.createTime; |
| 89 | 89 | data['deleted'] = entity.deleted; | ... | ... |
lib/models/course_process_entity.dart
lib/pages/practice/bloc/topic_picture_bloc.dart
| ... | ... | @@ -142,7 +142,7 @@ class TopicPictureBloc extends Bloc<TopicPictureEvent, TopicPictureState> { |
| 142 | 142 | void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async { |
| 143 | 143 | EasyLoading.show(status: '录音中....'); |
| 144 | 144 | methodChannel.invokeMethod( |
| 145 | - 'starVoice', | |
| 145 | + 'startVoice', | |
| 146 | 146 | {'word':event.testWord,'type':event.type,'userId':event.userId.toString()} |
| 147 | 147 | ); |
| 148 | 148 | _isVoicing = true; | ... | ... |
lib/pages/reading/bloc/reading_bloc.dart
| 1 | +import 'package:audioplayers/audioplayers.dart'; | |
| 1 | 2 | import 'package:flutter/cupertino.dart'; |
| 3 | +import 'package:flutter/foundation.dart'; | |
| 4 | +import 'package:flutter/services.dart'; | |
| 2 | 5 | import 'package:flutter_bloc/flutter_bloc.dart'; |
| 6 | +import 'package:flutter_easyloading/flutter_easyloading.dart'; | |
| 3 | 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 | 14 | part 'reading_event.dart'; |
| 6 | 15 | part 'reading_state.dart'; |
| 7 | 16 | |
| 8 | 17 | class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 9 | - | |
| 10 | 18 | final PageController pageController; |
| 11 | 19 | |
| 12 | 20 | ///当前页索引 |
| ... | ... | @@ -19,26 +27,68 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 19 | 27 | |
| 20 | 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 | 43 | ReadingPageBloc(this.pageController) : super(ReadingPageInitial()) { |
| 23 | 44 | on<CurrentPageIndexChangeEvent>(_pageControllerChange); |
| 24 | 45 | on<CurrentModeChangeEvent>(_selectItemLoad); |
| 25 | 46 | // pageController.addListener(() { |
| 26 | 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 | 75 | @override |
| 31 | 76 | Future<void> close() { |
| 32 | 77 | pageController.dispose(); |
| 78 | + audioPlayer.release(); | |
| 79 | + audioPlayer.dispose(); | |
| 33 | 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 | 85 | _currentPage = event.pageIndex; |
| 86 | + _playOriginVoice(null); | |
| 38 | 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 | 92 | if (_currentMode == ReadingModeType.auto) { |
| 43 | 93 | _currentMode = ReadingModeType.manual; |
| 44 | 94 | } else { |
| ... | ... | @@ -46,4 +96,77 @@ class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { |
| 46 | 96 | } |
| 47 | 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 | 8 | CurrentPageIndexChangeEvent(this.pageIndex); |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | -class CurrentModeChangeEvent extends ReadingPageEvent {} | |
| 12 | 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 | 42 | \ No newline at end of file | ... | ... |
lib/pages/reading/bloc/reading_state.dart
lib/pages/reading/reading_page.dart
| ... | ... | @@ -4,6 +4,8 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; |
| 4 | 4 | import 'package:wow_english/common/extension/string_extension.dart'; |
| 5 | 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 | 9 | import 'bloc/reading_bloc.dart'; |
| 8 | 10 | |
| 9 | 11 | class ReadingPage extends StatelessWidget { |
| ... | ... | @@ -12,7 +14,7 @@ class ReadingPage extends StatelessWidget { |
| 12 | 14 | @override |
| 13 | 15 | Widget build(BuildContext context) { |
| 14 | 16 | return BlocProvider( |
| 15 | - create: (_) => ReadingPageBloc(PageController()), | |
| 17 | + create: (_) => ReadingPageBloc(PageController())..add(RequestDataEvent()), | |
| 16 | 18 | child: _ReadingPage(), |
| 17 | 19 | ); |
| 18 | 20 | } |
| ... | ... | @@ -22,7 +24,15 @@ class _ReadingPage extends StatelessWidget { |
| 22 | 24 | @override |
| 23 | 25 | Widget build(BuildContext context) { |
| 24 | 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 | 36 | child: _readingPageView(), |
| 27 | 37 | ); |
| 28 | 38 | } |
| ... | ... | @@ -36,13 +46,13 @@ class _ReadingPage extends StatelessWidget { |
| 36 | 46 | child: Stack( |
| 37 | 47 | children: [ |
| 38 | 48 | PageView.builder( |
| 39 | - itemCount: 10, | |
| 49 | + itemCount: bloc.dataCount(), | |
| 40 | 50 | controller: bloc.pageController, |
| 41 | 51 | onPageChanged: (int index) { |
| 42 | 52 | bloc.add(CurrentPageIndexChangeEvent(index)); |
| 43 | 53 | }, |
| 44 | 54 | itemBuilder: (context, int index) { |
| 45 | - return _readingPagerItem(); | |
| 55 | + return _readingPagerItem(bloc.entity!.readings![index]); | |
| 46 | 56 | }), |
| 47 | 57 | Container( |
| 48 | 58 | color: Colors.transparent, |
| ... | ... | @@ -76,15 +86,14 @@ class _ReadingPage extends StatelessWidget { |
| 76 | 86 | ), |
| 77 | 87 | alignment: Alignment.center, |
| 78 | 88 | child: Text( |
| 79 | - '${bloc.currentPage}/10', | |
| 80 | - | |
| 81 | - ///todo 分母需要替换成数据数组长度 | |
| 89 | + '${bloc.currentPage}/${bloc.dataCount()}', | |
| 82 | 90 | style: TextStyle(fontSize: 20.sp, color: Colors.white), |
| 83 | 91 | ), |
| 84 | 92 | ), |
| 85 | 93 | |
| 86 | 94 | Padding( |
| 87 | - padding: EdgeInsets.only(right: 15.w + ScreenUtil().bottomBarHeight), | |
| 95 | + padding: EdgeInsets.only( | |
| 96 | + right: 15.w + ScreenUtil().bottomBarHeight), | |
| 88 | 97 | child: GestureDetector( |
| 89 | 98 | onTap: () { |
| 90 | 99 | bloc.add(CurrentModeChangeEvent()); |
| ... | ... | @@ -121,17 +130,26 @@ class _ReadingPage extends StatelessWidget { |
| 121 | 130 | margin: EdgeInsets.symmetric(horizontal: 10.w), |
| 122 | 131 | child: Row( |
| 123 | 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 | 147 | SizedBox( |
| 130 | 148 | width: 10.w, |
| 131 | 149 | ), |
| 132 | 150 | Expanded( |
| 133 | 151 | child: Text( |
| 134 | - "HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld", | |
| 152 | + bloc.currentPageData()?.word ?? '', | |
| 135 | 153 | style: TextStyle( |
| 136 | 154 | color: const Color(0xFF333333), fontSize: 21.sp), |
| 137 | 155 | maxLines: 2, |
| ... | ... | @@ -140,11 +158,18 @@ class _ReadingPage extends StatelessWidget { |
| 140 | 158 | SizedBox( |
| 141 | 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 | 173 | SizedBox( |
| 149 | 174 | width: 10.w, |
| 150 | 175 | ), |
| ... | ... | @@ -169,14 +194,12 @@ class _ReadingPage extends StatelessWidget { |
| 169 | 194 | ); |
| 170 | 195 | }); |
| 171 | 196 | |
| 172 | - Widget _readingPagerItem() => | |
| 197 | + Widget _readingPagerItem(CourseProcessReadings readings) => | |
| 173 | 198 | BlocBuilder<ReadingPageBloc, ReadingPageState>(builder: (context, state) { |
| 174 | 199 | return Stack( |
| 175 | 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 | }); | ... | ... |