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