Flutter video_player总结

文章目录

Flutter video_player总结

概述

video_player 是 Flutter 官方原生视频播放插件,跨平台稳定、轻量,是 Flutter 项目视频播放核心基础组件,支持 Android、iOS、Web、macOS。

video_player 是纯底层播放内核,只负责解码、渲染视频画面、提供播放控制 API。

添加依赖

复制代码
dependencies:
  video_player: ^2.14.0

平台配置

Android:

在 android/app/src/main/AndroidManifest.xml 中添加网络权限:

复制代码
<uses-permission android:name="android.permission.INTERNET"/>

iOS:

在 ios/Runner/Info.plist 下添加:

复制代码
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

核心类

  • VideoPlayerController:控制器,负责加载视频、控制播放、监听状态等
    • _videoController.value 获取实时播放状态,可用于自定义状态判断、业务逻辑拦截:
      • isInitialized:视频是否初始化完成,所有播放操作必须先判断该状态,防止闪退
      • isPlaying:当前是否处于播放状态
      • position:当前播放进度,可用于实现断点续播、进度记录
      • duration:视频总时长,用于计算播放进度百分比
      • isBuffering:是否处于缓冲加载状态,可自定义缓冲提示UI
      • volume:当前播放音量
  • VideoPlayer:Widget,负责显示视频
  • VidePlayerValue:状态存储类,保存播放进度、时长、播放状态、缓冲信息

用法

初始化

dart 复制代码
// 1. 网络视频(最常用、官方推荐)
VideoPlayerController.networkUrl(Uri.parse("你的视频链接"))

// 2. 项目本地assets视频
VideoPlayerController.asset("assets/video/demo.mp4")

// 3. 设备本地文件视频
VideoPlayerController.file(File("文件绝对路径"))

播放器空控制方法

dart 复制代码
// 播放、暂停
_videoController.play();
_videoController.pause();

// 精准跳转播放位置
_videoController.seekTo(const Duration(seconds: 10));

// 循环播放开关
_videoController.setLooping(true);

// 音量调节 0.0~1.0
_videoController.setVolume(0.6);

// 播放倍速调节
_videoController.setPlaybackSpeed(1.25);

完整案例

dart 复制代码
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

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

  @override
  State<VideoPage> createState() => _VideoPageState();
}

class _VideoPageState extends State<VideoPage> {
  late VideoPlayerController _controller;

  // 视频比例
  static const double _videoRatio = 16.0 / 9.0;

  Future<void> _initVideo() async {
    const String videoUrl = "https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4";
    _controller = VideoPlayerController.networkUrl(Uri.parse(videoUrl));
    await _controller.initialize();
    // 监听状态实时刷新UI(进度、音量、播放状态)
    _controller.addListener(() => setState(() {}));
  }

  // 开始播放
  void start() {
    _controller.play();
  }

  // 暂停
  void pause() {
    _controller.pause();
  }

  // 停止播放
  Future<void> _stop() async {
    await _controller.pause();
    // 重置视频到起始位置,实现真正停止效果
    await _controller.seekTo(Duration.zero);
  }

  // 是否在播放
  bool isPlaying() {
    return _controller.value.isPlaying;
  }

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

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("video_player")),
      body: Column(
        children: [
          // 视频画面
          AspectRatio(
            aspectRatio: _videoRatio,
            child: _controller.value.isInitialized
                ? VideoPlayer(_controller)
                : Center(child: CircularProgressIndicator()),
          ),

          // 播放进度条
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
            child: Slider(
              min: 0,
              max: _controller.value.duration.inSeconds.toDouble(),
              value: _controller.value.position.inSeconds.toDouble(),
              onChanged: (val) {
                _controller.seekTo(Duration(seconds: val.toInt()));
              },
            ),
          ),

          // 音量控制条
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20),
            child: Row(
              children: [
                const Icon(Icons.volume_up),
                Expanded(
                  child: Slider(
                    min: 0.0,
                    max: 1.0,
                    value: _controller.value.volume,
                    onChanged: (double volume) {
                      // 更新音量 0.0 ~ 1.0
                      _controller.setVolume(volume);
                    },
                  ),
                ),
              ],
            ),
          ),

          const SizedBox(height: 20),

          // 播放/暂停 / 停止 整套控制按钮
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 播放/暂停按钮
              IconButton(
                iconSize: 50,
                icon: Icon(isPlaying() ? Icons.pause_circle : Icons.play_circle, color: Colors.blue),
                onPressed: () {
                  setState(() {
                    isPlaying() ? pause() : start();
                  });
                },
              ),
              const SizedBox(width: 30),
              // 停止重置按钮
              IconButton(
                iconSize: 50,
                icon: const Icon(Icons.stop_circle, color: Colors.red),
                onPressed: _stop,
              ),
            ],
          ),
        ],
      ),
    );
  }
}

