Flutter控件封装之视频进度条

news2024/9/22 19:29:39

视频控制器,三方所提供的样式,有时很难满足我们的需求,对于此情况,我们不得不在此基础上自行封装,今天所分享的文章就是一个很简单的控制器封装案例,包含了基本的播放暂停,全屏和退出全屏,以及时间和进度的展示,封装了事件回调以及各个属性的控制,基本上可以满足大部分的业务需求,即便不满足,大家也可以在此基础之上拓展。

我们还是按照惯例,简单罗列一个大纲:

1、基本的效果展示

2、具体使用和相关属性介绍

3、控制器封装考虑因素

4、控制器部分功能代码刨析

5、总结及源码地址

一、基本的效果展示

具体的效果,没什么好说的,都是大众常见的样式,依次从左到右为:播放暂停按钮,播放时间,播放进度,总的时间,全屏及退出全屏按钮。

可以实现的功能有,图标的动态设置,时间进度的颜色及大小控制,以及定时器的开启,具体的可以看第二项。

二、具体使用和相关属性介绍

1、具体使用

作为一个Widget,大家可以随意使用,单独亦或者和视频播放器绑定使用。

VipVideoController(
        totalTime: 1000 * 60,
        backgroundColor: Colors.red,
        progressColor: Colors.amber,
        thumbColor: Colors.red,
        textStyle: TextStyle(color: Colors.red),
        onVideoPlayClick: (isPlay) {
          print("当前播放按钮状态$isPlay");
        },
        onVideoFullScreenClick: (isFullScreen) {
          print("当前全屏按钮状态$isFullScreen");
        },
        onVideoChanged: (position) {
          //返回毫秒
          print("当前拖拽的进度$position");
        }
      )

2、相关属性

属性

类型

概述

height

double

设置控制器高度

progressHeight

double

进度条高度

videoPlayIcon

String

视频播放Icon

videoPauseIcon

String

视频暂停Icon

videoFullScreenIcon

String

视频全屏Icon

videoExitFullScreenIcon

String

退出全屏Icon

textStyle

TextStyle

文本样式

backgroundColor

Color

背景颜色

progressColor

Color

进度颜色

thumbColor

Color

拖动颜色

thumbRadius

double

thumb大小

playTimeMarginLeft

double

播放时间距离左边的距离

playTimeMarginRight

double

播放时间距离左边的距离

videoTimeMarginLeft

double

视频时间距离左边的距离

videoTimeMarginRight

double

视频时间距离左边的距离

totalTime

int

总时长

changeTime

int

改变时长

isTimer

bool

是否需要定时

onVideoPlayClick

ValueChanged<bool>

视频播放点击

onVideoFullScreenClick

ValueChanged<bool>

视频全屏点击点击

onVideoChanged

ValueChanged<int>

滑动回调

onVideoChangeStart

ValueChanged<int>

拖动开始

onVideoChangeEnd

ValueChanged<int>

拖动结束

isPlayed

bool

播放控制状态,暂停还是开始

isFullScreen

bool

是否是全屏

三、控制器封装考虑因素

视频控制器虽然说简单,但需要考虑的因素还是比较多的,比如点击播放和暂停,全屏和退出全屏的事件回调,拖动进度除了更改自身进度也要更改时间进度,传递的时间换算,定时的开启和关闭等等都是需要解决的。

1、基本的UI设定

控制器的UI一定是基于设计同学所定的UI稿,否则就要以技术驱动设计更改,一般很难,不过也有特殊的案例存在。所以在封装的时候,要么基于UI稿,要么就是动态可配置,通过属性更改基本的样式或者位置。

2、拖动进度的实现

拖动进度就比较简单了,使用的是原生提供的Slider,也就是滑杆,类似于Android中的SeekBar,需要注意的是,颜色等属性的动态配置。

3、时间的换算和进度的绑定

时间的换算,需要把传入的时间戳,转化为我们所需要的时间格式,也就是时分秒的格式,这里使用了intl国际化的插件,主要用到到格式转换DateFormat。

4、定时器的控制

定时器很简单,实例化一个Timer即可,但是,什么时候开始,什么时候暂停都是我们需要考虑的,一般情况下,直接和视频播放器进行绑定,直接更改进度即可,就不用这个定时,如果要用,可以用一个属性控制;在需要定时的情况下,点击暂停,需要暂停定时,除此之外播放完毕后也需要暂停定时;当拖动完毕后,需要开启定时,点击播放,也需要开启定时,所以,对于定时器控制这一块,一定要缕清楚。

四、控制器部分功能代码刨析

1、基本的布局

