播放视频是应用开发中常见的任务,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 文件位于 <项目根目录>/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,将以下内容添加到 Info.plist 文件中,该文件位于 <项目根目录>/ios/Runner/Info.plist

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

macOS

#

如果你使用基于网络的视频,请添加 com.apple.security.network.client 授权

Web

#

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

不同的网络浏览器可能具有不同的视频播放功能,例如支持的格式或自动播放。请查阅 video_player_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();

    // Create and store the VideoPlayerController. The VideoPlayerController
    // offers several different constructors to play videos from assets, files,
    // or the internet.
    _controller = VideoPlayerController.networkUrl(
      Uri.parse(
        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
      ),
    );

    _initializeVideoPlayerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    // Ensure disposing of the VideoPlayerController to free up resources.
    _controller.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // Complete the code in the next step.
    return Container();
  }
}

4. 显示视频播放器

#

现在,显示视频。video_player 插件提供了 VideoPlayer 小部件来显示由 VideoPlayerController 初始化的视频。默认情况下,VideoPlayer 小部件会占用尽可能多的空间。这对于视频通常不理想,因为它们应该以特定的宽高比显示,例如 16x9 或 4x3。

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

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

dart
// Use a FutureBuilder to display a loading spinner while waiting for the
// VideoPlayerController to finish initializing.
FutureBuilder(
  future: _initializeVideoPlayerFuture,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.done) {
      // If the VideoPlayerController has finished initialization, use
      // the data it provides to limit the aspect ratio of the video.
      return AspectRatio(
        aspectRatio: _controller.value.aspectRatio,
        // Use the VideoPlayer widget to display the video.
        child: VideoPlayer(_controller),
      );
    } else {
      // If the VideoPlayerController is still initializing, show a
      // loading spinner.
      return const Center(child: CircularProgressIndicator());
    }
  },
)

5. 播放和暂停视频

#

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

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

dart
FloatingActionButton(
  onPressed: () {
    // Wrap the play or pause in a call to `setState`. This ensures the
    // correct icon is shown.
    setState(() {
      // If the video is playing, pause it.
      if (_controller.value.isPlaying) {
        _controller.pause();
      } else {
        // If the video is paused, play it.
        _controller.play();
      }
    });
  },
  // Display the correct icon depending on the state of the player.
  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();

    // Create and store the VideoPlayerController. The VideoPlayerController
    // offers several different constructors to play videos from assets, files,
    // or the internet.
    _controller = VideoPlayerController.networkUrl(
      Uri.parse(
        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
      ),
    );

    // Initialize the controller and store the Future for later use.
    _initializeVideoPlayerFuture = _controller.initialize();

    // Use the controller to loop the video.
    _controller.setLooping(true);
  }

  @override
  void dispose() {
    // Ensure disposing of the VideoPlayerController to free up resources.
    _controller.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Butterfly Video')),
      // Use a FutureBuilder to display a loading spinner while waiting for the
      // VideoPlayerController to finish initializing.
      body: FutureBuilder(
        future: _initializeVideoPlayerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            // If the VideoPlayerController has finished initialization, use
            // the data it provides to limit the aspect ratio of the video.
            return AspectRatio(
              aspectRatio: _controller.value.aspectRatio,
              // Use the VideoPlayer widget to display the video.
              child: VideoPlayer(_controller),
            );
          } else {
            // If the VideoPlayerController is still initializing, show a
            // loading spinner.
            return const Center(child: CircularProgressIndicator());
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Wrap the play or pause in a call to `setState`. This ensures the
          // correct icon is shown.
          setState(() {
            // If the video is playing, pause it.
            if (_controller.value.isPlaying) {
              _controller.pause();
            } else {
              // If the video is paused, play it.
              _controller.play();
            }
          });
        },
        // Display the correct icon depending on the state of the player.
        child: Icon(
          _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
        ),
      ),
    );
  }
}