style: 改为ios 风格

This commit is contained in:
2026-07-18 12:38:52 +08:00
parent 918ab47234
commit 9e5ac91e46

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'config/ice_servers.dart';
@@ -16,12 +17,9 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
return CupertinoApp(
title: 'WebRTC 控制端',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
theme: const CupertinoThemeData(primaryColor: CupertinoColors.activeBlue),
home: const ControllerHome(),
);
}
@@ -35,17 +33,15 @@ class ControllerHome extends StatefulWidget {
}
class _ControllerHomeState extends State<ControllerHome> {
final _serverUrlController =
TextEditingController(text: kDefaultSignalServer);
final _serverUrlController = TextEditingController(
text: kDefaultSignalServer,
);
final _deviceIdController = TextEditingController();
final _targetController = TextEditingController(text: '981964879');
RemoteController? _controller;
RTCVideoRenderer? _renderer;
/// 用于在任意位置(含回调中)弹出提示。
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
bool _connected = false;
String _status = '状态: 已停止';
String _stats = '';
@@ -76,6 +72,23 @@ class _ControllerHomeState extends State<ControllerHome> {
void _setStatus(String status) => setState(() => _status = status);
/// 以 iOS 风格弹窗提示用户(替代原 Material 的 SnackBar
void _showAlert(String message) {
if (!mounted) return;
showCupertinoDialog<void>(
context: context,
builder: (ctx) => CupertinoAlertDialog(
content: Text(message),
actions: [
CupertinoDialogAction(
child: const Text('确定'),
onPressed: () => Navigator.of(ctx).pop(),
),
],
),
);
}
Future<void> _connect() async {
final serverUrl = _serverUrlController.text.trim();
final deviceId = _deviceIdController.text.trim();
@@ -132,43 +145,19 @@ class _ControllerHomeState extends State<ControllerHome> {
/// ICE 断开:提示用户并自动返回连接设置面板。
Future<void> _onIceDisconnected(String message) async {
final scaffoldCtx = _scaffoldKey.currentState?.context;
if (mounted && scaffoldCtx != null) {
ScaffoldMessenger.of(scaffoldCtx).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(seconds: 3),
),
);
}
_showAlert(message);
await _disconnect();
}
/// 目标被控端不在线:提示用户并复位到连接设置面板。
Future<void> _onTargetOffline(String message) async {
final scaffoldCtx = _scaffoldKey.currentState?.context;
if (mounted && scaffoldCtx != null) {
ScaffoldMessenger.of(scaffoldCtx).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(seconds: 3),
),
);
}
_showAlert(message);
setState(() => _connected = false);
}
/// 被控端拒绝连接请求:提示用户并复位到连接设置面板。
Future<void> _onConnectionRejected(String message) async {
final scaffoldCtx = _scaffoldKey.currentState?.context;
if (mounted && scaffoldCtx != null) {
ScaffoldMessenger.of(scaffoldCtx).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(seconds: 3),
),
);
}
_showAlert(message);
setState(() => _connected = false);
}
@@ -186,15 +175,21 @@ class _ControllerHomeState extends State<ControllerHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(title: const Text('WebRTC 控制端')),
body: _connected ? _buildControlPanel() : _buildSetupPanel(),
return CupertinoPageScaffold(
// 控制模式(已连接)下隐藏导航栏,避免其半透明浮层遮挡顶部的
// 状态/统计信息浮层;连接设置面板再显示标题栏。
navigationBar: _connected
? null
: const CupertinoNavigationBar(
middle: Text('WebRTC 控制端'),
),
child: _connected ? _buildControlPanel() : _buildSetupPanel(),
);
}
Widget _buildSetupPanel() {
return SingleChildScrollView(
return SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -205,23 +200,27 @@ class _ControllerHomeState extends State<ControllerHome> {
),
const SizedBox(height: 24),
const Text('信令服务器地址:', style: TextStyle(fontSize: 14)),
TextField(
const SizedBox(height: 8),
CupertinoTextField(
controller: _serverUrlController,
decoration: const InputDecoration(
hintText: 'ws://175.178.213.60:8088/ws/signal',
),
placeholder: 'ws://175.178.213.60:8088/ws/signal',
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
),
const SizedBox(height: 16),
const Text('本机设备ID:', style: TextStyle(fontSize: 14)),
TextField(
const SizedBox(height: 8),
CupertinoTextField(
controller: _deviceIdController,
decoration: const InputDecoration(hintText: '设备ID'),
placeholder: '设备ID',
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
),
const SizedBox(height: 16),
const Text('目标被控设备ID:', style: TextStyle(fontSize: 14)),
TextField(
const SizedBox(height: 8),
CupertinoTextField(
controller: _targetController,
decoration: const InputDecoration(hintText: '被控端设备ID'),
placeholder: '被控端设备ID',
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
),
const SizedBox(height: 24),
Text(
@@ -231,18 +230,26 @@ class _ControllerHomeState extends State<ControllerHome> {
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
child: CupertinoButton.filled(
onPressed: _connect,
child: const Text('连接被控设备'),
),
),
],
),
),
);
}
Widget _buildControlPanel() {
return Stack(
return SafeArea(
// 仅保留顶部安全区(避开系统状态栏),底部/左右保持全屏,
// 以便右下角的断开按钮贴近屏幕边缘。
top: true,
bottom: false,
left: false,
right: false,
child: Stack(
children: [
Container(color: Colors.black),
if (_renderer != null)
@@ -263,10 +270,12 @@ class _ControllerHomeState extends State<ControllerHome> {
onTouch: (x, y) {},
onSwipe: (x1, y1, x2, y2, d) {},
onLongPress: (x, y) {},
onKey: (k, a) => _controller
?.sendControlCommand(ControlCommands.key(k, a)),
onMotionEvent: (a, x, y) => _controller
?.sendControlCommand(ControlCommands.motionEvent(a, x, y)),
onKey: (k, a) => _controller?.sendControlCommand(
ControlCommands.key(k, a),
),
onMotionEvent: (a, x, y) => _controller?.sendControlCommand(
ControlCommands.motionEvent(a, x, y),
),
),
],
),
@@ -281,14 +290,10 @@ class _ControllerHomeState extends State<ControllerHome> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_status,
style: const TextStyle(color: Colors.white),
),
Text(_status, style: const TextStyle(color: Colors.white)),
Text(
_stats,
style:
const TextStyle(color: Colors.white, fontSize: 12),
style: const TextStyle(color: Colors.white, fontSize: 12),
),
],
),
@@ -297,13 +302,26 @@ class _ControllerHomeState extends State<ControllerHome> {
Positioned(
bottom: 16,
right: 16,
child: FloatingActionButton(
child: CupertinoButton(
padding: EdgeInsets.zero,
onPressed: _disconnect,
tooltip: '断开连接',
child: const Icon(Icons.close),
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: CupertinoColors.destructiveRed,
borderRadius: BorderRadius.circular(24),
),
alignment: Alignment.center,
child: const Icon(
CupertinoIcons.clear,
color: CupertinoColors.white,
),
),
),
),
],
),
);
}
}