Flutter透明视频动画插件flutter_vap的使用

Flutter透明视频动画插件flutter_vap的使用

透明视频动画

透明视频动画是当前较为流行的动画实现方式之一。各大厂商也开源了自己的框架。最终,我们选择了腾讯的VAP(Video Animation Player),它支持Android、iOS和Web,并且为我们封装flutter_vap提供了便利。此外,它还提供了一个工具,可以从帧图像生成带有Alpha通道的视频,非常实用。

VAP(Video Animation Player)由Penguin E-sports开发,用于播放酷炫的动画。

  • 与Webp和Apng动画解决方案相比,它具有更高的压缩率(更小的素材)和硬件解码(更快的解码速度)的优势。
  • 与Lottie相比,它可以实现更复杂的动画效果(如粒子效果)。

预览

image

Youtube 视频

七牛云视频

APK 下载

安装

pubspec.yaml 文件中添加依赖:

dependencies:
  flutter_vap: ${last_version}

如何使用

导入必要的包:

import 'package:flutter_vap/flutter_vap.dart';

基本使用示例:

// VapView可以通过外层包Container()设置宽高来限制弹出视频的宽高
IgnorePointer(
  child: VapView(),
),
1. 播放本地视频
Future<Map<dynamic, dynamic>> _playFile(String path) async {
  if (path == null) {
    return null;
  }
  var res = await VapController.playPath(path);
  if (res["status"] == "failure") {
    showToast(res["errorMsg"]);
  }
  return res;
}
2. 播放资源文件中的视频
Future<Map<dynamic, dynamic>> _playAsset(String asset) async {
  if (asset == null) {
    return null;
  }
  var res = await VapController.playAsset(asset);
  if (res["status"] == "failure") {
    showToast(res["errorMsg"]);
  }
  return res;
}
3. 停止播放
VapController.stop();
4. 队列播放
_queuePlay() async {
  // 模拟多个地方同时调用播放,使得队列执行播放。
  QueueUtil.get("vapQueue").addTask(() => VapController.playPath(downloadPathList[0]));
  QueueUtil.get("vapQueue").addTask(() => VapController.playPath(downloadPathList[1]));
}
5. 取消队列播放
_queueCancelPlay() {
  QueueUtil.get("vapQueue").cancelTask();
}

示例代码

以下是完整的示例代码,演示了如何使用flutter_vap插件。

import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:dio/dio.dart';

