Skip to main content

播放和暂停视频

在应用开发中,播放视频是一项常见任务,Flutter 应用也不例外。为了播放视频,Flutter 团队提供了 video_player 插件。您可以使用 video_player 插件播放存储在文件系统中、作为资源或来自互联网的视频。

在 iOS 上,video_player 插件利用 AVPlayer 来处理播放。在 Android 上,它使用 ExoPlayer

此示例演示如何使用 video_player 包通过以下步骤从互联网流式传输视频,并使用基本的播放和暂停控件:

  1. 添加 video_player 依赖项。
  2. 向您的应用添加权限。
  3. 创建并初始化 VideoPlayerController
  4. 显示视频播放器。
  5. 播放和暂停视频。

1. 添加 video_player 依赖项

#

此示例依赖于一个 Flutter 插件:video_player。首先,将此依赖项添加到您的项目中。

要添加 video_player 包作为依赖项,请运行 flutter pub add

flutter pub add video_player

2. 向您的应用添加权限

#

接下来,更新您的 androidios 配置,以确保您的应用具有从互联网流式传输视频的正确权限。

Android

#

<application> 定义之后,将以下权限添加到 AndroidManifest.xml 文件中。AndroidManifest.xml 文件位于 <project root>/android/app/src/main/AndroidManifest.xml

xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application ...>

    </application>

    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

iOS

#

对于 iOS,请将以下内容添加到位于 <project root>/ios/Runner/Info.plistInfo.plist 文件中。

xml
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

macOS

#

如果您使用基于网络的视频,mac-entitlement

Web

#

Flutter web 不支持 dart:io,因此请避免对插件使用 VideoPlayerController.file 构造函数。使用此构造函数会尝试创建一个抛出 UnimplementedErrorVideoPlayerController.file

不同的 Web 浏览器可能具有不同的视频播放功能,例如支持的格式或自动播放。查看 video_player_web 包以获取更多特定于 Web 的信息。

VideoPlayerOptions.mixWithOthers 选项目前无法在 Web 中实现。如果您在 Web 中使用此选项,它将被静默忽略。

3. 创建并初始化 VideoPlayerController

#

现在您已经安装了具有正确权限的 video_player 插件,请创建一个 VideoPlayerControllerVideoPlayerController 类允许您连接到不同类型的视频并控制播放。

在播放视频之前,您还必须 initialize 控制器。这将建立与视频的连接并为播放准备控制器。

要创建并初始化 VideoPlayerController,请执行以下操作:

  1. 创建一个带有配套 State 类的 StatefulWidget
  2. State 类添加一个变量来存储 VideoPlayerController
  3. State 类添加一个变量来存储 VideoPlayerController.initialize 返回的 Future
  4. initState 方法中创建并初始化控制器
  5. dispose 方法中释放控制器
dart
class VideoPlayerScreen extends StatefulWidget {
  const VideoPlayerScreen({super.key});

  @override
  State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  late VideoPlayerController _controller;
  late Future<void> _initializeVideoPlayerFuture;

  @override
  void initState() {
    super.initState();

    // 创建并存储 VideoPlayerController。VideoPlayerController
    // 提供了多个不同的构造函数,用于从资源、文件或互联网播放视频。
    _controller = VideoPlayerController.networkUrl(
      Uri.parse(
        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
      ),
    );

    _initializeVideoPlayerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    // 确保释放 VideoPlayerController 以释放资源。
    _controller.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 完成下一步中的代码。
    return Container();
  }
}

4. 显示视频播放器

#

现在,显示视频。video_player 插件提供 VideoPlayer 小部件来显示由 VideoPlayerController 初始化的视频。 默认情况下,VideoPlayer 小部件占据尽可能多的空间。对于视频来说,这通常并非理想选择,因为视频旨在以特定纵横比显示,例如 16x9 或 4x3。

因此,将 VideoPlayer 小部件包装在 AspectRatio 小部件中,以确保视频具有正确的比例。

