屏幕适配

This commit is contained in:
2026-06-04 14:34:46 +08:00
parent 02c1c87b46
commit 5ddcb95358
17 changed files with 286 additions and 126 deletions

View File

@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:recording_tool/features/recording/recording_platform.dart';
@@ -108,6 +109,15 @@ class _RecordingPageState extends ConsumerState<RecordingPage> {
state: state,
eventTitle: showClipboardInfo ? clipboard.title : null,
eventAddress: showClipboardInfo ? clipboard.address : null,
onPasteEventInfo: () async {
final result = await ref
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
if (!context.mounted) return;
if (result != ClipboardReadResult.success) {
AppToast.show('无赛事信息');
}
},
onStart: () => controller.startRecording(),
onStop: () async {
await controller.stopRecording();
@@ -141,6 +151,7 @@ class _RecordingHud extends StatelessWidget {
required this.state,
this.eventTitle,
this.eventAddress,
required this.onPasteEventInfo,
required this.onStart,
required this.onStop,
required this.onOpenDnd,
@@ -151,31 +162,41 @@ class _RecordingHud extends StatelessWidget {
final RecordingSessionState state;
final String? eventTitle;
final String? eventAddress;
final Future<void> Function() onPasteEventInfo;
final VoidCallback onStart;
final VoidCallback onStop;
final VoidCallback onOpenDnd;
final VoidCallback onOpenBattery;
final VoidCallback onToggleTouchLock;
static const _overlayTextStyle = TextStyle(
static TextStyle get _overlayTextStyle => TextStyle(
color: Colors.white,
shadows: [Shadow(color: Colors.black54, blurRadius: 6)],
shadows: [Shadow(color: Colors.black54, blurRadius: 6.r)],
);
static double get _controlSlotWidth => 48.r;
@override
Widget build(BuildContext context) {
final showPasteEventInfo = eventTitle == null && !state.isRecording;
return SafeArea(
child: Stack(
children: [
Column(
children: [
SizedBox(
height: eventTitle != null || state.isRecording ? 56 : 8,
height:
eventTitle != null ||
state.isRecording ||
showPasteEventInfo
? 56.h
: 8.h,
),
const Spacer(),
if (state.errorMessage != null)
Padding(
padding: const EdgeInsets.all(12),
padding: EdgeInsets.all(12.r),
child: Text(
state.errorMessage!,
style: const TextStyle(color: Colors.amber),
@@ -184,15 +205,15 @@ class _RecordingHud extends StatelessWidget {
),
if (state.permissionWarning != null)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
padding: EdgeInsets.symmetric(
horizontal: 16.r,
vertical: 8.r,
),
child: Text(
state.permissionWarning!,
style: const TextStyle(
style: TextStyle(
color: Colors.orangeAccent,
fontSize: 12,
fontSize: 12.sp,
),
textAlign: TextAlign.center,
),
@@ -206,39 +227,56 @@ class _RecordingHud extends StatelessWidget {
onOpenNotificationSettings: openAppSettings,
),
Padding(
padding: const EdgeInsets.fromLTRB(24, 8, 24, 24),
padding: EdgeInsets.fromLTRB(24.r, 8.r, 24.r, 24.r),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if (state.isRecording)
IconButton(
onPressed: onToggleTouchLock,
icon: Icon(
state.isTouchLocked ? Icons.lock : Icons.lock_open,
color: Colors.white,
size: 28,
),
),
GestureDetector(
onTap: state.isRecording ? onStop : onStart,
child: Container(
width: 76,
height: 76,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 4),
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,
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.isRecording ? onStop : 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,
),
),
),
),
),
const SizedBox(width: 48),
SizedBox(
width: _controlSlotWidth,
height: _controlSlotWidth,
),
],
),
),
@@ -246,26 +284,53 @@ class _RecordingHud extends StatelessWidget {
!state.isRecording &&
!state.gallerySaveFailed)
Padding(
padding: const EdgeInsets.only(bottom: 16),
padding: EdgeInsets.only(bottom: 16.r),
child: Text(
'已保存到相册:${state.lastSavedDisplayName}',
style: const TextStyle(color: Colors.white70, fontSize: 12),
style: TextStyle(color: Colors.white70, fontSize: 12.sp),
textAlign: TextAlign.center,
),
),
],
),
if (showPasteEventInfo)
Positioned(
top: 8.r,
left: 12.w,
right: 12.w,
child: Center(
child: TextButton.icon(
onPressed: onPasteEventInfo,
icon: Icon(Icons.content_paste, size: 18.r),
label: const Text('粘贴赛事信息'),
style: TextButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.black.withValues(alpha: 0.5),
padding: EdgeInsets.symmetric(
horizontal: 14.r,
vertical: 8.r,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.r),
side: const BorderSide(color: Colors.white30),
),
),
),
),
),
if (eventTitle != null)
Positioned(
top: 8,
left: 12,
right: 12,
top: 8.r,
left: 12.w,
right: 12.w,
child: Padding(
padding: EdgeInsets.only(right: state.isRecording ? 96 : 0),
padding: EdgeInsets.only(
right: state.isRecording ? 96.w : 0,
),
child: Text(
eventTitle!,
style: _overlayTextStyle.copyWith(
fontSize: 16,
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center,
@@ -276,16 +341,16 @@ class _RecordingHud extends StatelessWidget {
),
if (state.isRecording)
Positioned(
top: 8,
right: 12,
top: 8.r,
right: 12.w,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
padding: EdgeInsets.symmetric(
horizontal: 12.r,
vertical: 6.r,
),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
'REC ${state.elapsedLabel}',
@@ -298,13 +363,13 @@ class _RecordingHud extends StatelessWidget {
),
if (eventAddress != null && eventAddress!.isNotEmpty)
Positioned(
left: 16,
bottom: 108,
right: 120,
left: 16.w,
bottom: 108.r,
right: 120.w,
child: Text(
eventAddress!,
style: _overlayTextStyle.copyWith(
fontSize: 13,
fontSize: 13.sp,
color: Colors.white70,
),
maxLines: 2,
@@ -341,7 +406,7 @@ class _SetupHints extends StatelessWidget {
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: EdgeInsets.symmetric(horizontal: 16.r, vertical: 8.r),
child: Column(
children: [
if (!notificationsGranted) ...[
@@ -349,12 +414,12 @@ class _SetupHints extends StatelessWidget {
label: '开启通知权限以显示录制前台服务',
onTap: onOpenNotificationSettings,
),
const SizedBox(height: 8),
SizedBox(height: 8.h),
],
if (!hasDndAccess)
_HintChip(label: '开启勿扰权限可减少录制中断', onTap: onOpenDnd),
if (!isBatteryIgnored) ...[
const SizedBox(height: 8),
SizedBox(height: 8.h),
_HintChip(label: '关闭电池优化可提升息屏续录稳定性', onTap: onOpenBattery),
],
],
@@ -376,19 +441,19 @@ class _HintChip extends StatelessWidget {
child: DecoratedBox(
decoration: BoxDecoration(
color: Colors.white12,
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(8.r),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
padding: EdgeInsets.symmetric(horizontal: 12.r, vertical: 8.r),
child: Row(
children: [
Expanded(
child: Text(
label,
style: const TextStyle(color: Colors.white70, fontSize: 12),
style: TextStyle(color: Colors.white70, fontSize: 12.sp),
),
),
const Icon(Icons.chevron_right, color: Colors.white54, size: 18),
Icon(Icons.chevron_right, color: Colors.white54, size: 18.r),
],
),
),

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class RecordingTouchLockOverlay extends StatefulWidget {
const RecordingTouchLockOverlay({
@@ -72,22 +73,22 @@ class _RecordingTouchLockOverlayState extends State<RecordingTouchLockOverlay> {
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.only(top: 48),
padding: EdgeInsets.only(top: 48.r),
child: DecoratedBox(
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(24),
borderRadius: BorderRadius.circular(24.r),
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
padding: EdgeInsets.symmetric(
horizontal: 16.r,
vertical: 8.r,
),
child: Text(
_isHolding
? '保持按住 ${widget.unlockHoldDuration.inSeconds}s 解锁…'
: '防误触已开启,按住 ${widget.unlockHoldDuration.inSeconds}s 解锁',
style: const TextStyle(color: Colors.white, fontSize: 13),
style: TextStyle(color: Colors.white, fontSize: 13.sp),
),
),
),