播放影片是應用開發中常見的任務,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,
        ),
      ),
    );
  }
}