Files
flutterApp/lib/features/remote_control/presentation/pages/remote_control_page.dart
2026-01-19 18:24:24 +08:00

207 lines
7.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart';
import '../../../../core/app/app_user_cubit.dart';
import '../../../devices/presentation/bloc/devices_cubit.dart';
import '../bloc/remote_control_cubit.dart';
import '../bloc/remote_control_state.dart';
import '../widgets/center_control_area.dart';
import '../widgets/emergency_overlay.dart';
import '../widgets/left_joystick_area.dart';
import '../widgets/my_video_player.dart';
import '../widgets/right_joystick_area.dart';
import '../widgets/top_status_bar.dart';
class RemoteControlPage extends StatefulWidget {
const RemoteControlPage({super.key});
@override
State<RemoteControlPage> createState() => _RemoteControlPageState();
}
class _RemoteControlPageState extends State<RemoteControlPage> {
@override
void initState() {
super.initState();
// 1. 锁定横屏
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
// 2. 隐藏状态栏和虚拟按键
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
}
@override
void dispose() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
super.dispose();
}
@override
Widget build(BuildContext context) {
// 监听全局状态(这些通常不随摇杆频繁变化)
final userState = context.watch<AppUserCubit>().state;
final deviceState = context.watch<DevicesCubit>().state;
final currentDevice = deviceState.selectedDevice;
if (currentDevice == null) {
return _buildOfflineScaffold();
}
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: [
// 【优化层 1】底层视频:传入固定参数,加上 RepaintBoundary
Positioned.fill(
child: MyVideoPlayer(
deviceIp: TCPConsts.TCP_IP,
device: currentDevice,
user: userState.user!,
),
),
// 【优化层 2】中层:急停效果(只在 isEmergency 变化时才触发显示/隐藏)
BlocBuilder<RemoteControlCubit, RemoteControlState>(
buildWhen: (p, c) => p.isEmergency != c.isEmergency,
builder: (context, state) {
return state.isEmergency ? const Positioned.fill(child: EmergencyOverlay()) : const SizedBox.shrink();
},
),
// 【优化层 3】顶层:UI 控制层
SafeArea(
child: Column(
children: [
const TopStatusBar(),
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
final double sideAreaWidth = constraints.maxWidth * 0.25;
final double centerAreaWidth = constraints.maxWidth * 0.5;
// 关键:使用 BlocBuilder 局部刷新摇杆,不要 watch 整个 Page
return BlocBuilder<RemoteControlCubit, RemoteControlState>(
// 只有 isLocked 改变时才重构摇杆区域,摇杆坐标改变由内部处理
buildWhen: (p, c) => p.isLocked != c.isLocked,
builder: (context, remoteState) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// 左摇杆
SizedBox(
width: sideAreaWidth,
child: Align(
alignment: Alignment.bottomCenter,
child: LeftJoystickArea(
isLocked: remoteState.isLocked,
width: sideAreaWidth * 0.5 * 0.7,
),
),
),
// 中间区
SizedBox(
width: centerAreaWidth,
child: Align(
alignment: Alignment.bottomCenter,
child: CenterControlArea(totalWidth: centerAreaWidth),
),
),
// 右摇杆
SizedBox(
width: sideAreaWidth,
child: Align(
alignment: Alignment.bottomCenter,
child: RightJoystickArea(
isLocked: remoteState.isLocked,
width: sideAreaWidth * 0.5 * 0.7,
),
),
),
],
),
);
},
);
},
),
),
],
),
),
// 权限弹窗
BlocBuilder<RemoteControlCubit, RemoteControlState>(
buildWhen: (p, c) => p.showPermissionRequestDialog != c.showPermissionRequestDialog,
builder: (context, state) {
if (state.showPermissionRequestDialog) {
return _buildPermissionDialog(context, state);
}
return const SizedBox.shrink();
},
),
],
),
);
}
Widget _buildOfflineScaffold() {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.signal_wifi_off, color: Colors.white, size: 60),
const SizedBox(height: 20),
const Text(
"设备已断开连接",
style: TextStyle(color: Colors.white, fontSize: 18),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () => context.pop(),
child: const Text("返回"),
),
],
),
),
);
}
Widget _buildPermissionDialog(
BuildContext context,
RemoteControlState state,
) {
return Container(
color: Colors.black54,
child: AlertDialog(
title: const Text("权限变更"),
content: Text("${state.permissionPlatform}端正请求控制权,同意释放吗?"),
actions: [
TextButton(
onPressed: () =>
context.read<RemoteControlCubit>().respondPermission(false),
child: const Text("拒绝"),
),
TextButton(
onPressed: () =>
context.read<RemoteControlCubit>().respondPermission(true),
child: const Text("同意"),
),
],
),
);
}
}