添加 apivideo_live_stream 依赖;
修改 Auth 页面文本为“输入执裁口令”; 添加公用 AppBar 组件 模拟核心流程交互
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apivideo_live_stream/apivideo_live_stream.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_bar.dart';
|
||||
|
||||
class PushSteamTestWidget extends StatefulWidget {
|
||||
const PushSteamTestWidget({super.key});
|
||||
|
||||
final String rtmpUrl = 'rtmp://192.168.1.245:19090/live/2026-07-07';
|
||||
|
||||
@override
|
||||
State<PushSteamTestWidget> createState() => _PushSteamTestWidgetState();
|
||||
}
|
||||
|
||||
class _PushSteamTestWidgetState extends State<PushSteamTestWidget>
|
||||
with WidgetsBindingObserver {
|
||||
late final ApiVideoLiveStreamController _controller;
|
||||
bool _ready = false;
|
||||
bool _isStreaming = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
_controller = ApiVideoLiveStreamController(
|
||||
initialAudioConfig: AudioConfig(bitrate: 128000),
|
||||
initialVideoConfig: VideoConfig.withDefaultBitrate(
|
||||
resolution: Resolution.RESOLUTION_1080,
|
||||
fps: 30,
|
||||
),
|
||||
onConnectionSuccess: () => {
|
||||
debugPrint('推流成功'),
|
||||
|
||||
setState(() => _isStreaming = true),
|
||||
},
|
||||
onConnectionFailed: (reason) {
|
||||
setState(() => _isStreaming = false);
|
||||
debugPrint('推流失败: $reason');
|
||||
},
|
||||
onDisconnection: () => setState(() => _isStreaming = false),
|
||||
);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _initialize());
|
||||
}
|
||||
|
||||
Future<void> _initialize() async {
|
||||
try {
|
||||
await _controller.initialize();
|
||||
await _controller.startPreview();
|
||||
|
||||
setState(() => _ready = true);
|
||||
} catch (e) {
|
||||
debugPrint('初始化失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 把服务端下发的完整 rtmp:// 地址拆成 "服务器地址" + "streamKey" 两部分
|
||||
Future<void> _startPush() async {
|
||||
final uri = Uri.parse(widget.rtmpUrl);
|
||||
final streamKey = uri.pathSegments.last;
|
||||
final baseUrl = widget.rtmpUrl.substring(
|
||||
0,
|
||||
widget.rtmpUrl.lastIndexOf(streamKey),
|
||||
);
|
||||
await _controller.startStreaming(streamKey: streamKey, url: baseUrl);
|
||||
}
|
||||
|
||||
Future<void> _stopPush() => _controller.stopStreaming();
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.inactive) {
|
||||
_controller.stop();
|
||||
} else if (state == AppLifecycleState.resumed) {
|
||||
_controller.startPreview();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: myAppBar(context: context),
|
||||
body: Stack(
|
||||
children: [
|
||||
if (_ready)
|
||||
ApiVideoCameraPreview(controller: _controller, fit: BoxFit.cover),
|
||||
Positioned(
|
||||
bottom: 32,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: FloatingActionButton(
|
||||
backgroundColor: _isStreaming ? Colors.red : Colors.green,
|
||||
onPressed: _ready
|
||||
? (_isStreaming ? _stopPush : _startPush)
|
||||
: null,
|
||||
child: Icon(_isStreaming ? Icons.stop : Icons.circle),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:recording_tool/app/router/app_navigator.dart';
|
||||
import 'package:recording_tool/features/scan_qrcode/pages/page_push_test.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_bar.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_button.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_qr_scanner_dialog.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_webview.dart';
|
||||
|
||||
class ScanQrCodePage extends StatefulWidget {
|
||||
const ScanQrCodePage({super.key});
|
||||
@@ -16,18 +17,7 @@ class _AuthPageWidgetState extends State<ScanQrCodePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
),
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Colors.black,
|
||||
),
|
||||
appBar: myAppBar(context: context, title: '扫码'),
|
||||
body: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -49,9 +39,10 @@ class _AuthPageWidgetState extends State<ScanQrCodePage> {
|
||||
final result = await AppQrScannerDialog.show(context);
|
||||
if (result == null || result.isEmpty) return;
|
||||
debugPrint('扫码结果: $result');
|
||||
AppNavigator.push(
|
||||
const WebviewPage(url: 'https://www.dronex.cc/'),
|
||||
);
|
||||
// AppNavigator.push(
|
||||
// const WebviewPage(url: 'https://www.dronex.cc/'),
|
||||
// );
|
||||
AppNavigator.push(const PushSteamTestWidget());
|
||||
},
|
||||
variant: AppButtonVariant.secondary,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user