flutter实现直播推流端

这里以 Flutter 社区常用的 apivideo_live_stream 插件为例,带你从 0 到 1 搭建一个基础的推流页面。

第一步:原生环境权限配置

推流需要调用手机的摄像头和麦克风,因此必须在 Android 和 iOS 的原生配置文件中申请权限。

Android 配置

打开 android/app/src/main/AndroidManifest.xml,在 <manifest> 标签内添加以下权限:

XML 复制代码
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />

iOS 配置

打开 ios/Runner/Info.plist,在 <dict> 标签内添加相机和麦克风的使用描述:

XML 复制代码
<!-- 2. 👇 插入在这里:直播所需的权限配置 (直接复制下面这6行) 👇 -->
	<key>NSCameraUsageDescription</key>
	<string>需要访问您的相机以进行直播推流</string>
	<key>NSMicrophoneUsageDescription</key>
	<string>需要访问您的麦克风以进行直播推流</string>
	<!-- 2. 👆 权限配置结束 👆 -->

第二步:安装依赖

在你的 Flutter 项目根目录下的 pubspec.yaml 文件中,添加推流插件:

XML 复制代码
dependencies:
  flutter:
    sdk: flutter
  apivideo_live_stream: ^1.2.0 #最新版本号  # 建议去 pub.dev 查看并填入最新版本
  permission_handler: ^12.0.1 #检测工具

保存后在终端运行 flutter pub get 安装依赖。

第三步:推流页面完整代码实现

以下是一个包含权限检查、推流控制器初始化、摄像头预览画面、开始推流 的完整页面代码。你可以直接将其复制到你的 lib 目录下运行(记得替换真实的 RTMP 推流地址):

Dart 复制代码
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:apivideo_live_stream/apivideo_live_stream.dart';
import 'package:permission_handler/permission_handler.dart'; // 用于权限检查

void main() {
  runApp(const LivePushPage());
}

class LivePushPage extends StatefulWidget {
  const LivePushPage({super.key});

  @override
  State<LivePushPage> createState() => _LivePushPageState();
}

class _LivePushPageState extends State<LivePushPage> {
  late ApiVideoLiveStreamController _controllerStream;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  bool _isStreaming = false;

  // TODO: 替换为你自己的 RTMP 推流地址(如阿里云、腾讯云或自建 Nginx-RTMP 服务器的地址)
  final String rtmpUrl = 'rtmp://你的推流服务器地址/live/streamKey';

  @override
  void initState() {
    super.initState();
    _initPermissions();
    _initLiveStreamController();
  }

  // 1. 检查并申请权限
  Future<void> _initPermissions() async {
    await Permission.camera.request();
    await Permission.microphone.request();
  }

  // 2. 初始化推流控制器
  void _initLiveStreamController() {
    _controllerStream = ApiVideoLiveStreamController(
      initialAudioConfig: AudioConfig(), // 默认音频配置
      initialVideoConfig: VideoConfig.withDefaultBitrate(
        resolution: Resolution.RESOLUTION_720, // 设置分辨率为 720P
      ),
      initialCameraPosition: CameraPosition.front, // 默认前置摄像头
      onConnectionSuccess: () {
        debugPrint('推流连接成功');
      },
      onConnectionFailed: (error) {
        debugPrint('推流连接失败: $error');
      },
      onDisconnection: () {
        debugPrint('推流已断开');
        setState(() => _isStreaming = false);
      },
      onError: (error) {
        debugPrint('推流发生错误: $error');
      },
    );
    _controllerStream.initialize();
  }

  // 3. 开始/停止推流
  Future<void> _toggleStreaming() async {
    if (_isStreaming) {
      await _controllerStream.stopStreaming();
      setState(() => _isStreaming = false);
    } else {
      try {
        await _controllerStream.startStreaming(
          url: rtmpUrl,
          streamKey: 'test_stream_key', // 部分服务器需要
        );
        setState(() => _isStreaming = true);
      } catch (e) {
        debugPrint('开始推流失败: $e');
      }
    }
  }

  @override
  void dispose() {
    _controllerStream.stopStreaming();
    _controllerStream.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      backgroundColor: Colors.black,
      body: Stack(
        children: [
          // 4. 主播预览画面
          SizedBox.expand(
            child: FittedBox(
              fit: BoxFit.cover,
              child: SizedBox(
                width: MediaQuery.of(context).size.width,
                child: ApiVideoCameraPreview(
                  key: _scaffoldKey,
                  controller: _controllerStream,
                ),
              ),
            ),
          ),
          // 底部控制按钮
          Positioned(
            bottom: 50,
            left: 0,
            right: 0,
            child: Center(
              child: ElevatedButton(
                onPressed: _toggleStreaming,
                style: ElevatedButton.styleFrom(
                  backgroundColor: _isStreaming ? Colors.red : Colors.green,
                  padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15),
                ),
                child: Text(_isStreaming ? '停止推流' : '开始推流'),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
相关推荐
心中有国也有家2 小时前
AtomGit Flutter 鸿蒙客户端:呼吸练习的三种科学模式
学习·flutter·华为·harmonyos
yuanlaile3 小时前
前端转 Flutter 自学完整规划,避开跨栈思维转换误区
前端·flutter·flutter跨平台开发
姑苏倾城客3 小时前
Flutter中,html 与 dart 桥接沟通
javascript·flutter·html
ZZZMMM.zip4 小时前
基于鸿蒙HarmonyOS NEXT开发AI书籍推荐应用:智能阅读新体验与鸿蒙Flutter框架跨端实践
人工智能·flutter·华为·harmonyos·鸿蒙
铅笔侠_小龙虾5 小时前
DSL-Flutter : 告别嵌套地狱,用缩进书写 Flutter!
flutter
ZZZMMM.zip18 小时前
基于鸿蒙HarmonyOS NEXT开发AI股票分析应用:智能投资新体验与鸿蒙Flutter框架跨端实践
人工智能·flutter·华为·harmonyos·鸿蒙
韩曙亮1 天前
【Flutter】Flutter 进程保活 ③ ( 屏幕常亮设置 | Flutter 禁止息屏、关闭自动锁屏、屏蔽系统屏保 )
android·flutter·ios·屏幕常亮·禁止息屏
心中有国也有家1 天前
AtomGit Flutter 鸿蒙客户端:情绪日记的时间线实现
学习·flutter·华为·harmonyos
西西学代码1 天前
Flutter---Container
flutter
西西学代码1 天前
Flutter---底部导航栏(2)
开发语言·javascript·flutter