删除未使用的相机和记录类来重构记录功能;更新pubspec中的依赖项。Yaml为本地插件路径;简化MainActivity和相关类,以提高性能和可维护性。
This commit is contained in:
@@ -1,34 +1,22 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:apivideo_live_stream/apivideo_live_stream.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class CameraPreviewWidget extends StatelessWidget {
|
||||
const CameraPreviewWidget({super.key});
|
||||
const CameraPreviewWidget({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.isReady,
|
||||
});
|
||||
|
||||
final ApiVideoLiveStreamController controller;
|
||||
final bool isReady;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Platform.isAndroid) {
|
||||
return AndroidView(
|
||||
viewType: 'recording-camera-preview',
|
||||
layoutDirection: TextDirection.ltr,
|
||||
creationParams: const <String, dynamic>{},
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
);
|
||||
if (!isReady) {
|
||||
return const ColoredBox(color: Colors.black);
|
||||
}
|
||||
|
||||
if (Platform.isIOS) {
|
||||
return UiKitView(
|
||||
viewType: 'recording-camera-preview',
|
||||
layoutDirection: TextDirection.ltr,
|
||||
creationParams: const <String, dynamic>{},
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
);
|
||||
}
|
||||
|
||||
return const ColoredBox(
|
||||
color: Colors.black,
|
||||
child: Center(child: Text('当前平台不支持相机预览')),
|
||||
);
|
||||
return ApiVideoCameraPreview(controller: controller, fit: BoxFit.cover);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:recording_tool/features/recording/widgets/record_content_transition.dart';
|
||||
import 'package:recording_tool/gen/assets.gen.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_toast.dart';
|
||||
|
||||
/// 录制页顶部:Logo、粘贴赛事、赛事标题
|
||||
/// 录制页顶部:Logo、赛事标题
|
||||
class RecordHeaderWidget extends StatelessWidget {
|
||||
const RecordHeaderWidget({
|
||||
super.key,
|
||||
required this.hasValidClipboardInfo,
|
||||
this.eventTitle,
|
||||
required this.isRecording,
|
||||
required this.onPasteEventInfo,
|
||||
required this.onClearEventInfo,
|
||||
});
|
||||
|
||||
final bool hasValidClipboardInfo;
|
||||
final String? eventTitle;
|
||||
final bool isRecording;
|
||||
final Future<void> Function() onPasteEventInfo;
|
||||
final VoidCallback onClearEventInfo;
|
||||
|
||||
bool get _showPasteButtons => !hasValidClipboardInfo && !isRecording;
|
||||
|
||||
bool get _showEventTitle => hasValidClipboardInfo;
|
||||
bool get _showEventTitle => eventTitle?.trim().isNotEmpty == true;
|
||||
|
||||
Widget _buildAnimatedHeaderContent() {
|
||||
if (_showEventTitle) {
|
||||
return _HeaderEventTitleRow(
|
||||
key: ValueKey('title-${eventTitle ?? ''}'),
|
||||
title: eventTitle ?? '',
|
||||
isRecording: isRecording,
|
||||
onClearEventInfo: onClearEventInfo,
|
||||
);
|
||||
}
|
||||
|
||||
return const SizedBox.shrink(key: ValueKey('header-empty'));
|
||||
}
|
||||
|
||||
void _mockCopyEventInfo() {
|
||||
const strTemp =
|
||||
'{"title":"蔡依婷vs夏志豪 空中格斗赛 初中组","address":"黑龙江省鹤岗市11111","filename":"蔡依婷_夏志豪_测试循环赛-7_空中格斗赛"}';
|
||||
Clipboard.setData(const ClipboardData(text: strTemp));
|
||||
AppToast.show('模拟复制赛事信息成功');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
@@ -75,14 +56,6 @@ class RecordHeaderWidget extends StatelessWidget {
|
||||
transitionBuilder: RecordContentTransition.builder,
|
||||
child: _buildAnimatedHeaderContent(),
|
||||
),
|
||||
if (_showPasteButtons)
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: _HeaderPasteActions(
|
||||
onMockCopy: _mockCopyEventInfo,
|
||||
onPasteEventInfo: onPasteEventInfo,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -95,16 +68,9 @@ class RecordHeaderWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _HeaderEventTitleRow extends StatelessWidget {
|
||||
const _HeaderEventTitleRow({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.isRecording,
|
||||
required this.onClearEventInfo,
|
||||
});
|
||||
const _HeaderEventTitleRow({super.key, required this.title});
|
||||
|
||||
final String title;
|
||||
final bool isRecording;
|
||||
final VoidCallback onClearEventInfo;
|
||||
|
||||
static TextStyle get _overlayTextStyle => TextStyle(
|
||||
color: Colors.white,
|
||||
@@ -135,87 +101,7 @@ class _HeaderEventTitleRow extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
!isRecording
|
||||
? IconButton(
|
||||
key: const ValueKey('clear-event-info'),
|
||||
onPressed: onClearEventInfo,
|
||||
icon: Assets.images.imageDelete.image(
|
||||
width: 15.r,
|
||||
height: 15.r,
|
||||
fit: BoxFit.contain,
|
||||
excludeFromSemantics: true,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: BoxConstraints(minWidth: 40.r, minHeight: 40.r),
|
||||
alignment: Alignment.centerRight,
|
||||
tooltip: '删除',
|
||||
)
|
||||
: const SizedBox.shrink(key: ValueKey('clear-event-info-hidden')),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HeaderPasteActions extends StatelessWidget {
|
||||
const _HeaderPasteActions({
|
||||
required this.onMockCopy,
|
||||
required this.onPasteEventInfo,
|
||||
});
|
||||
|
||||
final VoidCallback onMockCopy;
|
||||
final Future<void> Function() onPasteEventInfo;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
// _HeaderActionButton(label: 'mock', onPressed: onMockCopy),
|
||||
_HeaderActionButton(
|
||||
label: '粘贴选手信息',
|
||||
onPressed: () => onPasteEventInfo(),
|
||||
icon: Assets.images.imageCopy.image(
|
||||
width: 10.r,
|
||||
height: 10.r,
|
||||
fit: BoxFit.contain,
|
||||
excludeFromSemantics: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HeaderActionButton extends StatelessWidget {
|
||||
const _HeaderActionButton({
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.icon,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final VoidCallback onPressed;
|
||||
final Widget? icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton.icon(
|
||||
onPressed: onPressed,
|
||||
icon: icon ?? Icon(Icons.content_paste, size: 10.r),
|
||||
label: Text(label),
|
||||
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: Size.zero, // 取消 40dp 最小高度
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap, // 取消额外点击热区
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Colors.black.withValues(alpha: 0.5),
|
||||
textStyle: TextStyle(fontSize: 10.sp),
|
||||
padding: EdgeInsets.symmetric(horizontal: 7.r, vertical: 4.r),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(25.r),
|
||||
side: const BorderSide(color: Colors.white30),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -5,19 +5,19 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:recording_tool/core/utils/date_time_formatter.dart';
|
||||
import 'package:recording_tool/features/recording/widgets/record_content_transition.dart';
|
||||
|
||||
/// 左下角实时时钟与剪贴板地址
|
||||
class ClipboardAddressClockChipWidget extends StatefulWidget {
|
||||
const ClipboardAddressClockChipWidget({super.key, required this.address});
|
||||
/// 左下角实时时钟与赛事场地信息。
|
||||
class RecordingContextClockChipWidget extends StatefulWidget {
|
||||
const RecordingContextClockChipWidget({super.key, required this.address});
|
||||
|
||||
final String address;
|
||||
|
||||
@override
|
||||
State<ClipboardAddressClockChipWidget> createState() =>
|
||||
_ClipboardAddressClockChipWidgetState();
|
||||
State<RecordingContextClockChipWidget> createState() =>
|
||||
_RecordingContextClockChipWidgetState();
|
||||
}
|
||||
|
||||
class _ClipboardAddressClockChipWidgetState
|
||||
extends State<ClipboardAddressClockChipWidget> {
|
||||
class _RecordingContextClockChipWidgetState
|
||||
extends State<RecordingContextClockChipWidget> {
|
||||
Timer? _clockTimer;
|
||||
|
||||
static TextStyle get _textStyle => TextStyle(
|
||||
@@ -42,10 +42,8 @@ class _ClipboardAddressClockChipWidgetState
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
String get _nowText => DateTimeFormatter.format(
|
||||
DateTime.now(),
|
||||
pattern: 'yyyy-M-d-H:mm:ss',
|
||||
);
|
||||
String get _nowText =>
|
||||
DateTimeFormatter.format(DateTime.now(), pattern: 'yyyy-M-d-H:mm:ss');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -71,7 +69,9 @@ class _ClipboardAddressClockChipWidgetState
|
||||
key: ValueKey(widget.address),
|
||||
style: _textStyle,
|
||||
)
|
||||
: const SizedBox.shrink(key: ValueKey('clipboard-address-empty')),
|
||||
: const SizedBox.shrink(
|
||||
key: ValueKey('recording-address-empty'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -3,8 +3,8 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:recording_tool/core/utils/rate_limiter.dart';
|
||||
import 'package:recording_tool/features/recording/widgets/record_content_transition.dart';
|
||||
import 'package:recording_tool/features/recording/widgets/widget_clipboard_address_clock_chip.dart';
|
||||
import 'package:recording_tool/features/recording/widgets/widget_recording_button.dart';
|
||||
import 'package:recording_tool/features/recording/widgets/widget_recording_context_clock_chip.dart';
|
||||
import 'package:recording_tool/features/recording/widgets/widget_recording_setup_hints.dart';
|
||||
|
||||
/// 录制页 HUD 层(状态提示、录制控制)
|
||||
@@ -20,8 +20,8 @@ class RecordingHudWidget extends StatelessWidget {
|
||||
required this.isStartingRecording,
|
||||
required this.isSwitchingLens,
|
||||
required this.isTouchLocked,
|
||||
this.showClipboardHint = false,
|
||||
this.clipboardAddress = '',
|
||||
this.showContextHint = false,
|
||||
this.contextAddress = '',
|
||||
required this.zoomRatio,
|
||||
required this.minZoomRatio,
|
||||
required this.maxZoomRatio,
|
||||
@@ -42,8 +42,8 @@ class RecordingHudWidget extends StatelessWidget {
|
||||
final bool isStartingRecording;
|
||||
final bool isSwitchingLens;
|
||||
final bool isTouchLocked;
|
||||
final bool showClipboardHint;
|
||||
final String clipboardAddress;
|
||||
final bool showContextHint;
|
||||
final String contextAddress;
|
||||
final double zoomRatio;
|
||||
final double minZoomRatio;
|
||||
final double maxZoomRatio;
|
||||
@@ -117,12 +117,14 @@ class RecordingHudWidget extends StatelessWidget {
|
||||
switchOutCurve: Curves.easeInCubic,
|
||||
layoutBuilder: RecordContentTransition.bottomStackLayoutBuilder,
|
||||
transitionBuilder: RecordContentTransition.builder,
|
||||
child: showClipboardHint
|
||||
? ClipboardAddressClockChipWidget(
|
||||
key: const ValueKey('clipboard-info'),
|
||||
address: clipboardAddress,
|
||||
child: showContextHint
|
||||
? RecordingContextClockChipWidget(
|
||||
key: const ValueKey('recording-context-info'),
|
||||
address: contextAddress,
|
||||
)
|
||||
: const SizedBox.shrink(key: ValueKey('clipboard-info-hidden')),
|
||||
: const SizedBox.shrink(
|
||||
key: ValueKey('recording-context-info-hidden'),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isRecording)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:recording_tool/features/recording/dialog/dialog-record.dart';
|
||||
|
||||
/// 录制结束并保存到文件夹后的后续操作弹窗。
|
||||
/// 推流录制结束后的后续操作弹窗。
|
||||
Future<void> showRecordingSavedDialog(
|
||||
BuildContext context, {
|
||||
required String sessionTitle,
|
||||
@@ -10,7 +10,7 @@ Future<void> showRecordingSavedDialog(
|
||||
}) {
|
||||
return RecordDialog.showDouble(
|
||||
context,
|
||||
title: '本轮比赛视频已保存到文件夹\n请选择后续录制信息',
|
||||
title: '本轮比赛视频已提交 NAS 录制\n请选择后续录制信息',
|
||||
leftText: '继续本轮',
|
||||
rightText: '录制新轮',
|
||||
onLeftPressed: onContinueRound,
|
||||
|
||||
Reference in New Issue
Block a user