很简单,一个横向的组件Row,包裹了5个子组件,进度条使用Expanded,用于占有剩余的空间。

 return SizedBox(
        height: widget.height,
        child: Row(
          children: [
            getPlayIcon(), //开始和暂停
            getPlayTime(timeStampToStringDate(_progress)), //时间
            Expanded(child: getSliderTheme()), //进度
            getVideoTime(timeStampToStringDate(widget.totalTime!)), //时间
            getFullScreenIcon() //全屏
          ],
        ));

播放Icon和全屏Icon

未传Icon情况下,直接使用默认的Icon,如果传递了,那么直接使用传递的,需要根据播放状态展示播放按钮还是暂停按钮,全屏Icon需要根据是否全屏状态,来展示对应的图标,同时回调点击事件,VipImage是之前封装的图片组件,大家可以查看以往的分享。

  /*
  * 获取播放Icon
  * */
  Widget getPlayIcon() {
    if (widget.videoPlayIcon == null) {
      return InkWell(
        onTap: onPlayClick,
        child: Icon(_isPlayed ? Icons.pause : Icons.play_arrow),
      );
    } else {
      return VipImage(
        _isPlayed ? widget.videoPlayIcon : widget.videoPauseIcon,
        onClick: onPlayClick,
      );
    }
  }


/*
  * 获取全屏Icon
  * */
  Widget getFullScreenIcon() {
    if (widget.videoFullScreenIcon == null) {
      return InkWell(
        onTap: onFullScreenClick,
        child: Icon(_isFullScreen ? Icons.fullscreen_exit : Icons.fullscreen),
      );
    } else {
      return VipImage(
        _isFullScreen
            ? widget.videoFullScreenIcon
            : widget.videoExitFullScreenIcon,
        onClick: onFullScreenClick,
      );
    }
  }

播放时长和总时长

VipText是之前封装的文本组件,大家可以查看以往的分享,需要注意的是传入的时间需要格式化处理,转化为对应的时分秒结构。

  /*
   *获取播放时长
   * */
  Widget getPlayTime(String text) {
    return VipText(
      text,
      style: widget.textStyle,
      marginLeft: widget.playTimeMarginLeft,
      marginRight: widget.playTimeMarginRight,
    );
  }

 /*
   *获取总的播放时长
   * */
  Widget getVideoTime(String text) {
    return VipText(
      text,
      style: widget.textStyle,
      marginLeft: widget.videoTimeMarginLeft,
      marginRight: widget.videoTimeMarginRight,
    );
  }

中间的进度条

进度条使用的是Slider,直接按照原生的Api使用即可,需要注意,最大的进度也就是max,需要和设置的总时长绑定,还有divisions分段,需要以秒作为区分,否则在滑动改变的时候,有可能会和定时造成冲突。

Widget getSliderTheme() {
    var divisions = widget.totalTime! / 1000;
    return SliderTheme(
      data: SliderThemeData(
          //高度
          trackHeight: widget.progressHeight,
          //去掉长按光晕
          overlayColor: Colors.transparent,
          //背景颜色
          inactiveTrackColor: widget.backgroundColor,
          activeTrackColor: widget.progressColor,
          thumbColor: widget.thumbColor,
          thumbShape:
              RoundSliderThumbShape(enabledThumbRadius: widget.thumbRadius)),
      child: Slider(
        min: 0,
        max: widget.totalTime!.toDouble(),
        value: _progress.toDouble(),
        divisions: divisions.toInt(),
        onChangeStart: (progress) {
          if (widget.onVideoChangeStart != null) {
            widget.onVideoChangeStart!(progress.toInt());
          }
        },
        onChangeEnd: (progress) {
          if (widget.onVideoChangeEnd != null) {
            widget.onVideoChangeEnd!(progress.toInt());
          }
          if (_isPlayed) {
            //播放状态下,如果定时,才会执行
            _startTimer();
          }
        },
        onChanged: (double value) {
          setState(() {
            _progress = value.toInt();
          });
          //回调当前进度
          if (widget.onVideoChanged != null) {
            widget.onVideoChanged!(_progress);
          }
        },
      ),
    );
  }

2、时间转换

前边有说过使用的是intl国际化插件,主要用到的是dateFormat.format()。

  /*
  * 时间戳转换时间
  * */
  String timeStampToStringDate(int time) {
    String format = time < 1000 * 60 * 60 ? TimeUtil.m_s : TimeUtil.h_m_s;
    return TimeUtil.getTimeStampToStringDate(time, format: format);
  }


  /*
  * 时间戳转时间
  * */
  static String getTimeStampToStringDate(int timeStamp,
      {String format = y_M_d}) {
    var dateFormat = DateFormat(format);
    var dateTime = DateTime.fromMillisecondsSinceEpoch(timeStamp);
    return dateFormat.format(dateTime);
  }

3、定时操作

