Flutter控件封装之视频进度条

2023-10-27

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

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter控件封装之视频进度条 的相关文章

  • 如何设置itemTouchHelper选择性滑动?

    所以我在 recyclerview rv 上实现了这个 itemtouchhelper simple 回调 现在 在这个 rv 中 我根据内容类型将 2 种布局设置为一行 因此 当我在 rv 上设置此 touchhelper 时 它正在这两
  • Android ACTION_DATE_CHANGED 广播

    我有 Nexus S 当我在手机上手动更改日期时 ACTION DATE CHANGED 并不总是被广播 如果我将日期从 2014 年 2 月 13 日更改为 2014 年 2 月 14 日 我还没有获得 ACTION DATE CHANG
  • 可以混淆/加密 SharedPreferences 文件吗?

    因此 我对混淆应用程序的 SharedPreferences xml 文件很感兴趣 就像 Android LVL 混淆其许可证 cahce 数据一样 这是可以想象的吗 大量的谷歌挖掘几乎没有产生任何可能解决我的问题的结果 我当然不是密码学家
  • 将文本视图包裹在图像视图周围

    晚上好 我正在尝试将文本包裹在图像视图中 就像这里提出的问题一样 Textview 环绕 View https stackoverflow com questions 3626750 textview wrap around view 在风
  • 如何防止 Activity 在后退操作时重新加载

    我有连接到互联网以获取数据的应用程序 我可以多层次访问数据 假设我从第 3 级开始 在第 4 级我决定返回 每当我按回之前的活动时 就会从互联网重新加载数据 有可能阻止这种情况吗 我尝试以单顶模式运行该活动 将数据加载代码移至 single
  • 如何在flutter中从设备存储读取CSV文件

    我想将数据从 flutter 中的 CSV 文件导入到 firebase 数据库中 因此 我使用文件选择器从设备中选择 CSV 文件 现在我如何从该文件中读取数据 首先从 dart 包导入 file picker 和 CSV 包 比定义方法
  • 如何区分ActionBarSherlock中的两次菜单项点击?

    我最近一直在使用 ActionBarSherlock 并按照各种教程 我编写了这段代码来将项目添加到操作栏 Override public boolean onCreateOptionsMenu Menu menu menu add Ref
  • 在 doInBackground 方法中启动活动

    在下面的代码中 我从互联网下载 json 并希望显示在列表中 如果列表为空 则转到另一个活动 但其他活动未启动 没有错误 但没有启动活动 感谢您的帮助 package ir mohammadi android nightly import
  • 如何从另一个应用程序向一个应用程序添加视图

    我的应用程序叫做我的好应用 MyNiceApp 主要只是一个加载视图的核心coreView在主活动中onCreate coreView由用户根据需要下载的其他插件的视图填充 我定义了核心视图上的各个区域 这些区域可以通过 MyNiceApp
  • 无法解析配置“:app:debugRuntimeClasspath”的所有文件。问题

    我的 android studio 遇到了下一个问题 导致 org gradle api internal artifacts ivyservice DefaultLenientConfiguration ArtifactResolveEx
  • Droid 3 上的列表视图背景为灰色

    我有一个带有自定义背景的列表框 它在黑色背景的两侧显示一条细白线 在我所有的测试手机 Galaxy Captivate Vibrant Nexus 1 G Tablet Archos 32 Droid 上运行良好 我刚买了一台 Droid
  • EditText 不显示当前输入(Android 4)

    我的 Android 应用程序包含一个EditText http developer android com reference android widget EditText html查看可以在其中键入一些短消息 单行 按键盘的DONE键
  • Android Youtube API 可用吗? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有适用于 Android 的 YouTube API 吗 如果不是 除了通过网络浏览器之外 如何从 Yo
  • Android 如何从我的应用程序使用 SD 卡中的文件路径预览图像

    文件存在于sdcard image jpg我想创建我自己的应用程序 活动 按下按钮时 需要使用内置图像查看器显示存储在 SD 卡中的图像 按图像查看器中的后退按钮后 它应该返回到我正在运行的应用程序 需要一些帮助 您可以为此创建一个具有适当
  • 如何将 currentTimeMillis 转换为可读的日期格式? [复制]

    这个问题在这里已经有答案了 我想用currentTimeMillis两次 这样我就可以计算持续时间 但我也想以用户可读的格式显示时间和日期 我遇到了麻烦currentTimeMillis有利于计算 但我看不到内置函数可以转换为合适的时间或时
  • 如何在Webview中保存用户名和密码

    目前 我还在学习Android开发的过程中 所以如果我的这个问题对你来说不太容易理解 请原谅 我创建了一个 Android 应用程序 它使用 RecyclerView 显示一组列表 当用户单击列表中的每个名称时 它会将它们重定向到一组不同的
  • Android - 保持用户登录状态

    我正在尝试使用 PHP 和 MySQLi for Android 进行登录 我不明白的是如何保持用户登录状态 我看到一个简单的教程 其中有人使用 SQLite 来保护信息 但我不知道这是否真的安全 如何保存用户信息以保持用户登录状态 谢谢
  • Android Windows:它们何时以及如何创建?

    我已经阅读了标准的 Windows 相关文档并翻阅了 一堆源代码 试图理解 Android 如何以及何时 窗口已创建 我相信我已经拥抱它并愿意 对其进行验证或更正 据我所知 只有两种方法可以获得 Window 对象的句柄 1 Activit
  • 动画结束后更改视图位置

    我开发了一个基于ViewGroup我的问题是我需要在动画结束后保存项目的位置 我打了电话setFillAfter true 在我创建的动画对象中AnimationListener并在其中onAnimationEnd方法调用View layo
  • Android进程调度

    我试图更好地理解 以便在创建 Android 应用程序 服务时确定潜在的互操作性问题对可靠性的影响 我想弄清楚进程优先级是如何确定的 服务和活动之间优先级的差异以及调度程序是否以不同方式对待它们的优先级 基本上 我试图深入了解某个活动或服务

