From c0aa2db6db0326dbe41d4e8cd7c816eb126f5eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E9=94=8B?= <2535831261@qq.com> Date: Fri, 5 Jun 2026 14:09:25 +0800 Subject: [PATCH] =?UTF-8?q?1.=E8=BF=98=E5=8E=9F=20UI=20=E7=A8=BF=EF=BC=8C?= =?UTF-8?q?=E5=9C=B0=E5=9D=80=E5=92=8C=E6=97=B6=E9=97=B4=E4=B8=8E=E5=BD=95?= =?UTF-8?q?=E5=88=B6=E6=8C=89=E9=92=AE=E6=8E=92=E7=89=88=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widget_clipboard_address_clock_chip.dart | 38 ++-- .../widgets/widget_recording_hud.dart | 205 +++++++++--------- .../widgets/widget_recording_setup_hints.dart | 20 +- 3 files changed, 132 insertions(+), 131 deletions(-) diff --git a/lib/features/recording/widgets/widget_clipboard_address_clock_chip.dart b/lib/features/recording/widgets/widget_clipboard_address_clock_chip.dart index 21b3190..e994ff2 100644 --- a/lib/features/recording/widgets/widget_clipboard_address_clock_chip.dart +++ b/lib/features/recording/widgets/widget_clipboard_address_clock_chip.dart @@ -1,17 +1,16 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:recording_tool/core/utils/date_time_formatter.dart'; -import 'package:recording_tool/features/recording/widgets/widget_recording_hint_chip.dart'; -/// 显示剪贴板地址与实时时钟的提示芯片 +/// 左下角实时时钟与剪贴板地址 class ClipboardAddressClockChipWidget extends StatefulWidget { const ClipboardAddressClockChipWidget({super.key, required this.address}); final String address; @override - /// 创建芯片状态 State createState() => _ClipboardAddressClockChipWidgetState(); } @@ -20,8 +19,14 @@ class _ClipboardAddressClockChipWidgetState extends State { Timer? _clockTimer; + static TextStyle get _textStyle => TextStyle( + color: Colors.white, + fontSize: 12.sp, + height: 1.4, + shadows: [Shadow(color: Colors.black54, blurRadius: 6.r)], + ); + @override - /// 启动每秒刷新时钟 void initState() { super.initState(); _clockTimer = Timer.periodic(const Duration(seconds: 1), (_) { @@ -30,26 +35,27 @@ class _ClipboardAddressClockChipWidgetState } @override - /// 取消定时器 void dispose() { _clockTimer?.cancel(); _clockTimer = null; super.dispose(); } - /// 拼接地址与当前时间文本 - String _buildLabel() { - final nowText = DateTimeFormatter.format( - DateTime.now(), - pattern: 'yyyy-M-d-H:mm:ss', - ); - if (widget.address.isEmpty) return nowText; - return '${widget.address}\n$nowText'; - } + String get _nowText => DateTimeFormatter.format( + DateTime.now(), + pattern: 'yyyy-M-d-H:mm:ss', + ); @override - /// 渲染时钟芯片 Widget build(BuildContext context) { - return RecordingHintChipWidget(label: _buildLabel(), onTap: () {}); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(_nowText, style: _textStyle), + if (widget.address.isNotEmpty) + Text(widget.address, style: _textStyle), + ], + ); } } diff --git a/lib/features/recording/widgets/widget_recording_hud.dart b/lib/features/recording/widgets/widget_recording_hud.dart index da3bef6..36064f5 100644 --- a/lib/features/recording/widgets/widget_recording_hud.dart +++ b/lib/features/recording/widgets/widget_recording_hud.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:recording_tool/features/recording/model/model_recording_session.dart'; +import 'package:recording_tool/features/recording/widgets/widget_clipboard_address_clock_chip.dart'; import 'package:recording_tool/features/recording/widgets/widget_recording_setup_hints.dart'; /// 录制页 HUD 层(状态提示、录制控制) @@ -27,112 +28,120 @@ class RecordingHudWidget extends StatelessWidget { final VoidCallback onOpenBattery; final VoidCallback onToggleTouchLock; - /// 底部控制区左右占位宽度 - static double get _controlSlotWidth => 48.r; + static double get _recordButtonSize => 70.r; + static double get _recordButtonBottom => 63.r; + static double get _overlayInfoLeft => 13.r; + static double get _overlayInfoBottom => 10.r; @override - /// 构建 HUD 布局 Widget build(BuildContext context) { - return SafeArea( - child: Column( - children: [ - SizedBox(height: 8.h), - const Spacer(), - if (state.errorMessage != null) - Padding( - padding: EdgeInsets.all(12.r), - child: Text( - state.errorMessage!, - style: const TextStyle(color: Colors.amber), - textAlign: TextAlign.center, - ), - ), - if (state.permissionWarning != null) - Padding( - padding: EdgeInsets.symmetric( - horizontal: 16.r, - vertical: 8.r, - ), - child: Text( - state.permissionWarning!, - style: TextStyle( - color: Colors.orangeAccent, - fontSize: 12.sp, - ), - textAlign: TextAlign.center, - ), - ), - RecordingSetupHintsWidget( - hasDndAccess: state.hasDndAccess, - isBatteryIgnored: state.isBatteryOptimizedIgnored, - notificationsGranted: state.notificationsGranted, - showClipboardHint: showClipboardHint, - clipboardAddress: clipboardAddress, - onOpenDnd: onOpenDnd, - onOpenBattery: onOpenBattery, - onOpenNotificationSettings: openAppSettings, - ), - Padding( - padding: EdgeInsets.fromLTRB(24.r, 8.r, 24.r, 24.r), - child: Row( - children: [ - SizedBox( - width: _controlSlotWidth, - height: _controlSlotWidth, - child: state.isRecording - ? IconButton( - onPressed: onToggleTouchLock, - icon: Icon( - state.isTouchLocked ? Icons.lock : Icons.lock_open, - color: Colors.white, - size: 28.r, - ), - ) - : null, - ), - Expanded( - child: Center( - child: GestureDetector( - onTap: state.isStartingRecording - ? null - : () async { - if (state.isRecording) { - await onStop(); - } else { - await onStart(); - } - }, - child: Container( - width: 76.w, - height: 76.h, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: Colors.white, - width: 4.r, - ), - color: state.isRecording ? Colors.white : Colors.red, - ), - child: Icon( - state.isRecording - ? Icons.stop - : Icons.fiber_manual_record, - color: state.isRecording ? Colors.red : Colors.white, - size: 36.r, - ), - ), - ), + return Stack( + fit: StackFit.expand, + children: [ + Positioned( + left: 0, + right: 0, + top: 0, + bottom: _recordButtonBottom + _recordButtonSize + 16.h, + child: Column( + children: [ + SizedBox(height: 8.h), + const Spacer(), + if (state.errorMessage != null) + Padding( + padding: EdgeInsets.all(12.r), + child: Text( + state.errorMessage!, + style: const TextStyle(color: Colors.amber), + textAlign: TextAlign.center, ), ), - SizedBox( - width: _controlSlotWidth, - height: _controlSlotWidth, + if (state.permissionWarning != null) + Padding( + padding: EdgeInsets.symmetric( + horizontal: 16.r, + vertical: 8.r, + ), + child: Text( + state.permissionWarning!, + style: TextStyle( + color: Colors.orangeAccent, + fontSize: 12.sp, + ), + textAlign: TextAlign.center, + ), ), - ], + RecordingSetupHintsWidget( + hasDndAccess: state.hasDndAccess, + isBatteryIgnored: state.isBatteryOptimizedIgnored, + notificationsGranted: state.notificationsGranted, + onOpenDnd: onOpenDnd, + onOpenBattery: onOpenBattery, + onOpenNotificationSettings: openAppSettings, + ), + ], + ), + ), + if (showClipboardHint) + Positioned( + left: _overlayInfoLeft, + bottom: _overlayInfoBottom, + child: ClipboardAddressClockChipWidget(address: clipboardAddress), + ), + if (state.isRecording) + Positioned( + left: 16.r, + bottom: _recordButtonBottom, + child: SizedBox( + height: _recordButtonSize, + child: Center( + child: IconButton( + onPressed: onToggleTouchLock, + icon: Icon( + state.isTouchLocked ? Icons.lock : Icons.lock_open, + color: Colors.white, + size: 28.r, + ), + ), + ), ), ), - ], - ), + Positioned( + left: 0, + right: 0, + bottom: _recordButtonBottom, + child: Center( + child: GestureDetector( + onTap: state.isStartingRecording + ? null + : () async { + if (state.isRecording) { + await onStop(); + } else { + await onStart(); + } + }, + child: Container( + width: _recordButtonSize, + height: _recordButtonSize, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.white, + width: 4.r, + ), + color: state.isRecording ? Colors.white : Colors.red, + ), + child: Icon( + state.isRecording ? Icons.stop : Icons.fiber_manual_record, + color: state.isRecording ? Colors.red : Colors.white, + size: 32.r, + ), + ), + ), + ), + ), + ], ); } } diff --git a/lib/features/recording/widgets/widget_recording_setup_hints.dart b/lib/features/recording/widgets/widget_recording_setup_hints.dart index 837db4a..7782e6d 100644 --- a/lib/features/recording/widgets/widget_recording_setup_hints.dart +++ b/lib/features/recording/widgets/widget_recording_setup_hints.dart @@ -1,17 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:recording_tool/features/recording/widgets/widget_clipboard_address_clock_chip.dart'; import 'package:recording_tool/features/recording/widgets/widget_recording_hint_chip.dart'; -/// 权限与剪贴板相关设置提示条 +/// 权限相关设置提示条 class RecordingSetupHintsWidget extends StatelessWidget { const RecordingSetupHintsWidget({ super.key, required this.hasDndAccess, required this.isBatteryIgnored, required this.notificationsGranted, - this.showClipboardHint = false, - this.clipboardAddress = '', required this.onOpenDnd, required this.onOpenBattery, required this.onOpenNotificationSettings, @@ -20,19 +17,15 @@ class RecordingSetupHintsWidget extends StatelessWidget { final bool hasDndAccess; final bool isBatteryIgnored; final bool notificationsGranted; - final bool showClipboardHint; - final String clipboardAddress; final VoidCallback onOpenDnd; final VoidCallback onOpenBattery; final VoidCallback onOpenNotificationSettings; @override - /// 按需展示权限/剪贴板提示 Widget build(BuildContext context) { final showPermissionHints = !hasDndAccess || !isBatteryIgnored || !notificationsGranted; - final showClipboardHint = this.showClipboardHint; - if (!showPermissionHints && !showClipboardHint) { + if (!showPermissionHints) { return const SizedBox.shrink(); } @@ -48,10 +41,7 @@ class RecordingSetupHintsWidget extends StatelessWidget { SizedBox(height: 8.h), ], if (!hasDndAccess) - RecordingHintChipWidget( - label: '开启勿扰权限可减少录制中断', - onTap: onOpenDnd, - ), + RecordingHintChipWidget(label: '开启勿扰权限可减少录制中断', onTap: onOpenDnd), if (!isBatteryIgnored) ...[ SizedBox(height: 8.h), RecordingHintChipWidget( @@ -59,10 +49,6 @@ class RecordingSetupHintsWidget extends StatelessWidget { onTap: onOpenBattery, ), ], - if (showClipboardHint) ...[ - SizedBox(height: 8.h), - ClipboardAddressClockChipWidget(address: clipboardAddress), - ], ], ), );