From 2fb3c7d2b557f8c8b43492fc234afb8cc1ca63a1 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 12 Mar 2025 14:22:56 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E7=94=B5=E6=9C=BA?= =?UTF-8?q?=E5=8A=9F=E7=8E=87=E6=93=8D=E4=BD=9C=E6=88=90=E5=8A=9F=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E3=80=81=E4=B8=8D=E5=9C=A8=E9=94=81=E8=BE=B9=E5=BA=94?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E6=93=8D=E4=BD=9C=E5=A4=B1=E8=B4=A5=E3=80=81?= =?UTF-8?q?=E6=97=A0=E7=BD=91=E7=BB=9C=E5=9C=A8=E7=82=B9=E5=87=BB=E6=97=B6?= =?UTF-8?q?=E5=BA=94=E6=8F=90=E7=A4=BA=E7=BD=91=E7=BB=9C=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lockSet/motorPower/motorPower_logic.dart | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/main/lockDetail/lockSet/motorPower/motorPower_logic.dart b/lib/main/lockDetail/lockSet/motorPower/motorPower_logic.dart index 44b72e64..79f860ea 100755 --- a/lib/main/lockDetail/lockSet/motorPower/motorPower_logic.dart +++ b/lib/main/lockDetail/lockSet/motorPower/motorPower_logic.dart @@ -30,9 +30,10 @@ class MotorPowerLogic extends BaseGetXController { state.lockSetInfoData.value.lockSettingInfo!.openDirectionValue = state.motorTorsion.value; - eventBus - .fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value)); - showToast('操作成功'.tr); + showToast('操作成功'.tr, something: () { + eventBus + .fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value)); + }); } } @@ -80,6 +81,7 @@ class MotorPowerLogic extends BaseGetXController { switch (status) { case 0x00: //成功 + cancelBlueConnetctToastTimer(); _setLockSetGeneralSetting(); break; case 0x06: @@ -123,6 +125,10 @@ class MotorPowerLogic extends BaseGetXController { // 设置支持功能(带参数) Future sendOpenDoorDirection() async { + showEasyLoading(); + showBlueConnetctToastTimer(action: () { + dismissEasyLoading(); + }); BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async { if (connectionState == BluetoothConnectionState.connected) { @@ -149,6 +155,10 @@ class MotorPowerLogic extends BaseGetXController { needAuthor: 1, publicKey: getPublicKeyList, privateKey: getPrivateKeyList); + } else if (connectionState == BluetoothConnectionState.disconnected) { + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); + showBlueConnetctToast(); } }); } From 794bf8cf1127a9c15a0775b231b9ea3bc68e34a0 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 12 Mar 2025 17:31:54 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=E8=B0=83=E6=95=B4=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=97=B6=E4=B8=80=E7=9B=B4=E8=BD=AC=E5=9C=88?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../doorLockLog/doorLockLog_logic.dart | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/main/lockDetail/doorLockLog/doorLockLog_logic.dart b/lib/main/lockDetail/doorLockLog/doorLockLog_logic.dart index 052ac7e8..4999b8ed 100755 --- a/lib/main/lockDetail/doorLockLog/doorLockLog_logic.dart +++ b/lib/main/lockDetail/doorLockLog/doorLockLog_logic.dart @@ -54,9 +54,11 @@ class DoorLockLogLogic extends BaseGetXController { switch (status) { case 0x00: + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); //成功 final int dataLength = (reply.data[5] << 8) + reply.data[6]; - // AppLog.log("dataLength:$dataLength"); + AppLog.log("dataLength:$dataLength"); // var dataLength = reply.data[5]; if (dataLength > 0) { reply.data.removeRange(0, 7); @@ -108,6 +110,8 @@ class DoorLockLogLogic extends BaseGetXController { state.ifHaveNext = false; } lockRecordUploadData(uploadList); + } else { + showToast('暂无最新记录'.tr); } break; case 0x06: @@ -117,6 +121,7 @@ class DoorLockLogLogic extends BaseGetXController { default: //失败 dismissEasyLoading(); + cancelBlueConnetctToastTimer(); break; } } @@ -148,30 +153,23 @@ class DoorLockLogLogic extends BaseGetXController { ).toString(); showEasyLoading(); - showBlueConnetctToastTimer( - isShowBlueConnetctToast: true, - action: () async { - cancelBlueConnetctToastTimer(); + showBlueConnetctToastTimer(action: () async { + dismissEasyLoading(); + final String getMobile = (await Storage.getMobile())!; + ApmHelper.instance.trackEvent('check_doorLockLog', { + 'lockName': state.keyInfos.value.lockName!, + 'account': + getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!, + 'date': DateTool().getNowDateWithType(1), + 'open_lock_result': '超时', + }); - final String getMobile = (await Storage.getMobile())!; - ApmHelper.instance.trackEvent('check_doorLockLog', { - 'lockName': state.keyInfos.value.lockName!, - 'account': - getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!, - 'date': DateTool().getNowDateWithType(1), - 'open_lock_result': '超时', - }); - - BuglyTool.uploadException( - message: '查询锁记录超时-查询锁记录失败', - detail: - '添加密码超时,查询锁记录失败--senderReferEventRecordTimeCommand:$command', - eventStr: '查询锁记录事件超时', - upload: true); - if (state.isLockReceiveResponse == false) { - dismissEasyLoading(); - } - }); + BuglyTool.uploadException( + message: '查询锁记录超时-查询锁记录失败', + detail: '添加密码超时,查询锁记录失败--senderReferEventRecordTimeCommand:$command', + eventStr: '查询锁记录事件超时', + upload: true); + }); BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionStateState) async { if (connectionStateState == BluetoothConnectionState.connected) { @@ -303,20 +301,22 @@ class DoorLockLogLogic extends BaseGetXController { lockId: state.keyInfos.value.lockId.toString(), records: list); final String getMobile = (await Storage.getMobile())!; if (entity.errorCode!.codeIsSuccessful) { - if (state.ifHaveNext == true) { - showEasyLoading(); - getLockRecordLastUploadDataTime(); - } else { - ApmHelper.instance.trackEvent('check_doorLockLog', { - 'lockName': state.keyInfos.value.lockName!, - 'account': - getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!, - 'date': DateTool().getNowDateWithType(1), - 'open_lock_result': '成功', - }); - mockNetworkDataRequest(isRefresh: true); - } - dismissEasyLoading(); + showToast('操作成功'.tr, something: () async { + dismissEasyLoading(); + if (state.ifHaveNext == true) { + showEasyLoading(); + getLockRecordLastUploadDataTime(); + } else { + ApmHelper.instance.trackEvent('check_doorLockLog', { + 'lockName': state.keyInfos.value.lockName!, + 'account': + getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!, + 'date': DateTool().getNowDateWithType(1), + 'open_lock_result': '成功', + }); + mockNetworkDataRequest(isRefresh: true); + } + }); } else { ApmHelper.instance.trackEvent('check_doorLockLog', { 'lockName': state.keyInfos.value.lockName!, From fcdd09fcb2448d0579bbbcf816e1a842311b96f3 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 12 Mar 2025 17:32:26 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=E8=B0=83=E6=95=B4IOS=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E5=88=86=E4=BA=AB=E9=80=89=E6=A8=A1=E6=9D=BF=E5=90=8E?= =?UTF-8?q?=E5=92=8C=E7=94=B5=E5=AD=90=E9=92=A5=E5=8C=99=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E9=94=AE=E7=9B=98=E6=97=A0=E6=B3=95=E6=94=B6?= =?UTF-8?q?=E8=B5=B7=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sendEmailNotification/sendEmailNotification_page.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/main/lockDetail/electronicKey/sendEmailNotification/sendEmailNotification_page.dart b/lib/main/lockDetail/electronicKey/sendEmailNotification/sendEmailNotification_page.dart index 90e4c33e..06efc928 100755 --- a/lib/main/lockDetail/electronicKey/sendEmailNotification/sendEmailNotification_page.dart +++ b/lib/main/lockDetail/electronicKey/sendEmailNotification/sendEmailNotification_page.dart @@ -95,6 +95,9 @@ class _SendEmailNotificationPageState extends State { maxLength: 1000, textAlign: TextAlign.start, controller: state.templateContentController, + keyboardType: TextInputType.multiline, // 多行文本键盘类型 + textInputAction: TextInputAction.done, // 键盘完成按钮 + onEditingComplete: () => FocusScope.of(context).unfocus(), // 点击完成 style: TextStyle( color: Colors.black, fontSize: 22.sp, From 0cdaa26fe5112d748c44b4a87e095dfb6d622b51 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 12 Mar 2025 17:42:02 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=E8=B0=83=E6=95=B4image=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E5=AF=B9=E8=AE=B2=E7=9A=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/talkView/talk_view_logic.dart | 256 +++++++++++++++--- .../views/talkView/talk_view_page.dart | 24 +- .../views/talkView/talk_view_state.dart | 3 + 3 files changed, 228 insertions(+), 55 deletions(-) diff --git a/lib/talk/starChart/views/talkView/talk_view_logic.dart b/lib/talk/starChart/views/talkView/talk_view_logic.dart index 71473bee..111741e3 100644 --- a/lib/talk/starChart/views/talkView/talk_view_logic.dart +++ b/lib/talk/starChart/views/talkView/talk_view_logic.dart @@ -38,17 +38,25 @@ class TalkViewLogic extends BaseGetXController { final LockDetailState lockDetailState = Get.put(LockDetailLogic()).state; Timer? _syncTimer; // 音视频播放刷新率定时器 Timer? _audioTimer; // 音视频播放刷新率定时器 + Timer? _networkQualityTimer; // 网络质量监测定时器 + int _startTime = 0; // 开始播放时间戳,用于判断帧数据中的时间戳位置 int bufferSize = 40; // 缓冲区大小(以帧为单位) int audioBufferSize = 500; // 缓冲区大小(以帧为单位) + // 帧率监控相关 + final List _lastFewFps = []; // 存储最近的帧率数据 - int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) + int frameIntervalMs = 83; // 初始帧间隔设置为83毫秒(12FPS) int audioFrameIntervalMs = 20; // 初始帧间隔设置为45毫秒(约22FPS) - int minFrameIntervalMs = 30; // 最小帧间隔(约33 FPS) - int maxFrameIntervalMs = 100; // 最大帧间隔(约1 FPS) + int minFrameIntervalMs = 83; // 最小帧间隔(12 FPS) + int maxFrameIntervalMs = 166; // 最大帧间隔(约6 FPS) // 定义音频帧缓冲和发送函数 final List _bufferedAudioFrames = []; + // 在类的开始处添加缓存相关变量 + final int maxImageCacheCount = 40; // 最大图片缓存数量 + final Map _imageCache = {}; + /// 初始化音频播放器 void _initFlutterPcmSound() { const int sampleRate = 8000; @@ -150,63 +158,205 @@ class TalkViewLogic extends BaseGetXController { /// 播放视频数据 void _playVideoData(TalkData talkData) async { - state.listData.value = Uint8List.fromList(talkData.content); + try { + // 计算当前帧的哈希值作为缓存key + String cacheKey = talkData.content.hashCode.toString(); + + // 检查缓存 + if (_imageCache.containsKey(cacheKey)) { + // 使用缓存的解码图片 + state.currentImage.value = _imageCache[cacheKey]; + } else { + // 将 List 转换为 Uint8List + final Uint8List uint8Data = Uint8List.fromList(talkData.content); + // 在后台线程解码图片 + ui.Image? image = await decodeImageFromList(uint8Data); + + // 缓存管理:如果缓存太大则移除最早的项 + if (_imageCache.length >= maxImageCacheCount) { + _imageCache.remove(_imageCache.keys.first); + } + + // 添加到缓存 + _imageCache[cacheKey] = image; + state.currentImage.value = image; + } + + // 更新显示数据 + state.listData.value = Uint8List.fromList(talkData.content); + } catch (e) { + print('视频帧解码错误: $e'); + } + // state.listData.value = Uint8List.fromList(talkData.content); } /// 启动播放 void _startPlayback() { Future.delayed(Duration(milliseconds: 800), () { + // 添加网络质量监测 + _networkQualityTimer ??= + Timer.periodic(const Duration(seconds: 5), _checkNetworkQuality); _startTime = DateTime.now().millisecondsSinceEpoch; _syncTimer ??= Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { // 动态调整帧间隔 _adjustFrameInterval(); + // 监控帧率稳定性 + _monitorFrameStability(); }); }); } /// 动态调整帧间隔 void _adjustFrameInterval() { - int newFrameIntervalMs = frameIntervalMs; - if (state.videoBuffer.length < 10 && frameIntervalMs < maxFrameIntervalMs) { - // 如果缓冲区较小且帧间隔小于最大值,则增加帧间隔 - frameIntervalMs += 5; - } else if (state.videoBuffer.length > 20 && - frameIntervalMs > minFrameIntervalMs) { - // 如果缓冲区较大且帧间隔大于最小值,则减少帧间隔 - frameIntervalMs -= 5; + // 计算目标帧间隔 + int targetInterval = _calculateTargetInterval(); + + // 平滑过渡到目标帧率,避免突变 + if (frameIntervalMs != targetInterval) { + // 每次最多调整2ms,使变化更平滑 + frameIntervalMs += (targetInterval > frameIntervalMs) ? 2 : -2; + + // 确保在合理范围内 + frameIntervalMs = + frameIntervalMs.clamp(minFrameIntervalMs, maxFrameIntervalMs); + + // 只在帧间隔变化超过阈值时才重建定时器 + if ((frameIntervalMs - targetInterval).abs() >= 5) { + _rebuildTimers(); + } } - // 只有在帧间隔发生变化时才重建定时器 - if (newFrameIntervalMs != frameIntervalMs) { - frameIntervalMs = newFrameIntervalMs; - // 取消旧的定时器 - _syncTimer?.cancel(); - _syncTimer = - Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { - // 播放视频帧 - _playVideoFrames(); - }); + // int newFrameIntervalMs = frameIntervalMs; + // if (state.videoBuffer.length < 10 && frameIntervalMs < maxFrameIntervalMs) { + // // 如果缓冲区较小且帧间隔小于最大值,则增加帧间隔 + // frameIntervalMs += 5; + // } else if (state.videoBuffer.length > 20 && + // frameIntervalMs > minFrameIntervalMs) { + // // 如果缓冲区较大且帧间隔大于最小值,则减少帧间隔 + // frameIntervalMs -= 5; + // } + // // 只有在帧间隔发生变化时才重建定时器 + // if (newFrameIntervalMs != frameIntervalMs) { + // frameIntervalMs = newFrameIntervalMs; + // // 取消旧的定时器 + // _syncTimer?.cancel(); + // _syncTimer = + // Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { + // // 播放视频帧 + // _playVideoFrames(); + // }); + // + // _audioTimer?.cancel(); + // _audioTimer = + // Timer.periodic(Duration(milliseconds: audioFrameIntervalMs), (timer) { + // final currentTime = DateTime.now().millisecondsSinceEpoch; + // final elapsedTime = currentTime - _startTime; + // + // // 播放合适的音频帧 + // if (state.audioBuffer.isNotEmpty && + // state.audioBuffer.first.durationMs <= elapsedTime) { + // // 判断音频开关是否打开 + // if (state.isOpenVoice.value) { + // _playAudioData(state.audioBuffer.removeAt(0)); + // } else { + // // 如果不播放音频,只从缓冲区中读取数据,但不移除 + // // 你可以根据需要调整此处逻辑,例如保留缓冲区的最大长度,防止无限增长 + // // 仅移除缓冲区数据但不播放音频,确保音频也是实时更新的 + // state.audioBuffer.removeAt(0); + // } + // } + // }); + // } + } - _audioTimer?.cancel(); - _audioTimer = - Timer.periodic(Duration(milliseconds: audioFrameIntervalMs), (timer) { - final currentTime = DateTime.now().millisecondsSinceEpoch; - final elapsedTime = currentTime - _startTime; + /// 监控帧率稳定性 + void _monitorFrameStability() { + const stabilityThreshold = 5; // 帧率波动阈值 + final currentFps = 1000 / frameIntervalMs; - // 播放合适的音频帧 - if (state.audioBuffer.isNotEmpty && - state.audioBuffer.first.durationMs <= elapsedTime) { - // 判断音频开关是否打开 - if (state.isOpenVoice.value) { - _playAudioData(state.audioBuffer.removeAt(0)); - } else { - // 如果不播放音频,只从缓冲区中读取数据,但不移除 - // 你可以根据需要调整此处逻辑,例如保留缓冲区的最大长度,防止无限增长 - // 仅移除缓冲区数据但不播放音频,确保音频也是实时更新的 - state.audioBuffer.removeAt(0); - } - } - }); + if (_lastFewFps.length >= 10) { + _lastFewFps.removeAt(0); + } + _lastFewFps.add(currentFps); + + // 计算帧率标准差 + if (_lastFewFps.length >= 5) { + double mean = _lastFewFps.reduce((a, b) => a + b) / _lastFewFps.length; + double variance = + _lastFewFps.map((fps) => pow(fps - mean, 2)).reduce((a, b) => a + b) / + _lastFewFps.length; + double stdDev = sqrt(variance); + + // 如果帧率波动过大,采取平滑措施 + if (stdDev > stabilityThreshold) { + _smoothFrameRate(mean); + } + } + } + + /// 检查网络质量 + void _checkNetworkQuality(Timer timer) { + final bufferHealth = state.videoBuffer.length / bufferSize; + + if (bufferHealth < 0.3) { + // 缓冲区不足30% + // 降低帧率以适应网络状况 + frameIntervalMs = min(frameIntervalMs + 10, maxFrameIntervalMs); + _rebuildTimers(); + } else if (bufferHealth > 0.7) { + // 缓冲区超过70% + // 提高帧率以提供更好体验 + frameIntervalMs = max(frameIntervalMs - 5, minFrameIntervalMs); + _rebuildTimers(); + } + } + + /// 计算目标帧间隔 + int _calculateTargetInterval() { + const int optimalBufferSize = 15; // 理想的缓冲区大小 + const int bufferTolerance = 5; // 缓冲区容差 + + if (state.videoBuffer.length < optimalBufferSize - bufferTolerance) { + // 缓冲区过小,降低帧率 + return (frameIntervalMs * 1.2).round(); + } else if (state.videoBuffer.length > optimalBufferSize + bufferTolerance) { + // 缓冲区过大,提高帧率 + return (frameIntervalMs * 0.8).round(); + } + return frameIntervalMs; + } + + /// 重建定时器 + void _rebuildTimers() { + // 取消现有定时器 + _syncTimer?.cancel(); + _audioTimer?.cancel(); + + // 创建新的视频定时器 + _syncTimer = + Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { + _playVideoFrames(); + }); + + // 创建新的音频定时器,使用固定间隔 + _audioTimer = + Timer.periodic(Duration(milliseconds: audioFrameIntervalMs), (timer) { + _processAudioFrame(); + }); + } + + /// 处理音频帧 + void _processAudioFrame() { + final currentTime = DateTime.now().millisecondsSinceEpoch; + final elapsedTime = currentTime - _startTime; + + while (state.audioBuffer.isNotEmpty && + state.audioBuffer.first.durationMs <= elapsedTime) { + if (state.isOpenVoice.value) { + _playAudioData(state.audioBuffer.removeAt(0)); + } else { + state.audioBuffer.removeAt(0); + } } } @@ -404,6 +554,24 @@ class TalkViewLogic extends BaseGetXController { requestPermissions(); } + /// 平滑帧率 + void _smoothFrameRate(double targetFps) { + // 计算目标帧间隔 + int targetInterval = (1000 / targetFps).round(); + + // 使用加权平均来平滑过渡 + double weight = 0.3; // 权重因子,可以根据需要调整 + frameIntervalMs = + (frameIntervalMs * (1 - weight) + targetInterval * weight).round(); + + // 确保帧间隔在合理范围内 + frameIntervalMs = + frameIntervalMs.clamp(minFrameIntervalMs, maxFrameIntervalMs); + + // 重建定时器 + _rebuildTimers(); + } + @override void onClose() { _stopPlayG711Data(); // 停止播放音频 @@ -416,7 +584,13 @@ class TalkViewLogic extends BaseGetXController { _audioTimer = null; // 释放定时器引用 state.oneMinuteTimeTimer?.cancel(); state.oneMinuteTimeTimer = null; + // 添加新的清理代码 + _networkQualityTimer?.cancel(); + _lastFewFps.clear(); stopProcessingAudio(); + // 清理图片缓存 + _imageCache.clear(); + super.onClose(); } diff --git a/lib/talk/starChart/views/talkView/talk_view_page.dart b/lib/talk/starChart/views/talkView/talk_view_page.dart index 2059d0ac..021d1616 100644 --- a/lib/talk/starChart/views/talkView/talk_view_page.dart +++ b/lib/talk/starChart/views/talkView/talk_view_page.dart @@ -133,20 +133,16 @@ class _TalkViewPageState extends State child: SizedBox.expand( child: RotatedBox( quarterTurns: -1, - child: Image.memory( - state.listData.value, - width: ScreenUtil().scaleWidth, - height: ScreenUtil().scaleHeight, - gaplessPlayback: true, - fit: BoxFit.cover, - filterQuality: FilterQuality.high, - errorBuilder: ( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - return Container(color: Colors.transparent); - }, + child: Obx( + () => state.currentImage.value != null + ? RawImage( + image: state.currentImage.value, + width: ScreenUtil().scaleWidth, + height: ScreenUtil().scaleHeight, + fit: BoxFit.cover, + filterQuality: FilterQuality.high, + ) + : Container(color: Colors.transparent), ), ), ), diff --git a/lib/talk/starChart/views/talkView/talk_view_state.dart b/lib/talk/starChart/views/talkView/talk_view_state.dart index 25538093..66974032 100644 --- a/lib/talk/starChart/views/talkView/talk_view_state.dart +++ b/lib/talk/starChart/views/talkView/talk_view_state.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:typed_data'; +import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter_voice_processor/flutter_voice_processor.dart'; @@ -89,4 +90,6 @@ class TalkViewState { RxBool isLongPressing = false.obs; // 旋转角度(以弧度为单位) RxBool hasAudioData = false.obs; // 是否有音频数据 RxInt lastAudioTimestamp = 0.obs; // 最后接收到的音频数据的时间戳 + // 添加图片状态变量 + final Rx currentImage = Rx(null); }