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 ? '停止推流' : '开始推流'),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
相关推荐
●VON14 小时前
鸿蒙Flutter实战:多选批量删除模式的实现
flutter·华为·harmonyos·鸿蒙
坚果的博客14 小时前
Flutter 三方库(Flutter-New-Badge)适配开源鸿蒙教程
flutter·开源·harmonyos
二蛋和他的大花14 小时前
高德地图 Flutter 插件:跨 Android / iOS / HarmonyOS 的完整实现
android·flutter·ios
不爱吃糖的程序媛15 小时前
鸿蒙Flutter 三方库 country_codes 的 适配实战
flutter·华为·harmonyos
●VON15 小时前
鸿蒙Flutter实战:Markdown编辑与预览实时切换
flutter·华为·harmonyos·鸿蒙
程序员老刘·1 天前
Flutter 3.44 更新要点:很重要但暂时先别升级
flutter·ai编程·跨平台开发·客户端开发
用户86284129549441 天前
Flutter rxflare 计算属性 computed:自动依赖追踪 + 缓存(超实用)
flutter
用户86284129549442 天前
Flutter rxflare 性能测试(最终推荐版):1000 列表极致优化・官方最佳写法
flutter
用户86284129549442 天前
Flutter rxflare 响应式编程:.obs + Rx 组件极简实战
flutter