chewie

概述

原生的 video_player 无全屏、倍速、进度条UI,搭配 chewie 一键实现成熟播放器。

添加依赖

复制代码
dependencies:
  video_player: ^2.14.0
  chewie: ^1.15.0

核心类

Chewie 播放器采用双层控制器架构

  • VideoPlayerController::Chewie 只是UI壳子,真正解码、播放、控制视频的是 video_player 的控制器,必须绑定
  • ChewieController:
    • autoPlay:页面加载完后是否自动播放
    • looping:是否循环播放
    • startAt:初始启动播放位置
    • startAtVolume:初始音量大小
    • showControls:是否显示UI(进度条、播放按钮、音量、全屏)
    • allowFullScreen:是否开启全屏
    • allowPlaybackSpeedChanging:是否控制速度
    • playbackSpeeds:配置倍速
    • placeholder:加载时的占位组件
    • bufferingBuilder:缓冲时的组件

灰色蒙层问题

原因

Chewie 区分双平台默认样式,Android 平台自带半透明黑色渐变蒙层,iOS 平台无蒙层。

仅覆盖控件展示区域,降低画面通透度,会让视频看起来发灰、不够鲜亮。

解决

强制切换为 iOS 渲染样式

dart 复制代码
Widget _buildVideo() {
  return Theme(
    data: Theme.of(context).copyWith(platform: TargetPlatform.iOS),
    child: Chewie(controller: _chewieController),
  );
}

完整案例

dart 复制代码
import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

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

  @override
  State<ChewiePage> createState() => _ChewiePageState();
}

class _ChewiePageState extends State<ChewiePage> {
  late VideoPlayerController _videoController;
  late ChewieController _chewieController;
  bool _isLoading = true;
  bool _hasError = false;

  Future<void> _initVideo() async {
    setState(() {
      _isLoading = true;
      _hasError = false;
    });
    const String videoUrl = "https://bdcloud-player-new.cdn.bcebos.com/testvideo/mp4/360video/ThaiKongfu.mp4";
    _videoController = VideoPlayerController.networkUrl(Uri.parse(videoUrl));

    try {
      await _videoController.initialize();
      _chewieController = ChewieController(
        videoPlayerController: _videoController,
        autoPlay: false,
        looping: false,
        allowFullScreen: true,
        allowPlaybackSpeedChanging: true,
        placeholder: _buildLoading(),
      );
      setState(() => _isLoading = false);
    } catch (e) {
      setState(() {
        _isLoading = false;
        _hasError = true;
      });
      debugPrint("Chewie播放器加载失败:$e");
    }
  }

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

  @override
  void dispose() {
    _videoController.dispose();
    _chewieController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Chewie")),
      body: _isLoading
          ? _buildLoading()
          : _hasError
          ? _buildError()
          : _buildVideo(),
    );
  }

  /// loading
  Widget _buildLoading() => const Center(child: CircularProgressIndicator());

  /// error
  Widget _buildError() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Icon(Icons.error_outline, color: Colors.red, size: 48),
          const SizedBox(height: 12),
          const Text("播放器加载失败", style: TextStyle(color: Colors.red)),
          const SizedBox(height: 16),
          ElevatedButton(onPressed: _initVideo, child: const Text("点击重试")),
        ],
      ),
    );
  }

  /// 播放
  Widget _buildVideo() {
    return Theme(
      data: Theme.of(context).copyWith(platform: TargetPlatform.iOS),
      child: Chewie(controller: _chewieController),
    );
  }
}
相关推荐
见山是山-见水是水17 小时前
鸿蒙版 Flutter Video 视频播放组件:播放控制、全屏切换与倍速播放
flutter·音视频·harmonyos
六年码农20 小时前
2026最新开源 屏译ScreenTranslator 0.4.4 开源 全屏实时翻译教程
运维·人工智能·flutter·开源
WeiAreYoung21 小时前
Flutter+iOS 实现 Live Activity
flutter·ios
恋猫de小郭1 天前
超好用 R8 Configuration Analyzer, 优化你的 App 大小和内存
android·前端·flutter
键盘飞行员2 天前
Flutter App 全套实战学习计划:从零搭建到 APK 部署
学习·flutter
GitLqr2 天前
Flutter 实战:图片上传前的压缩优化,解决带宽与存储的“大户”问题
flutter·全栈·dart
进哥AI研习社2 天前
AtomCode 跨端开发实战:Flutter 项目从原型到多端运行的 AI 驱动体验
人工智能·flutter
恋猫de小郭2 天前
Flutter A2UI 的正确用法,怎么把 AI 和动态 UI 结合有效生产
android·前端·flutter
年小个大2 天前
受 go-zero 启发,我给 Flutter 整了套 MVI-BLoC 脚手架
android·flutter·架构