定时需要注意的是,在需要定时的时候再开启,比如定义的属性为true时,就执行开启动作,当开启定时后,我们的进度大于总时长时,就需要暂停定时。

开启定时场景:1、定义的属性为true时,进入直接开启定时。2、当点击开始播放按钮时,如果使用定时,就要开启,3、当播放完毕后,再次拖动,也需要开启定时。

暂停定时场景:1、点击暂停视频时,关闭定时,2、播放结束时,关闭定时,3、页面销毁时,也需要关闭定时。

/*
  * 开启定时
  * */
  void _startTimer() {
    if (widget.isTimer! && _timer == null) {
      //开启定时,一秒执行一次
      _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
        if (_progress >= widget.totalTime!) {
          _pauseTimer();
        } else {
          setState(() {
            _progress += 1000;
          });
          widget.onVideoChanged!(_progress);
        }
      });
    }
  }

  /*
  * 暂停定时
  * */
  void _pauseTimer() {
    if (_timer != null) {
      _timer!.cancel(); //取消计时器
      _timer = null;
    }
  }

五、总结及源码地址

源码是一个简单的文件,地址:https://github.com/AbnerMing888/flutter_widget/blob/master/lib/ui/widget/vip_video_controller.dart

源码中有用到之前封装的组件,请大家悉知,目前所封装的组件,样式和图标都是可以更换的,但是有一个就是位置还有组件是否显示没有封装,不过源码已经贴出,大家可以在源码基础之上进行更改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/690246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在ElementUI中如何通过按钮控制输入框的隐藏与显示

点击此按钮会出现两个输入框 当点击完新增后会出现两个输入框及一个按钮&#xff0c;解决此方案思路比较简单&#xff1a;就是设置一个属性值&#xff0c;用v-if进行绑定&#xff0c;在什么情况下需要显示就需要设置v-if绑定的值等于需要显示的值 <div style"height:7…

Istio 熔断 连接池 故障处理

istio熔断 熔断主要是无感的处理服务异常并保证不会发生级联甚至雪崩的服务异常。在微服务方面体现是对异常的服务情况进行快速失败&#xff0c;它对已经调用失败的服务不再会继续调用&#xff0c;如果仍需要调用此异常服务&#xff0c;它将立刻返回失败。 与此同时&#xff0…

c语言修炼第一章--初识c语言(1.3)

目录 前言&#xff1a; 1.关键字 1.1常见关键字: 1.1.1typedef类型重定义 1.2.static 1.2.1static修饰局部变量 1.2.2static修饰全局变量 1.2.3static修饰函数 …

待办事项工具用哪个?每天都在用的待办工具

随着职场“内卷”程度的加深&#xff0c;越来越多的上班族感到自己每天都会面临各种繁忙的日程安排、琐碎的待办事项&#xff0c;并且工作事项和家庭事项交织在一起&#xff0c;需要各方面都兼顾到&#xff0c;这简直让人恨不得长出三头六臂。在待办的事情比较多时&#xff0c;…

艺考之路,很艰难也很幸福

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 嘉宾 / 小川同学 产品统筹 / bobo 录音间 / 声湃轩北京站 本期节目是拼娃时代的第三十期&#xff0c;我们请来了拼娃时代的老朋友&#xff0c;小川同学。 拼娃时代的听友们肯定…

带有10位数码管显示的语音芯片ic,节省30%MCU硬件资源WTV890-B001

随着科技的不断发展&#xff0c;WTV890-B001多功能语音芯片的推出&#xff0c;为工程师带来了极大的便利和产品体验&#xff0c;这款多功能语音芯片具有许多优势&#xff0c;其中WTV890-B001版本中&#xff0c;最引人注目的特点是它带有10位的数码管显示功能&#xff0c;无需再…

nginx配置https证书(nginx-docker版本)

1、下载证书文件 以下两种多可配置 2、nginx server下加上配置 注意&#xff1a;ssl_certificate 参数可为crt文件、或者pem文件。需要将文件docker cp进容器中&#xff0c; ssl_certificate、ssl_certificate_key 为容器内的文件路径。 listen 443 ssl;server_name 域名地…

TMS FlexCel Studio for net 7.17 cRACK

TTMS FlexCel Studio for net 100% 托管代码 Excel 文件操作引擎以及 Excel 和 PDF 报告生成&#xff0c;适用于 .NET、Xamarin.iOS、Xamarin.Android、Xamarin.Mac​​ 功能概述 使用 FlexCel Studio for .NET 创建可动态快速读写 Excel 文件的应用程序、Web 服务和移动设备应…

深入理解浏览器的缓存机制之协商缓存与强缓存

目录 什么是浏览器缓存 浏览器缓存的分类 &#x1f397;️ 强缓存 &#x1f397;️ 协商缓存 &#x1f449;&#x1f3fb; 缓存请求流程 &#x1f449;&#x1f3fb; 为什么要有Etag &#x1f449;&#x1f3fb; 缓存优先级 &#x1f449;&#x1f3fb; 启发式缓存 &a…