此外,您必须在 _initializeVideoPlayerFuture() 完成后显示 VideoPlayer 小部件。使用 FutureBuilder 在控制器完成初始化之前显示加载微调器。注意:初始化控制器不会开始播放。

dart
// 使用 FutureBuilder 在等待 VideoPlayerController 完成初始化时显示加载微调器。
FutureBuilder(
  future: _initializeVideoPlayerFuture,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.done) {
      // 如果 VideoPlayerController 已完成初始化,请使用
      // 它提供的数据来限制视频的纵横比。
      return AspectRatio(
        aspectRatio: _controller.value.aspectRatio,
        // 使用 VideoPlayer 小部件显示视频。
        child: VideoPlayer(_controller),
      );
    } else {
      // 如果 VideoPlayerController 仍在初始化,则显示
      // 加载微调器。
      return const Center(
        child: CircularProgressIndicator(),
      );
    }
  },
)

5. 播放和暂停视频

#

默认情况下,视频以暂停状态开始。要开始播放,请调用 VideoPlayerController 提供的 play() 方法。要暂停播放,请调用 pause() 方法。

对于此示例,请向您的应用添加一个 FloatingActionButton,该按钮根据情况显示播放或暂停图标。当用户点击按钮时,如果视频当前已暂停,则播放视频;如果视频正在播放,则暂停视频。

dart
FloatingActionButton(
  onPressed: () {
    // 将播放或暂停包装在对 `setState` 的调用中。这确保
    // 显示正确的图标。
    setState(() {
      // 如果视频正在播放,则暂停它。
      if (_controller.value.isPlaying) {
        _controller.pause();
      } else {
        // 如果视频已暂停,则播放它。
        _controller.play();
      }
    });
  },
  // 根据播放器的状态显示正确的图标。
  child: Icon(
    _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
  ),
)

完整示例

#
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

void main() => runApp(const VideoPlayerApp());

class VideoPlayerApp extends StatelessWidget {
  const VideoPlayerApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Video Player Demo',
      home: VideoPlayerScreen(),
    );
  }
}

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

  @override
  State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  late VideoPlayerController _controller;
  late Future<void> _initializeVideoPlayerFuture;

  @override
  void initState() {
    super.initState();

    // 创建并存储 VideoPlayerController。VideoPlayerController
    // 提供了多个不同的构造函数,用于从资源、文件或互联网播放视频。
    _controller = VideoPlayerController.networkUrl(
      Uri.parse(
        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
      ),
    );

    // 初始化控制器并存储 Future 以备后用。
    _initializeVideoPlayerFuture = _controller.initialize();

    // 使用控制器循环播放视频。
    _controller.setLooping(true);
  }

  @override
  void dispose() {
    // 确保释放 VideoPlayerController 以释放资源。
    _controller.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Butterfly Video'),
      ),
      // 使用 FutureBuilder 在等待 VideoPlayerController 完成初始化时显示加载微调器。
      body: FutureBuilder(
        future: _initializeVideoPlayerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            // 如果 VideoPlayerController 已完成初始化,请使用
            // 它提供的数据来限制视频的纵横比。
            return AspectRatio(
              aspectRatio: _controller.value.aspectRatio,
              // 使用 VideoPlayer 小部件显示视频。
              child: VideoPlayer(_controller),
            );
          } else {
            // 如果 VideoPlayerController 仍在初始化,则显示
            // 加载微调器。
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 将播放或暂停包装在对 `setState` 的调用中。这确保
          // 显示正确的图标。
          setState(() {
            // 如果视频正在播放,则暂停它。
            if (_controller.value.isPlaying) {
              _controller.pause();
            } else {
              // 如果视频已暂停,则播放它。
              _controller.play();
            }
          });
        },
        // 根据播放器的状态显示正确的图标。
        child: Icon(
          _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
        ),
      ),
    );
  }
}