重构录制页面,优化 UI 组件,简化状态管理,移除不必要的参数,提升代码可读性和维护性。
This commit is contained in:
@@ -239,32 +239,13 @@ class _RecordingPageState extends ConsumerState<RecordingPage> {
|
||||
@override
|
||||
/// 构建录制页 UI
|
||||
Widget build(BuildContext context) {
|
||||
final recordingInfo = ref.watch(recordingViewModelProvider);
|
||||
final state = recordingInfo.session;
|
||||
final viewModel = ref.read(recordingViewModelProvider.notifier);
|
||||
final clipboard = recordingInfo.clipboardRecordingModel;
|
||||
final showClipboardInfo = recordingInfo.hasValidClipboardInfo;
|
||||
|
||||
return PopScope(
|
||||
canPop: !state.isRecording,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
if (didPop) {
|
||||
await _exitRecordingMode();
|
||||
return;
|
||||
}
|
||||
if (state.isRecording) {
|
||||
AppToast.show('录制中无法返回,请先停止录制');
|
||||
}
|
||||
},
|
||||
return _RecordingPopScope(
|
||||
onExitRecordingMode: _exitRecordingMode,
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: Column(
|
||||
children: [
|
||||
RecordHeaderWidget(
|
||||
hasValidClipboardInfo: showClipboardInfo,
|
||||
eventTitle: showClipboardInfo ? clipboard.title : null,
|
||||
isRecording: state.isRecording,
|
||||
elapsedLabel: state.elapsedLabel,
|
||||
_RecordHeaderSection(
|
||||
onPasteEventInfo: _pasteEventInfo,
|
||||
onClearEventInfo: _clearClipboardForNewRound,
|
||||
),
|
||||
@@ -272,43 +253,16 @@ class _RecordingPageState extends ConsumerState<RecordingPage> {
|
||||
child: Stack(
|
||||
children: [
|
||||
const CameraPreviewWidget(),
|
||||
if (!state.isPreviewReady && state.errorMessage == null)
|
||||
const RecordingLoadingOverlayWidget(message: '正在启动相机…'),
|
||||
const _PreviewLoadingLayer(),
|
||||
const RecordTimerWidget(),
|
||||
RecordingHudWidget(
|
||||
state: state,
|
||||
showClipboardHint: showClipboardInfo,
|
||||
clipboardAddress: clipboard.address.trim(),
|
||||
_RecordingHudLayer(
|
||||
onStart: _onStartRecording,
|
||||
onStop: _stopRecordingAndShowResult,
|
||||
onOpenDnd: () async {
|
||||
await viewModel.openDndSettings();
|
||||
await viewModel.refreshDndAccess();
|
||||
},
|
||||
onOpenBattery: () async {
|
||||
await viewModel.openBatterySettings();
|
||||
await viewModel.refreshBatteryOptimization();
|
||||
},
|
||||
onToggleTouchLock: () {
|
||||
viewModel.setTouchLocked(!state.isTouchLocked);
|
||||
},
|
||||
),
|
||||
if (state.isTouchLocked && state.isRecording)
|
||||
RecordingTouchLockOverlayWidget(
|
||||
enabled: true,
|
||||
onUnlocked: (intent) async {
|
||||
viewModel.setTouchLocked(false);
|
||||
if (intent ==
|
||||
RecordingTouchLockUnlockIntent.stopRecording) {
|
||||
await _stopRecordingAndShowResult();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (state.isStartingRecording)
|
||||
RecordingLoadingOverlayWidget(
|
||||
message: '正在开始录制…',
|
||||
backgroundColor: Colors.black.withValues(alpha: 0.24),
|
||||
),
|
||||
_TouchLockOverlayLayer(
|
||||
onStopRecording: _stopRecordingAndShowResult,
|
||||
),
|
||||
const _StartingRecordingOverlay(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -319,3 +273,207 @@ class _RecordingPageState extends ConsumerState<RecordingPage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RecordingPopScope extends ConsumerWidget {
|
||||
const _RecordingPopScope({
|
||||
required this.onExitRecordingMode,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final Future<void> Function() onExitRecordingMode;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isRecording = ref.watch(
|
||||
recordingViewModelProvider.select((m) => m.session.isRecording),
|
||||
);
|
||||
|
||||
return PopScope(
|
||||
canPop: !isRecording,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
if (didPop) {
|
||||
await onExitRecordingMode();
|
||||
return;
|
||||
}
|
||||
if (isRecording) {
|
||||
AppToast.show('录制中无法返回,请先停止录制');
|
||||
}
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RecordHeaderSection extends ConsumerWidget {
|
||||
const _RecordHeaderSection({
|
||||
required this.onPasteEventInfo,
|
||||
required this.onClearEventInfo,
|
||||
});
|
||||
|
||||
final Future<void> Function() onPasteEventInfo;
|
||||
final VoidCallback onClearEventInfo;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final headerState = ref.watch(
|
||||
recordingViewModelProvider.select(
|
||||
(m) => (
|
||||
m.hasValidClipboardInfo,
|
||||
m.hasValidClipboardInfo ? m.clipboardRecordingModel.title : null,
|
||||
m.session.isRecording,
|
||||
),
|
||||
),
|
||||
);
|
||||
final (hasValidClipboardInfo, eventTitle, isRecording) = headerState;
|
||||
|
||||
return RecordHeaderWidget(
|
||||
hasValidClipboardInfo: hasValidClipboardInfo,
|
||||
eventTitle: eventTitle,
|
||||
isRecording: isRecording,
|
||||
onPasteEventInfo: onPasteEventInfo,
|
||||
onClearEventInfo: onClearEventInfo,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PreviewLoadingLayer extends ConsumerWidget {
|
||||
const _PreviewLoadingLayer();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final showLoading = ref.watch(
|
||||
recordingViewModelProvider.select(
|
||||
(m) => !m.session.isPreviewReady && m.session.errorMessage == null,
|
||||
),
|
||||
);
|
||||
|
||||
if (!showLoading) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return const RecordingLoadingOverlayWidget(message: '正在启动相机…');
|
||||
}
|
||||
}
|
||||
|
||||
class _RecordingHudLayer extends ConsumerWidget {
|
||||
const _RecordingHudLayer({
|
||||
required this.onStart,
|
||||
required this.onStop,
|
||||
});
|
||||
|
||||
final Future<void> Function() onStart;
|
||||
final Future<void> Function() onStop;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final hudState = ref.watch(
|
||||
recordingViewModelProvider.select(
|
||||
(m) => (
|
||||
m.session.errorMessage,
|
||||
m.session.permissionWarning,
|
||||
m.session.hasDndAccess,
|
||||
m.session.isBatteryOptimizedIgnored,
|
||||
m.session.notificationsGranted,
|
||||
m.session.isRecording,
|
||||
m.session.isStartingRecording,
|
||||
m.session.isTouchLocked,
|
||||
m.hasValidClipboardInfo,
|
||||
m.clipboardRecordingModel.address.trim(),
|
||||
),
|
||||
),
|
||||
);
|
||||
final (
|
||||
errorMessage,
|
||||
permissionWarning,
|
||||
hasDndAccess,
|
||||
isBatteryOptimizedIgnored,
|
||||
notificationsGranted,
|
||||
isRecording,
|
||||
isStartingRecording,
|
||||
isTouchLocked,
|
||||
showClipboardHint,
|
||||
clipboardAddress,
|
||||
) = hudState;
|
||||
final viewModel = ref.read(recordingViewModelProvider.notifier);
|
||||
|
||||
return RecordingHudWidget(
|
||||
errorMessage: errorMessage,
|
||||
permissionWarning: permissionWarning,
|
||||
hasDndAccess: hasDndAccess,
|
||||
isBatteryOptimizedIgnored: isBatteryOptimizedIgnored,
|
||||
notificationsGranted: notificationsGranted,
|
||||
isRecording: isRecording,
|
||||
isStartingRecording: isStartingRecording,
|
||||
isTouchLocked: isTouchLocked,
|
||||
showClipboardHint: showClipboardHint,
|
||||
clipboardAddress: clipboardAddress,
|
||||
onStart: onStart,
|
||||
onStop: onStop,
|
||||
onOpenDnd: () async {
|
||||
await viewModel.openDndSettings();
|
||||
await viewModel.refreshDndAccess();
|
||||
},
|
||||
onOpenBattery: () async {
|
||||
await viewModel.openBatterySettings();
|
||||
await viewModel.refreshBatteryOptimization();
|
||||
},
|
||||
onToggleTouchLock: () {
|
||||
final locked = ref.read(recordingViewModelProvider).session.isTouchLocked;
|
||||
viewModel.setTouchLocked(!locked);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TouchLockOverlayLayer extends ConsumerWidget {
|
||||
const _TouchLockOverlayLayer({required this.onStopRecording});
|
||||
|
||||
final Future<void> Function() onStopRecording;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final overlayState = ref.watch(
|
||||
recordingViewModelProvider.select(
|
||||
(m) => (m.session.isTouchLocked, m.session.isRecording),
|
||||
),
|
||||
);
|
||||
final (isTouchLocked, isRecording) = overlayState;
|
||||
|
||||
if (!isTouchLocked || !isRecording) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final viewModel = ref.read(recordingViewModelProvider.notifier);
|
||||
|
||||
return RecordingTouchLockOverlayWidget(
|
||||
enabled: true,
|
||||
onUnlocked: (intent) async {
|
||||
viewModel.setTouchLocked(false);
|
||||
if (intent == RecordingTouchLockUnlockIntent.stopRecording) {
|
||||
await onStopRecording();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StartingRecordingOverlay extends ConsumerWidget {
|
||||
const _StartingRecordingOverlay();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isStartingRecording = ref.watch(
|
||||
recordingViewModelProvider.select((m) => m.session.isStartingRecording),
|
||||
);
|
||||
|
||||
if (!isStartingRecording) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return RecordingLoadingOverlayWidget(
|
||||
message: '正在开始录制…',
|
||||
backgroundColor: Colors.black.withValues(alpha: 0.24),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user