1.广角/主摄切换按钮已上移:
2.新增 isSwitchingLens 状态、避免切换镜头 stop 按钮禁用,不会触发 stopRecording 倍距按钮禁用,避免连续切换 ViewModel 层也兜底:isSwitchingLens=true 时直接拒绝 stop 3.合并失败兜底提示
This commit is contained in:
@@ -7,6 +7,7 @@ class RecordingSessionState {
|
|||||||
this.isTouchLocked = true,
|
this.isTouchLocked = true,
|
||||||
this.isPreviewReady = false,
|
this.isPreviewReady = false,
|
||||||
this.isStartingRecording = false,
|
this.isStartingRecording = false,
|
||||||
|
this.isSwitchingLens = false,
|
||||||
this.hasDndAccess = false,
|
this.hasDndAccess = false,
|
||||||
this.isBatteryOptimizedIgnored = true,
|
this.isBatteryOptimizedIgnored = true,
|
||||||
this.notificationsGranted = true,
|
this.notificationsGranted = true,
|
||||||
@@ -19,12 +20,14 @@ class RecordingSessionState {
|
|||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
this.permissionWarning,
|
this.permissionWarning,
|
||||||
this.fileSaveFailed = false,
|
this.fileSaveFailed = false,
|
||||||
|
this.segmentOutputPaths = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
final RecordingStatus status;
|
final RecordingStatus status;
|
||||||
final bool isTouchLocked;
|
final bool isTouchLocked;
|
||||||
final bool isPreviewReady;
|
final bool isPreviewReady;
|
||||||
final bool isStartingRecording;
|
final bool isStartingRecording;
|
||||||
|
final bool isSwitchingLens;
|
||||||
final bool hasDndAccess;
|
final bool hasDndAccess;
|
||||||
final bool isBatteryOptimizedIgnored;
|
final bool isBatteryOptimizedIgnored;
|
||||||
final bool notificationsGranted;
|
final bool notificationsGranted;
|
||||||
@@ -37,6 +40,7 @@ class RecordingSessionState {
|
|||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
final String? permissionWarning;
|
final String? permissionWarning;
|
||||||
final bool fileSaveFailed;
|
final bool fileSaveFailed;
|
||||||
|
final List<String> segmentOutputPaths;
|
||||||
|
|
||||||
bool get isRecording => status.isRecording;
|
bool get isRecording => status.isRecording;
|
||||||
|
|
||||||
@@ -53,6 +57,7 @@ class RecordingSessionState {
|
|||||||
bool? isTouchLocked,
|
bool? isTouchLocked,
|
||||||
bool? isPreviewReady,
|
bool? isPreviewReady,
|
||||||
bool? isStartingRecording,
|
bool? isStartingRecording,
|
||||||
|
bool? isSwitchingLens,
|
||||||
bool? hasDndAccess,
|
bool? hasDndAccess,
|
||||||
bool? isBatteryOptimizedIgnored,
|
bool? isBatteryOptimizedIgnored,
|
||||||
bool? notificationsGranted,
|
bool? notificationsGranted,
|
||||||
@@ -65,6 +70,7 @@ class RecordingSessionState {
|
|||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
String? permissionWarning,
|
String? permissionWarning,
|
||||||
bool? fileSaveFailed,
|
bool? fileSaveFailed,
|
||||||
|
List<String>? segmentOutputPaths,
|
||||||
bool clearPermissionWarning = false,
|
bool clearPermissionWarning = false,
|
||||||
bool clearLastSaved = false,
|
bool clearLastSaved = false,
|
||||||
}) {
|
}) {
|
||||||
@@ -73,6 +79,7 @@ class RecordingSessionState {
|
|||||||
isTouchLocked: isTouchLocked ?? this.isTouchLocked,
|
isTouchLocked: isTouchLocked ?? this.isTouchLocked,
|
||||||
isPreviewReady: isPreviewReady ?? this.isPreviewReady,
|
isPreviewReady: isPreviewReady ?? this.isPreviewReady,
|
||||||
isStartingRecording: isStartingRecording ?? this.isStartingRecording,
|
isStartingRecording: isStartingRecording ?? this.isStartingRecording,
|
||||||
|
isSwitchingLens: isSwitchingLens ?? this.isSwitchingLens,
|
||||||
hasDndAccess: hasDndAccess ?? this.hasDndAccess,
|
hasDndAccess: hasDndAccess ?? this.hasDndAccess,
|
||||||
isBatteryOptimizedIgnored:
|
isBatteryOptimizedIgnored:
|
||||||
isBatteryOptimizedIgnored ?? this.isBatteryOptimizedIgnored,
|
isBatteryOptimizedIgnored ?? this.isBatteryOptimizedIgnored,
|
||||||
@@ -90,6 +97,7 @@ class RecordingSessionState {
|
|||||||
? null
|
? null
|
||||||
: (permissionWarning ?? this.permissionWarning),
|
: (permissionWarning ?? this.permissionWarning),
|
||||||
fileSaveFailed: fileSaveFailed ?? this.fileSaveFailed,
|
fileSaveFailed: fileSaveFailed ?? this.fileSaveFailed,
|
||||||
|
segmentOutputPaths: segmentOutputPaths ?? this.segmentOutputPaths,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,7 +173,11 @@ class _RecordingPageState extends ConsumerState<RecordingPage> {
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final latest = ref.read(recordingViewModelProvider).session;
|
final latest = ref.read(recordingViewModelProvider).session;
|
||||||
if (latest.fileSaveFailed) {
|
if (latest.fileSaveFailed) {
|
||||||
|
if (latest.segmentOutputPaths.isNotEmpty) {
|
||||||
|
AppToast.show('视频合并失败,已为你保存分段文件,可在相册中查看');
|
||||||
|
} else {
|
||||||
AppToast.show(latest.errorMessage ?? '保存到文件夹失败,请检查文件保存权限');
|
AppToast.show(latest.errorMessage ?? '保存到文件夹失败,请检查文件保存权限');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await _showRecordingSavedDialogIfNeeded();
|
await _showRecordingSavedDialogIfNeeded();
|
||||||
@@ -374,6 +378,7 @@ class _RecordingHudLayer extends ConsumerWidget {
|
|||||||
m.session.notificationsGranted,
|
m.session.notificationsGranted,
|
||||||
m.session.isRecording,
|
m.session.isRecording,
|
||||||
m.session.isStartingRecording,
|
m.session.isStartingRecording,
|
||||||
|
m.session.isSwitchingLens,
|
||||||
m.session.isTouchLocked,
|
m.session.isTouchLocked,
|
||||||
m.session.zoomRatio,
|
m.session.zoomRatio,
|
||||||
m.session.minZoomRatio,
|
m.session.minZoomRatio,
|
||||||
@@ -391,6 +396,7 @@ class _RecordingHudLayer extends ConsumerWidget {
|
|||||||
notificationsGranted,
|
notificationsGranted,
|
||||||
isRecording,
|
isRecording,
|
||||||
isStartingRecording,
|
isStartingRecording,
|
||||||
|
isSwitchingLens,
|
||||||
isTouchLocked,
|
isTouchLocked,
|
||||||
zoomRatio,
|
zoomRatio,
|
||||||
minZoomRatio,
|
minZoomRatio,
|
||||||
@@ -408,6 +414,7 @@ class _RecordingHudLayer extends ConsumerWidget {
|
|||||||
notificationsGranted: notificationsGranted,
|
notificationsGranted: notificationsGranted,
|
||||||
isRecording: isRecording,
|
isRecording: isRecording,
|
||||||
isStartingRecording: isStartingRecording,
|
isStartingRecording: isStartingRecording,
|
||||||
|
isSwitchingLens: isSwitchingLens,
|
||||||
isTouchLocked: isTouchLocked,
|
isTouchLocked: isTouchLocked,
|
||||||
zoomRatio: zoomRatio,
|
zoomRatio: zoomRatio,
|
||||||
minZoomRatio: minZoomRatio,
|
minZoomRatio: minZoomRatio,
|
||||||
|
|||||||
@@ -349,10 +349,14 @@ class RecordingViewModel extends Notifier<RecordingModel> {
|
|||||||
/// 设置相机倍距,原生层会返回设备实际应用后的倍距范围与当前值。
|
/// 设置相机倍距,原生层会返回设备实际应用后的倍距范围与当前值。
|
||||||
Future<void> setZoomRatio(double ratio) async {
|
Future<void> setZoomRatio(double ratio) async {
|
||||||
final session = state.session;
|
final session = state.session;
|
||||||
|
if (session.isSwitchingLens) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final clamped = ratio
|
final clamped = ratio
|
||||||
.clamp(session.minZoomRatio, session.maxZoomRatio)
|
.clamp(session.minZoomRatio, session.maxZoomRatio)
|
||||||
.toDouble();
|
.toDouble();
|
||||||
|
|
||||||
|
_updateSession((s) => s.copyWith(isSwitchingLens: true));
|
||||||
try {
|
try {
|
||||||
final zoom = await RecordingPlatform.setZoomRatio(clamped);
|
final zoom = await RecordingPlatform.setZoomRatio(clamped);
|
||||||
_updateSession(
|
_updateSession(
|
||||||
@@ -368,13 +372,19 @@ class RecordingViewModel extends Notifier<RecordingModel> {
|
|||||||
? '切换镜头失败,请重试'
|
? '切换镜头失败,请重试'
|
||||||
: (error.message ?? '相机倍距设置失败');
|
: (error.message ?? '相机倍距设置失败');
|
||||||
_updateSession((s) => s.copyWith(errorMessage: message));
|
_updateSession((s) => s.copyWith(errorMessage: message));
|
||||||
|
} finally {
|
||||||
|
_updateSession(
|
||||||
|
(s) => s.copyWith(isSwitchingLens: false, errorMessage: s.errorMessage),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 开始录制,可选开启勿扰模式。
|
/// 开始录制,可选开启勿扰模式。
|
||||||
Future<void> startRecording({bool enableDoNotDisturb = true}) async {
|
Future<void> startRecording({bool enableDoNotDisturb = true}) async {
|
||||||
final session = state.session;
|
final session = state.session;
|
||||||
if (session.isRecording || session.isStartingRecording) {
|
if (session.isRecording ||
|
||||||
|
session.isStartingRecording ||
|
||||||
|
session.isSwitchingLens) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!session.isPreviewReady) {
|
if (!session.isPreviewReady) {
|
||||||
@@ -401,6 +411,7 @@ class RecordingViewModel extends Notifier<RecordingModel> {
|
|||||||
isTouchLocked: true,
|
isTouchLocked: true,
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
fileSaveFailed: false,
|
fileSaveFailed: false,
|
||||||
|
segmentOutputPaths: const [],
|
||||||
clearLastSaved: true,
|
clearLastSaved: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -415,7 +426,7 @@ class RecordingViewModel extends Notifier<RecordingModel> {
|
|||||||
|
|
||||||
/// 停止录制、保存到文件夹,并恢复相机预览。
|
/// 停止录制、保存到文件夹,并恢复相机预览。
|
||||||
Future<void> stopRecording() async {
|
Future<void> stopRecording() async {
|
||||||
if (!state.session.isRecording) return;
|
if (!state.session.isRecording || state.session.isSwitchingLens) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = await RecordingPlatform.stopRecording();
|
final result = await RecordingPlatform.stopRecording();
|
||||||
@@ -432,6 +443,7 @@ class RecordingViewModel extends Notifier<RecordingModel> {
|
|||||||
? (result.fileErrorMessage ?? '保存到文件夹失败,请检查文件保存权限')
|
? (result.fileErrorMessage ?? '保存到文件夹失败,请检查文件保存权限')
|
||||||
: null,
|
: null,
|
||||||
fileSaveFailed: fileFailed,
|
fileSaveFailed: fileFailed,
|
||||||
|
segmentOutputPaths: result.segmentOutputPaths,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} on PlatformException catch (error) {
|
} on PlatformException catch (error) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class RecordingHudWidget extends StatelessWidget {
|
|||||||
required this.notificationsGranted,
|
required this.notificationsGranted,
|
||||||
required this.isRecording,
|
required this.isRecording,
|
||||||
required this.isStartingRecording,
|
required this.isStartingRecording,
|
||||||
|
required this.isSwitchingLens,
|
||||||
required this.isTouchLocked,
|
required this.isTouchLocked,
|
||||||
this.showClipboardHint = false,
|
this.showClipboardHint = false,
|
||||||
this.clipboardAddress = '',
|
this.clipboardAddress = '',
|
||||||
@@ -39,6 +40,7 @@ class RecordingHudWidget extends StatelessWidget {
|
|||||||
final bool notificationsGranted;
|
final bool notificationsGranted;
|
||||||
final bool isRecording;
|
final bool isRecording;
|
||||||
final bool isStartingRecording;
|
final bool isStartingRecording;
|
||||||
|
final bool isSwitchingLens;
|
||||||
final bool isTouchLocked;
|
final bool isTouchLocked;
|
||||||
final bool showClipboardHint;
|
final bool showClipboardHint;
|
||||||
final String clipboardAddress;
|
final String clipboardAddress;
|
||||||
@@ -143,8 +145,9 @@ class RecordingHudWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
right: 16.r,
|
right: 16.r,
|
||||||
bottom: _recordButtonBottom + _recordButtonSize + 14.h,
|
bottom: 260.r,
|
||||||
child: _ZoomPresetControl(
|
child: _ZoomPresetControl(
|
||||||
|
enabled: !isSwitchingLens,
|
||||||
zoomRatio: zoomRatio,
|
zoomRatio: zoomRatio,
|
||||||
minZoomRatio: minZoomRatio,
|
minZoomRatio: minZoomRatio,
|
||||||
maxZoomRatio: maxZoomRatio,
|
maxZoomRatio: maxZoomRatio,
|
||||||
@@ -160,7 +163,7 @@ class RecordingHudWidget extends StatelessWidget {
|
|||||||
child: RecordingControlButton(
|
child: RecordingControlButton(
|
||||||
isRecording: isRecording,
|
isRecording: isRecording,
|
||||||
isStartingRecording: isStartingRecording,
|
isStartingRecording: isStartingRecording,
|
||||||
enabled: !isStartingRecording,
|
enabled: !isStartingRecording && !isSwitchingLens,
|
||||||
size: _recordButtonSize,
|
size: _recordButtonSize,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
@@ -197,6 +200,7 @@ class RecordingHudWidget extends StatelessWidget {
|
|||||||
|
|
||||||
class _ZoomPresetControl extends StatelessWidget {
|
class _ZoomPresetControl extends StatelessWidget {
|
||||||
const _ZoomPresetControl({
|
const _ZoomPresetControl({
|
||||||
|
required this.enabled,
|
||||||
required this.zoomRatio,
|
required this.zoomRatio,
|
||||||
required this.minZoomRatio,
|
required this.minZoomRatio,
|
||||||
required this.maxZoomRatio,
|
required this.maxZoomRatio,
|
||||||
@@ -204,6 +208,7 @@ class _ZoomPresetControl extends StatelessWidget {
|
|||||||
required this.onSelected,
|
required this.onSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final bool enabled;
|
||||||
final double zoomRatio;
|
final double zoomRatio;
|
||||||
final double minZoomRatio;
|
final double minZoomRatio;
|
||||||
final double maxZoomRatio;
|
final double maxZoomRatio;
|
||||||
@@ -235,7 +240,7 @@ class _ZoomPresetControl extends StatelessWidget {
|
|||||||
displayRatio: preset,
|
displayRatio: preset,
|
||||||
requestRatio: preset,
|
requestRatio: preset,
|
||||||
selected: _isPresetSelected(preset),
|
selected: _isPresetSelected(preset),
|
||||||
enabled: true,
|
enabled: enabled,
|
||||||
onSelected: onSelected,
|
onSelected: onSelected,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:permission_handler/permission_handler.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/model/model_recording_session.dart';
|
||||||
import 'package:recording_tool/features/recording/platform/recording_channel_names.dart';
|
import 'package:recording_tool/features/recording/platform/recording_channel_names.dart';
|
||||||
|
import 'package:recording_tool/features/recording/platform/recording_platform.dart';
|
||||||
import 'package:recording_tool/features/recording/view-model/view_model_recording.dart';
|
import 'package:recording_tool/features/recording/view-model/view_model_recording.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -255,6 +258,125 @@ void main() {
|
|||||||
expect(session.errorMessage, '切换镜头失败,请重试');
|
expect(session.errorMessage, '切换镜头失败,请重试');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test('sets switching lens while native zoom request is pending', () async {
|
||||||
|
final completer = Completer<Map<String, dynamic>>();
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(
|
||||||
|
const MethodChannel(RecordingChannelNames.method),
|
||||||
|
(call) => completer.future,
|
||||||
|
);
|
||||||
|
final container = ProviderContainer();
|
||||||
|
addTearDown(container.dispose);
|
||||||
|
final notifier = container.read(recordingViewModelProvider.notifier);
|
||||||
|
|
||||||
|
final future = notifier.setZoomRatio(2);
|
||||||
|
await Future<void>.delayed(Duration.zero);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.read(recordingViewModelProvider).session.isSwitchingLens,
|
||||||
|
isTrue,
|
||||||
|
);
|
||||||
|
|
||||||
|
completer.complete(<String, dynamic>{
|
||||||
|
'zoomRatio': 2.0,
|
||||||
|
'minZoomRatio': 1.0,
|
||||||
|
'maxZoomRatio': 3.0,
|
||||||
|
});
|
||||||
|
await future;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.read(recordingViewModelProvider).session.isSwitchingLens,
|
||||||
|
isFalse,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('RecordingViewModel.stopRecording', () {
|
||||||
|
test('does not call native stop while switching lens', () async {
|
||||||
|
final calls = <MethodCall>[];
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(
|
||||||
|
const MethodChannel(RecordingChannelNames.method),
|
||||||
|
(call) async {
|
||||||
|
calls.add(call);
|
||||||
|
return <String, dynamic>{};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final container = ProviderContainer();
|
||||||
|
addTearDown(container.dispose);
|
||||||
|
final notifier = container.read(recordingViewModelProvider.notifier);
|
||||||
|
// ignore: invalid_use_of_protected_member
|
||||||
|
notifier.state = container
|
||||||
|
.read(recordingViewModelProvider)
|
||||||
|
.copyWith(
|
||||||
|
session: const RecordingSessionState(
|
||||||
|
status: RecordingStatus(state: RecordingState.recording),
|
||||||
|
isSwitchingLens: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await notifier.stopRecording();
|
||||||
|
|
||||||
|
expect(calls, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'stores segment output paths when native save falls back to parts',
|
||||||
|
() async {
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(
|
||||||
|
const MethodChannel(RecordingChannelNames.method),
|
||||||
|
(call) async {
|
||||||
|
switch (call.method) {
|
||||||
|
case 'stopRecording':
|
||||||
|
return <String, dynamic>{
|
||||||
|
'outputPath': 'content://recordings/part1',
|
||||||
|
'status': <String, dynamic>{
|
||||||
|
'state': 'error',
|
||||||
|
'message': 'Merge failed',
|
||||||
|
},
|
||||||
|
'fileSaved': false,
|
||||||
|
'fileErrorMessage': 'Merge failed',
|
||||||
|
'segmentOutputPaths': <String>[
|
||||||
|
'content://recordings/part1',
|
||||||
|
'content://recordings/part2',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
case 'initializePreview':
|
||||||
|
return <String, dynamic>{'state': 'previewing'};
|
||||||
|
case 'getZoomCapabilities':
|
||||||
|
return <String, dynamic>{
|
||||||
|
'zoomRatio': 1.0,
|
||||||
|
'minZoomRatio': 1.0,
|
||||||
|
'maxZoomRatio': 3.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return <String, dynamic>{};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final container = ProviderContainer();
|
||||||
|
addTearDown(container.dispose);
|
||||||
|
final notifier = container.read(recordingViewModelProvider.notifier);
|
||||||
|
// ignore: invalid_use_of_protected_member
|
||||||
|
notifier.state = container
|
||||||
|
.read(recordingViewModelProvider)
|
||||||
|
.copyWith(
|
||||||
|
session: const RecordingSessionState(
|
||||||
|
status: RecordingStatus(state: RecordingState.recording),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await notifier.stopRecording();
|
||||||
|
|
||||||
|
final session = container.read(recordingViewModelProvider).session;
|
||||||
|
expect(session.fileSaveFailed, isTrue);
|
||||||
|
expect(session.segmentOutputPaths, <String>[
|
||||||
|
'content://recordings/part1',
|
||||||
|
'content://recordings/part2',
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
group('recordingFileSavePermissionsForHost', () {
|
group('recordingFileSavePermissionsForHost', () {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ void main() {
|
|||||||
double minZoomRatio = 1.0,
|
double minZoomRatio = 1.0,
|
||||||
double maxZoomRatio = 3.0,
|
double maxZoomRatio = 3.0,
|
||||||
bool isRecording = false,
|
bool isRecording = false,
|
||||||
|
bool isSwitchingLens = false,
|
||||||
|
Future<void> Function()? onStop,
|
||||||
ValueChanged<double>? onZoomSelected,
|
ValueChanged<double>? onZoomSelected,
|
||||||
}) async {
|
}) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -25,12 +27,13 @@ void main() {
|
|||||||
notificationsGranted: true,
|
notificationsGranted: true,
|
||||||
isRecording: isRecording,
|
isRecording: isRecording,
|
||||||
isStartingRecording: false,
|
isStartingRecording: false,
|
||||||
|
isSwitchingLens: isSwitchingLens,
|
||||||
isTouchLocked: false,
|
isTouchLocked: false,
|
||||||
zoomRatio: zoomRatio,
|
zoomRatio: zoomRatio,
|
||||||
minZoomRatio: minZoomRatio,
|
minZoomRatio: minZoomRatio,
|
||||||
maxZoomRatio: maxZoomRatio,
|
maxZoomRatio: maxZoomRatio,
|
||||||
onStart: () async {},
|
onStart: () async {},
|
||||||
onStop: () async {},
|
onStop: onStop ?? () async {},
|
||||||
onOpenDnd: () {},
|
onOpenDnd: () {},
|
||||||
onOpenBattery: () {},
|
onOpenBattery: () {},
|
||||||
onToggleTouchLock: () {},
|
onToggleTouchLock: () {},
|
||||||
@@ -112,7 +115,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await tester.tap(find.text('0.5x'));
|
await tester.tap(find.text('0.5x'));
|
||||||
await tester.pump();
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
|
||||||
expect(selected, 0.5);
|
expect(selected, 0.5);
|
||||||
});
|
});
|
||||||
@@ -128,7 +131,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await tester.tap(find.text('0.6x'));
|
await tester.tap(find.text('0.6x'));
|
||||||
await tester.pump();
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
|
||||||
expect(selected, 0.6);
|
expect(selected, 0.6);
|
||||||
});
|
});
|
||||||
@@ -155,7 +158,7 @@ void main() {
|
|||||||
expect(mainButton.enabled, isFalse);
|
expect(mainButton.enabled, isFalse);
|
||||||
|
|
||||||
await tester.tap(find.text('0.6x'));
|
await tester.tap(find.text('0.6x'));
|
||||||
await tester.pump();
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
|
||||||
expect(selected, 0.6);
|
expect(selected, 0.6);
|
||||||
});
|
});
|
||||||
@@ -183,8 +186,45 @@ void main() {
|
|||||||
expect(mainButton.enabled, isTrue);
|
expect(mainButton.enabled, isTrue);
|
||||||
|
|
||||||
await tester.tap(find.text('1x'));
|
await tester.tap(find.text('1x'));
|
||||||
await tester.pump();
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
|
||||||
expect(selected, 1.0);
|
expect(selected, 1.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('disables stop button while switching lens', (tester) async {
|
||||||
|
var stopped = false;
|
||||||
|
await pumpHud(
|
||||||
|
tester,
|
||||||
|
minZoomRatio: 0.6,
|
||||||
|
isRecording: true,
|
||||||
|
isSwitchingLens: true,
|
||||||
|
onStop: () async => stopped = true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byType(GestureDetector).last);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(stopped, isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('disables zoom buttons while switching lens', (tester) async {
|
||||||
|
double? selected;
|
||||||
|
await pumpHud(
|
||||||
|
tester,
|
||||||
|
minZoomRatio: 0.6,
|
||||||
|
isRecording: true,
|
||||||
|
isSwitchingLens: true,
|
||||||
|
onZoomSelected: (ratio) => selected = ratio,
|
||||||
|
);
|
||||||
|
|
||||||
|
final ultraWideButton = tester.widget<TextButton>(
|
||||||
|
find.ancestor(of: find.text('0.6x'), matching: find.byType(TextButton)),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(ultraWideButton.enabled, isFalse);
|
||||||
|
await tester.tap(find.text('0.6x'));
|
||||||
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
|
||||||
|
expect(selected, isNull);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user