添加 apivideo_live_stream 依赖;

修改 Auth 页面文本为“输入执裁口令”;
添加公用 AppBar 组件
模拟核心流程交互
This commit is contained in:
2026-07-08 09:37:38 +08:00
parent f6c477c9be
commit b21857f364
5 changed files with 152 additions and 20 deletions
@@ -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,
),