随机推荐

  • Window查看apache的版本

    我使用的是xampp进入shell命令界面的 1 点击shell 进入 2 直接输入命令 httpd v 就可以看到你电脑平时使用的Apache版本了
  • 在线Qt查看源码网站

    Woboq Code Browser Explore C code on the web
  • Tomcat Manager 账号密码设置

    Windows版本 下载https tomcat apache org 我选择Tomcat9 可以查看包信息 详细信息介绍 所以Windows版本下载 我这里先下载了9 0 37 所以没有下载 解压以后 启动bin startup bat
  • 3-排序算法

    冒泡排序 冒泡排序的思路是将每两个数据之间进行大小比对 将大的数据后移 反复比对移动数据 直至数组排列整齐 include
  • Vosviewer+Pajek实现知网社交网络(解决中文乱码问题)

    Vosviewer Pajek实现知网社交网络 一 软件准备 Vosviewer安装 下载地址https www vosviewer com download 安装JDKhttps www oracle com java technolog
  • Python+Selenium_UI自动化操作(3)——刷新页面

    UI自动化 刷新页面 语法 refresh class TestRefreshWeb unittest TestCase def setUp self setUp是一个初始化方法 为test案例做数据准备 当前方法的数据准备动作是 启动ch
  • MQTT通讯协议简介和测试 [MQTT.fx]

    一 介绍 1 MQTT简介 MQTT Message Queuing Telemetry Transport 消息队列遥测传输 是IBM开发的一个即时通讯协议 有可能成为物联网的重要组成部分 该协议支持所有平台 几乎可以把所有联网物品和外部
  • 使用 POI创建一个简单的 Excel 文件

    初级程序员一枚 看到公司大佬写的生成Excel文件 做下记录 同时也分享给大家参考 实现原理是用workBook 然后还有OutputStream 首先是一个ExcelDataBean 这个实体类 用来 声明 Excel的最大行数 最大列数
  • 用tornado ,Supervisord ,nginx架网站

    最近使用 Tornado 重写了博客 于是查看了很多关于部署基于 Tornado 开发的网站的资料 比较成熟的方案就是使用 Nginx 来做反向代理 使用 Supervisord 来作为进程管理工具 至于什么叫反向代理 为什么 Tornad
  • 小鱼深度产品测评之:阿里云低代码开发平台魔笔,一站式的应用全生命周期管理,高效解决应用研发、迭代、运维的问题。

    低代码开发平台魔笔测评 1 引言 2 购买流程 3 魔笔 3 1添加应用 3 2 应用列表 3 1 1 列表应用展示 3 2 1 列应用操作 3 2 1 1 自动保存 3 2 1 2 复制功能 3 2 1 3 编辑功能 3 2 1 4 删除
  • (二)Android导航栏和菜单资源的结合使用

    ActionBar是Android3 0的重要更新之一 位于传统标题栏的位置 1 注意在使用ActionBar时保证该应用的目标版本应高于11 Android3 0的版本号
  • 知乎Python大佬带你10分钟入门Python爬虫(推荐收藏)

    一 基础入门 1 1 什么是爬虫 爬虫 spider 又网络爬虫 是指向网站 网络发起请求 获取资源后分析并提取有用数据的程序 从技术层面来说就是 通过程序模拟浏览器请求站点的行为 把站点返回的HTML代码 JSON数据 二进制数据 图片
  • 《软件调试的艺术》笔记--调试多线程程序

    下面是于线程相关的GDB命令用法汇总 info threads 给出关于当前所有线程的信息 thread 3 改成线程3 break 88 thread 3 当线程到达源代码88时停止执行 break 88 thread 3 if i 2
  • LaTeX教程(三)——文档格式排版

    文章目录 1 章节目录 1 1 生成章节 1 2 生成目录 2 交叉引用和脚注 2 1 交叉引用 2 2 脚注 3 特殊环境 3 1 列表 3 2 文本对齐 3 3 引用环境 3 4 代码环境 1 章节目录 1 1 生成章节 写文章或者论文
  • 关于程序员认知和编程学习,没有任何一篇文章会讲得如此透彻

    程序猿问科比 你为什么这么成功 科比 你知道洛杉矶凌晨四点是什么样子吗 程序猿 知道 一般那个时候我还在写代码 怎么了 科比 额 你以为程序员都是这样生活的 别瞎 他们只是在历劫 度过这段日子 他们就飞升上仙 乃至上神 过上另一种生活 就像
  • 【北京工业大学申请个人学生邮箱】

    北京工业大学申请个人学生邮箱 由于近期想要购买苹果设备 但是苹果更新了unidays认证 需要用到学生邮箱认证 因此花了几个小时查找北工大的邮箱注册方法 一趟下来 说实在的流程有些阴间 在此进行下记录 也方便后人的查找 一 进入 内网综合服
  • 商品上架、es应用到商品上架-35

    一 商品上架 上架的商品才可以在网站展示 上架的商品需要可以被检索 es是将数据保存到内存当中 所以我们不能将什么数据都保存到es当中 我们需要将重要的数据保存到es中 例如商品名称 规格型号 价格等信息 当需要的数据较多时 我们可以将主键
  • 我的博客地图

    最新更新时间 2020年03月12日11 37 13 猛戳 查看我的博客地图 总有你意想不到的惊喜 我的博客首页 我的博客创作中心 前端漫漫路 我的GitHub 我的站点 前言 由于自己写的文章越来越多 查看和查找具体内容变得格外耗时 因此
  • 【算法学习笔记】26:匈牙利算法(二分图最大匹配)

    1 简述 给定一个二分图 例如 匈牙利算法能够快速的计算出一种匹配方式 使得匹配的数量最多 注意 一个成功的匹配方式中 没有两条边是共用了同一个点的 形象的说 这个问题可以理解成二分图两边分别是男生和女生 有连线的表示可以凑成一对 匈牙利算
  • Flutter控件封装之视频进度条

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