Midjourney教程古风人像类

古风图像的特点&#xff1a; 人物发型多为飘逸的长发&#xff0c;或是精致的盘发&#xff1b; 人物服装多为飘逸的长袍、长裙&#xff1b; 整体画风以水墨、水彩、工笔为c主&#xff0c;线条写意&#xff0c;色彩清新淡雅&#xff1b; 背景中多用花鸟、亭台楼阁、桃林等构建氛…

抖音seo矩阵系统源码|需求文档编译说明(三)

文章目录 批量剪辑工具技术源码框架 短视频SEO矩阵系统源码技术开发&#xff0c;遵循步骤抖音矩阵系统源码搭建功能 开发语言及开发环境 抖音账号矩阵系统源码搭建包括以下步骤 概要 ​​抖音seo源码&#xff0c;抖音矩阵系统如何使用源码二次开发&#xff0c;开发的功能有那些…

RT-Thread内核介绍

目录 三、RT-Thread内核介绍 3.1、内核框架 3.1.1、线程调度 3.1.2、时钟管理 3.1.3、线程间同步 3.1.4、线程间通信 3.1.5、内存管理 3.1.6、I/O 设备管理 3.2、RTT内核启动流程 3.2.1、汇编阶段 3.2.2、C阶段 3.2.3、rtthread_startup函数 3.2.4、创建主线程 三、…

jupyter notebook内核挂掉的解决方案

今天在运行notebook时报错 “jupyter notebook内核挂掉了”查找发现是下面这一句出的错&#xff0c;这是pytorch深度学习实战中的一个例程&#xff0c;读取三维体素文件并把其中的一张当作图片显示出来 plt.imshow(vol_arr[50]) 完整代码如下 import torch torch.set_printo…

决策树精讲

一、决策树的构造 决策树的构造是一个递归的过程&#xff0c;有三种情形会导致递归返回&#xff1a;(1)当前结点包含的样本全属于同一类别&#xff0c;这时直接将该结点标记为叶结点&#xff0c;并设为相应的类别&#xff1b;(2)当前属性集为空&#xff0c;或是所有样本在所有…

消息队列选型——为什么选择RabbitMQ

前言 MQ消息是目前分布式系统必不可少的组件&#xff0c;但是面对市面上众多的MQ组件&#xff0c;我们该用什么呢&#xff1f;我以实际项目的需求触发&#xff0c;介绍今天的主角——rabbitMQ。同时也会告知又有哪些优势和不足。事不宜迟&#xff0c;就开始今天的学习吧 目录…

ghost cms配置qq邮箱

背景&#xff1a; 在ghost cms中使用qq邮箱作为注册、登录、订阅等邮件发送方。 1 修改配置 在config.*.json文件中&#xff0c;添加mail配置 "mail": {"from": "xxxxxxqq.com","transport": "SMTP","options"…

Qt6 FFmpeg入门1 - 环境配置

目录 环境配置ffmpeg 下载qt 配套环境配置代码测试 环境配置 文章更新时间&#xff1a;2023/06/24 ffmpeg 下载 由于大部分的关于配置 ffmpegqt 环境的文章都停留在 2021 年&#xff0c;且许多方法均已过时&#xff0c;现在介绍一个最新的方法&#xff0c;并分析槽点供大家参考…

ElementUI在对话框中新增数据,使用rules校验规则进行校验,再点击完修改之后,在进行新增校验会报错

1.新增与修改使用同一个对话框&#xff0c;当修改完成一条数据后&#xff0c;重新打开新增的对话框&#xff0c;通过ElementUI的rules检验会报错 解决方案&#xff1a;在对应的rules校验中每个字段添加trigger: blur 即可解决上述问题,注意&#xff1a;下面的xxx代表需要校验的…

设计模式-05.01-行为型-观察者模板

观察者模式【常用】 我们常把 23 种经典的设计模式分为三类&#xff1a;创建型、结构型、行为型。前面我们已经学习了创建型和结构型&#xff0c;从今天起&#xff0c;我们开始学习行为型设计模式。我们知道&#xff0c;创建型设计模式主要解决“对象的创建”问题&#xff0c;…

工具篇7--RocketMq消息模型介绍

文章目录 前言&#xff1a;一、RocketMq是什么&#xff1f;二、RocketMq 模型介绍&#xff1a;1.RocketMq 模型图&#xff1a;2.RocketMq 生产者&#xff1a;2.1 生产者消费发送流程&#xff1a;2.2 生产者消息发送&#xff1a;2.2.1 同步发送普通消息&#xff1a;2.2.1 异步发…