import 'dart:async'; import 'package:eitc_erm_dental_flutter/funcs.dart'; import 'package:eitc_erm_dental_flutter/sp_util.dart'; import 'package:eitc_erm_dental_flutter/widget/count_down_text.dart'; import 'package:eitc_erm_dental_flutter/widget/operation_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; ///视频操作视图 class VideoOperationView extends StatefulWidget { final VideoOperationViewController controller; final void Function() onTakePhoto; final void Function() onStartRecord; final void Function() onStopRecord; final void Function() onStopVideo; final String area; const VideoOperationView( {super.key, required this.controller, required this.onTakePhoto, required this.onStartRecord, required this.onStopRecord, required this.onStopVideo, required this.area}); @override State createState() => _VideoOperationViewState(); } class _VideoOperationViewState extends State { late final CountDownTextController _countController = CountDownTextController(_onCount); ///是否正在倒计时 bool _isCountingDown = false; ///录像时间 int _recordingTime = 0; Timer? _recordingTimer; @override void initState() { super.initState(); widget.controller._setCountController(_countController); widget.controller.recordingState.addListener(_onUpdateRecordingState); widget.controller.isCountingDown.addListener(_onUpdateIsCountingDown); } ///更新录像状态 void _onUpdateRecordingState() { if (!mounted) { return; } setState(() { if (widget.controller.isRecording) { _recordingTimer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { _recordingTime = timer.tick; }); }); } else { _recordingTimer?.cancel(); _recordingTime = 0; } }); } ///更新倒计时状态 void _onUpdateIsCountingDown() { setState(() { _isCountingDown = widget.controller.isCounting; }); } @override void dispose() { super.dispose(); _recordingTimer?.cancel(); _countController.dispose(); widget.controller.recordingState.removeListener(_onUpdateRecordingState); widget.controller.isCountingDown.removeListener(_onUpdateIsCountingDown); } @override Widget build(BuildContext context) { return Stack( children: [ _getButtons(), _getCountHold(), ], ); } ///获取倒计时,保持等 Widget _getCountHold() { TextStyle? style = Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.white); return Align( alignment: Alignment.topCenter, child: Padding( padding: EdgeInsets.only(top: 10.h), child: Column( children: [ SizedBox( height: 30.h, ), //静止不动提示 Visibility( visible: _isCountingDown, child: Column( children: [ Text(getS().pleaseHold, style: style), SizedBox( height: 10.h, ), //倒计时 CountDownText( controller: _countController, textStyle: style, ), SizedBox( height: 10.h, ), ], )), //录像计时 Visibility( visible: widget.controller.isRecording, child: Text( _getRecordingTimeStr(), style: style, )), Visibility( visible: widget.area.isNotEmpty && !widget.controller.isRecording, child: Text( textAlign: TextAlign.center, getS().takePhotoAreaHint(toothAreaTranslate(widget.area)), style: style), ) ], ), ), ); } ///获取录像时间文本 String _getRecordingTimeStr() { Duration duration = Duration(seconds: _recordingTime); return "${"${duration.inMinutes}".padLeft(2, "0")}:${"${duration.inSeconds % 60}".padLeft(2, "0")}"; } ///获取按钮 Widget _getButtons() { return OrientationBuilder(builder: (ctx, orientation) { Alignment alignment = orientation == Orientation.landscape ? Alignment.centerRight : Alignment.bottomCenter; //拍照按钮 Widget buttonCamera = OperationButton( text: getS().takePhoto, onTap: _takePhoto, child: const Icon( Icons.camera_alt_outlined, color: Colors.white, )); //录像按钮 Widget buttonRecord = OperationButton( text: getS().record, onTap: () { if (widget.controller.isRecording) { _stopRecord(); } else { _startRecord(); } }, child: Container( width: 17.5.r, height: 17.5.r, decoration: BoxDecoration( color: widget.controller.isRecording ? Colors.red : Colors.white, shape: BoxShape.circle), )); //根据横竖屏显示在不同位置 return Align( alignment: alignment, child: Container( padding: orientation == Orientation.landscape ? EdgeInsets.only(right: 20.w) : EdgeInsets.only(bottom: 20.h), child: orientation == Orientation.landscape ? Column( mainAxisSize: MainAxisSize.min, children: [ buttonCamera, SizedBox( height: 40.h, ), buttonRecord, ], ) : Row( mainAxisSize: MainAxisSize.min, children: [ buttonRecord, SizedBox( width: 40.w, ), buttonCamera, ], ), ), ); }); } ///拍照 void _takePhoto() async { if (!await requestStoreagePermission()) { showToast(text: getS().storagePermissionRejectHint); return; } bool isDelay = await SpUtil.getEnableDelayShot(); int delay = await SpUtil.getDelayShotTime(); _countController.startCount(isDelay ? delay : 0); if (isDelay) { widget.controller.isCounting = true; } } ///开始录像 void _startRecord() async { if (!await requestStoreagePermission()) { showToast(text: getS().storagePermissionRejectHint); return; } widget.onStartRecord(); } ///停止录像 void _stopRecord() { widget.onStopRecord(); } ///关闭视频 void _stopVideo() { widget.onStopVideo(); widget.controller.isCounting = false; } ///拍照倒计时 void _onCount(int tick, bool complete) { if (!complete) { return; } widget.onTakePhoto(); widget.controller.isCounting = false; } } class VideoOperationViewController { CountDownTextController? _countController; ValueNotifier? _recordingState; ValueNotifier get recordingState => _recordingState ??= ValueNotifier(false); ValueNotifier? _isCountingDown; ValueNotifier get isCountingDown => _isCountingDown ??= ValueNotifier(false); void _setCountController(CountDownTextController controller) { _countController = controller; } void dispose() { _countController?.stopCount(); _recordingState?.dispose(); _recordingState = null; _isCountingDown?.dispose(); _isCountingDown = null; } void updateRecordingState(bool isRecording) { if (recordingState.value == isRecording) { return; } recordingState.value = isRecording; } bool get isRecording => recordingState.value; bool get isCounting => isCountingDown.value; set isCounting(bool bo) => isCountingDown.value = bo; }