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 ? '停止推流' : '开始推流'),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
相关推荐
程序员老刘21 小时前
跨平台开发地图 | 2026年6月
flutter·ai编程·客户端
悟空瞎说2 天前
Flutter 架构详解:新手必懂底层原理
flutter
SoaringHeart2 天前
Flutter最佳实践:IM聊天文字链接自动识别跳转
前端·flutter
恋猫de小郭2 天前
KMP / CMP 鸿蒙版本 Beta 发布,他有什么特别之处?
android·前端·flutter
风华圆舞3 天前
Flutter + 鸿蒙 Intents Kit:页面直达能力的完整接入方案
flutter·ui·华为·harmonyos
韩曙亮3 天前
【Flutter】Flutter 组件 ④ ( 组件渲染 的 三棵树理论 | Widget 树 → Element 树 → RenderObject 树 )
flutter·element·widget·renderobject
恋猫de小郭3 天前
Android 17 正式版发布,全新 AI 和各种破坏性更新
android·前端·flutter
kingbal3 天前
Windows:flutter环境搭建
windows·flutter
911hzh3 天前
Flutter MethodChannel 跨端通信框架 zh_native_channel:快速入门、优势分析与 Pigeon 对比
flutter
911hzh3 天前
Flutter 快速搭建新项目:用 Flutter Foundation Kit 一条命令生成带基础架构的 App 模板
flutter