import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:star_lock/talk/call/callTalk.dart'; import 'package:star_lock/talk/startChart/constant/talk_status.dart'; import 'package:star_lock/talk/startChart/views/talkView/talk_view_logic.dart'; import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart'; import 'package:star_lock/talk/udp/udp_manage.dart'; import '../../../../app_settings/app_colors.dart'; import '../../../../tools/showTFView.dart'; class TalkViewPage extends StatefulWidget { const TalkViewPage({Key? key}) : super(key: key); @override State createState() => _TalkViewPageState(); } class _TalkViewPageState extends State with TickerProviderStateMixin { final TalkViewLogic logic = Get.put(TalkViewLogic()); final TalkViewState state = Get.find().state; @override void initState() { super.initState(); state.listData.value = Uint8List(0); //写一个定时器,三十秒后页面自动返回 // state.autoBackTimer = Timer(const Duration(seconds: 30), Get.back); state.animationController = AnimationController( vsync: this, // 确保使用的TickerProvider是当前Widget duration: const Duration(seconds: 1), ); state.animationController.repeat(); //动画开始、结束、向前移动或向后移动时会调用StatusListener state.animationController.addStatusListener((AnimationStatus status) { if (status == AnimationStatus.completed) { state.animationController.reset(); state.animationController.forward(); } else if (status == AnimationStatus.dismissed) { state.animationController.reset(); state.animationController.forward(); } }); } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { // 返回 false 表示禁止退出 return false; }, child: SizedBox( width: 1.sw, height: 1.sh, child: Stack( alignment: Alignment.center, children: [ Obx( () { final screenWidth = MediaQuery.of(context).size.width; final screenHeight = MediaQuery.of(context).size.height; final logicalWidth = MediaQuery.of(context).size.width; final logicalHeight = MediaQuery.of(context).size.height; final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; // 计算物理像素值 final physicalWidth = logicalWidth * devicePixelRatio; final physicalHeight = logicalHeight * devicePixelRatio; // 旋转后的图片尺寸 final rotatedImageWidth = 480; // 原始高度 final rotatedImageHeight = 864; // 原始宽度 // 计算缩放比例 final scaleWidth = physicalWidth / rotatedImageWidth; final scaleHeight = physicalHeight / rotatedImageHeight; final scale = max(scaleWidth, scaleHeight); // 选择较大的缩放比例 return state.listData.value.isEmpty ? Image.asset( 'images/main/monitorBg.png', width: screenWidth, height: screenHeight, fit: BoxFit.cover, ) : PopScope( canPop: false, child: RepaintBoundary( key: state.globalKey, child: Transform.rotate( angle: state.rotateAngle.value * (pi / 180), // 旋转 90 度 child: Transform.scale( scale: scale, // 动态计算的缩放比例 child: Image.memory( state.listData.value, gaplessPlayback: true, fit: BoxFit.cover, filterQuality: FilterQuality.high, errorBuilder: ( BuildContext context, Object error, StackTrace? stackTrace, ) { return Container(color: Colors.transparent); }, ), ), ), ), ); }, ), Obx(() => state.listData.value.isEmpty ? Positioned( bottom: 300.h, child: Text( '正在创建安全连接...'.tr, style: TextStyle(color: Colors.black, fontSize: 26.sp), )) : Container()), Positioned( bottom: 10.w, child: Container( width: 1.sw - 30.w * 2, // height: 300.h, margin: EdgeInsets.all(30.w), decoration: BoxDecoration( color: Colors.black.withOpacity(0.2), borderRadius: BorderRadius.circular(20.h)), child: Column( children: [ SizedBox(height: 20.h), bottomTopBtnWidget(), SizedBox(height: 20.h), bottomBottomBtnWidget(), SizedBox(height: 20.h), ], ), ), ), // Positioned( // top: 100.h, // left: 10.w, // child: Obx( // () => Text( // 'FPS:${state.fps.value}', // style: TextStyle( // fontSize: 30.sp, // color: Colors.orange, // fontWeight: FontWeight.bold), // ), // ), // ), Obx(() => state.listData.value.isEmpty ? buildRotationTransition() : Container()), Obx(() => state.isLongPressing.value ? Positioned( top: 80.h, left: 0, right: 0, child: Center( child: Container( padding: EdgeInsets.all(10.w), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(10.w), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.mic, color: Colors.white, size: 24.w), SizedBox(width: 10.w), Text( '正在说话...', style: TextStyle( fontSize: 20.sp, color: Colors.white), ), ], ), ), ), ) : Container()), ], ), ), ); } Widget bottomTopBtnWidget() { return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ // 打开关闭声音 GestureDetector( onTap: () { if (state.talkStatus.value == TalkStatus.answeredSuccessfully) { // 打开关闭声音 logic.updateTalkExpect(); } }, child: Container( width: 50.w, height: 50.w, padding: EdgeInsets.all(5.w), child: Obx(() => Image( width: 40.w, height: 40.w, image: state.isOpenVoice.value ? const AssetImage( 'images/main/icon_lockDetail_monitoringOpenVoice.png') : const AssetImage( 'images/main/icon_lockDetail_monitoringCloseVoice.png'))), ), ), SizedBox(width: 50.w), // 截图 GestureDetector( onTap: () async { if (state.talkStatus.value == TalkStatus.answeredSuccessfully) { await logic.captureAndSavePng(); } }, child: Container( width: 50.w, height: 50.w, padding: EdgeInsets.all(5.w), child: Image( width: 40.w, height: 40.w, image: const AssetImage( 'images/main/icon_lockDetail_monitoringScreenshot.png')), ), ), SizedBox(width: 50.w), // 录制 GestureDetector( onTap: () async { logic.showToast('功能暂未开放'.tr); // if ( // state.talkStatus.value == TalkStatus.answeredSuccessfully) { // if (state.isRecordingScreen.value) { // await logic.stopRecording(); // } else { // await logic.startRecording(); // } // } }, child: Container( width: 50.w, height: 50.w, padding: EdgeInsets.all(5.w), child: Image( width: 40.w, height: 40.w, fit: BoxFit.fill, image: const AssetImage( 'images/main/icon_lockDetail_monitoringScreenRecording.png'), ), ), ), SizedBox(width: 50.w), GestureDetector( onTap: () { logic.showToast('功能暂未开放'.tr); }, child: Image( width: 28.w, height: 28.w, fit: BoxFit.fill, image: const AssetImage('images/main/icon_lockDetail_rectangle.png'), ), ), ]); } Widget bottomBottomBtnWidget() { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // 接听 Obx( () => bottomBtnItemWidget( getAnswerBtnImg(), getAnswerBtnName(), Colors.white, longPress: () async { if (state.talkStatus.value == TalkStatus.answeredSuccessfully) { // 启动录音 logic.startProcessingAudio(); state.isLongPressing.value = true; } }, longPressUp: () async { // 停止录音 logic.stopProcessingAudio(); state.isLongPressing.value = false; }, onClick: () async { if (state.talkStatus.value == TalkStatus.passiveCallWaitingAnswer) { // 接听 logic.initiateAnswerCommand(); } }, ), ), bottomBtnItemWidget( 'images/main/icon_lockDetail_hangUp.png', '挂断'.tr, Colors.red, onClick: () { // 挂断 logic.udpHangUpAction(); }), bottomBtnItemWidget( 'images/main/icon_lockDetail_monitoringUnlock.png', '开锁'.tr, AppColors.mainColor, onClick: () { // if (UDPManage().remoteUnlock == 1) { logic.udpOpenDoorAction(); // showDeletPasswordAlertDialog(context); // } else { // logic.showToast('请在锁设置中开启远程开锁'.tr); // } }, ) ]); } String getAnswerBtnImg() { switch (state.talkStatus.value) { case TalkStatus.passiveCallWaitingAnswer: return 'images/main/icon_lockDetail_monitoringAnswerCalls.png'; case TalkStatus.answeredSuccessfully: case TalkStatus.proactivelyCallWaitingAnswer: return 'images/main/icon_lockDetail_monitoringUnTalkback.png'; default: return 'images/main/icon_lockDetail_monitoringAnswerCalls.png'; } } String getAnswerBtnName() { switch (state.talkStatus.value) { case TalkStatus.passiveCallWaitingAnswer: return '接听'.tr; case TalkStatus.proactivelyCallWaitingAnswer: case TalkStatus.answeredSuccessfully: return '长按说话'.tr; default: return '接听'.tr; } } Widget bottomBtnItemWidget( String iconUrl, String name, Color backgroundColor, { required Function() onClick, Function()? longPress, Function()? longPressUp, }) { double wh = 80.w; return GestureDetector( onTap: onClick, onLongPress: longPress, onLongPressUp: longPressUp, child: SizedBox( height: 140.h, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( width: wh, height: wh, decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular((wh + 10.w * 2) / 2), ), padding: EdgeInsets.all(20.w), child: Image.asset(iconUrl, fit: BoxFit.fitWidth), ), SizedBox(height: 20.w), Expanded( child: Text(name, style: TextStyle(fontSize: 20.sp, color: Colors.white), textAlign: TextAlign.center), ) ], ), ), ); } void showDeletPasswordAlertDialog(BuildContext context) { showDialog( barrierDismissible: false, context: context, builder: (BuildContext context) { return ShowTFView( title: '请输入6位数字开锁密码'.tr, tipTitle: '', controller: state.passwordTF, inputFormatters: [ LengthLimitingTextInputFormatter(6), //限制长度 FilteringTextInputFormatter.allow(RegExp('[0-9]')), ], sureClick: () async { //发送删除锁请求 // if (state.passwordTF.text.isEmpty) { // logic.showToast('请输入开锁密码'.tr); // return; // } // List numbers = state.passwordTF.text.split('').map((char) => int.parse(char)).toList(); // 开锁 // lockID // final List numbers = []; // final List lockIDData = utf8.encode(state.passwordTF.text); // numbers.addAll(lockIDData); // // topBytes = getFixedLengthList(lockIDData, 20 - lockIDData.length); // for (int i = 0; i < 6 - lockIDData.length; i++) { // numbers.add(0); // } logic.udpOpenDoorAction(); }, cancelClick: () { Get.back(); }, ); }, ); } //旋转动画 Widget buildRotationTransition() { return Positioned( left: ScreenUtil().screenWidth / 2 - 220.w / 2, top: ScreenUtil().screenHeight / 2 - 220.w / 2 - 150.h, child: GestureDetector( child: RotationTransition( //设置动画的旋转中心 alignment: Alignment.center, //动画控制器 turns: state.animationController, //将要执行动画的子view child: AnimatedOpacity( opacity: 0.5, duration: const Duration(seconds: 2), child: Image.asset( 'images/main/realTime_connecting.png', width: 220.w, height: 220.w, ), ), ), onTap: () { state.animationController.forward(); }, ), ); } @override void dispose() { state.animationController.dispose(); state.realTimePicTimer.cancel(); state.autoBackTimer.cancel(); state.videoBuffer.clear(); state.listData.value = Uint8List(0); CallTalk().finishAVData(); super.dispose(); } }