import 'package:flutter_vap/flutter_vap.dart';
import 'package:oktoast/oktoast.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<String> downloadPathList = [];
  bool isDownload = false;

  [@override](/user/override)
  void initState() {
    super.initState();
    initDownloadPath();
  }

  Future<void> initDownloadPath() async {
    Directory appDocDir = await getApplicationDocumentsDirectory();
    String rootPath = appDocDir.path;
    downloadPathList = ["$rootPath/vap_demo1.mp4", "$rootPath/vap_demo2.mp4"];
    print("downloadPathList:$downloadPathList");
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return OKToast(
      child: MaterialApp(
        home: Scaffold(
          body: Container(
            width: double.infinity,
            height: double.infinity,
            decoration: BoxDecoration(
              color: Color.fromARGB(255, 140, 41, 43),
              image: DecorationImage(image: AssetImage("static/bg.jpeg")),
            ),
            child: Stack(
              alignment: Alignment.bottomCenter,
              children: [
                Column(
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    CupertinoButton(
                      color: Colors.purple,
                      child: Text("download video source${isDownload ? "(✅)" : ""}"),
                      onPressed: _download,
                    ),
                    CupertinoButton(
                      color: Colors.purple,
                      child: Text("File1 play"),
                      onPressed: () => _playFile(downloadPathList[0]),
                    ),
                    CupertinoButton(
                      color: Colors.purple,
                      child: Text("File2 play"),
                      onPressed: () => _playFile(downloadPathList[1]),
                    ),
                    CupertinoButton(
                      color: Colors.purple,
                      child: Text("asset play"),
                      onPressed: () => _playAsset("static/demo.mp4"),
                    ),
                    CupertinoButton(
                      color: Colors.purple,
                      child: Text("stop play"),
                      onPressed: () => VapController.stop(),
                    ),
                    CupertinoButton(
                      color: Colors.purple,
                      child: Text("queue play"),
                      onPressed: _queuePlay,
                    ),
                    CupertinoButton(
                      color: Colors.purple,
                      child: Text("cancel queue play"),
                      onPressed: _cancelQueuePlay,
                    ),
                  ],
                ),
                IgnorePointer(
                  // VapView可以通过外层包Container()设置宽高来限制弹出视频的宽高
                  child: VapView(),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  _download() async {
    await Dio().download(
        "http://file.jinxianyun.com/vap_demo1.mp4", downloadPathList[0]);
    await Dio().download(
        "http://file.jinxianyun.com/vap_demo2.mp4", downloadPathList[1]);
    setState(() {
      isDownload = true;
    });
  }

  Future<Map<dynamic, dynamic>?> _playFile(String path) async {
    if (path == null) {
      return null;
    }
    var res = await VapController.playPath(path);
    if (res!["status"] == "failure") {
      showToast(res["errorMsg"]);
    }
    return res;
  }

  Future<Map<dynamic, dynamic>?> _playAsset(String asset) async {
    if (asset == null) {
      return null;
    }
    var res = await VapController.playAsset(asset);
    if (res!["status"] == "failure") {
      showToast(res["errorMsg"]);
    }
    return res;
  }

  _queuePlay() async {
    // 模拟多个地方同时调用播放,使得队列执行播放。
    QueueUtil.get("vapQueue")
        ?.addTask(() => VapController.playPath(downloadPathList[0]));
    QueueUtil.get("vapQueue")
        ?.addTask(() => VapController.playPath(downloadPathList[1]));
  }

  _cancelQueuePlay() {
    QueueUtil.get("vapQueue")?.cancelTask();
  }
}

更多关于Flutter透明视频动画插件flutter_vap的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter透明视频动画插件flutter_vap的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,针对flutter_vap这个未知功能但名称可能暗示某些特性的Flutter插件,我们可以基于一些常见的插件命名习惯和Flutter开发中的常见功能进行合理推测,并给出一些假设性的代码案例。请注意,这些代码案例是基于假设的,因为flutter_vap的实际功能未知。

假设一:flutter_vap可能与视频播放相关

基于名称中的vap可能代表“Video Audio Player”或类似的缩写,我们可以假设这个插件用于视频播放功能。以下是一个假设性的代码案例,展示如何使用这样的插件播放视频:

import 'package:flutter/material.dart';
import 'package:flutter_vap/flutter_vap.dart'; // 假设的包导入

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: VideoPlayerScreen(),
    );
  }
}

class VideoPlayerScreen extends StatefulWidget {
  @override
  _VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  VapController? _controller;

  @override
  void initState() {
    super.initState();
    // 初始化控制器并设置视频源
    _controller = VapController.network(
      'https://www.example.com/video.mp4',
      autoPlay: true,
      looping: false,
    );

    // 监听视频播放完成事件
    _controller!.setVideoCompletedListener(() {
      print('Video playback completed.');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Video Player Demo'),
      ),
      body: Center(
        child: _controller!.value.isInitialized
            ? AspectRatio(
                aspectRatio: _controller!.value.aspectRatio,
                child: VapPlayer(_controller!),
              )
            : Container(
                child: CircularProgressIndicator(),
              ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            // 切换播放/暂停状态
            if (_controller!.value.isPlaying) {
              _controller!.pause();
            } else {
              _controller!.play();
            }
          });
        },
        tooltip: 'Play/Pause',
        child: Icon(
          _controller!.value.isPlaying ? Icons.pause : Icons.play_arrow,
        ),
      ),
    );
  }

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

假设二:flutter_vap可能与音频处理相关

如果flutter_vap与音频处理相关,比如音频录制或播放,以下是一个假设性的音频录制和播放的代码案例:

import 'package:flutter/material.dart';
import 'package:flutter_vap/flutter_vap.dart'; // 假设的包导入

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: AudioRecorderScreen(),
    );
  }
}

class AudioRecorderScreen extends StatefulWidget {
  @override
  _AudioRecorderScreenState createState() => _AudioRecorderScreenState();
}

class _AudioRecorderScreenState extends State<AudioRecorderScreen> {
  VapAudioRecorder? _recorder;
  String _recordingPath = '';

  @override
  void initState() {
    super.initState();
    // 初始化音频录制器
    _recorder = VapAudioRecorder(
      onRecordingStateChanged: (state) {
        print('Recording state changed to: $state');
      },
      onError: (error) {
        print('Recording error: $error');
      },
    );
  }

  void _startRecording() async {
    String path = await _recorder!.startRecording(localPath: 'example_audio.wav');
    setState(() {
      _recordingPath = path;
    });
  }

  void _stopRecording() async {
    await _recorder!.stopRecording();
  }

  void _playRecording() async {
    if (_recordingPath.isNotEmpty) {
      await VapAudioPlayer().play(_recordingPath);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Audio Recorder Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _startRecording,
              child: Text('Start Recording'),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: _stopRecording,
              child: Text('Stop Recording'),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: _playRecording,
              child: Text('Play Recording'),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _recorder?.dispose();
    super.dispose();
  }
}

请注意,以上代码案例完全是基于假设的,因为flutter_vap插件的实际功能和API未知。在实际开发中,你需要查阅插件的官方文档或源代码来了解其真实的功能和使用方法。

回到顶部