Flutter控件封装之视频进度条

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

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

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

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,用于占有剩余的空间。


returnSizedBox(
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* */WidgetgetPlayIcon() {
if (widget.videoPlayIcon==null) {
returnInkWell(
onTap: onPlayClick,
child: Icon(_isPlayed?Icons.pause : Icons.play_arrow),
      );
    } else {
returnVipImage(
_isPlayed?widget.videoPlayIcon : widget.videoPauseIcon,
onClick: onPlayClick,
      );
    }
  }
/** 获取全屏Icon* */WidgetgetFullScreenIcon() {
if (widget.videoFullScreenIcon==null) {
returnInkWell(
onTap: onFullScreenClick,
child: Icon(_isFullScreen?Icons.fullscreen_exit : Icons.fullscreen),
      );
    } else {
returnVipImage(
_isFullScreen?widget.videoFullScreenIcon            : widget.videoExitFullScreenIcon,
onClick: onFullScreenClick,
      );
    }
  }

播放时长和总时长


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


/**获取播放时长* */WidgetgetPlayTime(Stringtext) {
returnVipText(
text,
style: widget.textStyle,
marginLeft: widget.playTimeMarginLeft,
marginRight: widget.playTimeMarginRight,
    );
  }
/**获取总的播放时长* */WidgetgetVideoTime(Stringtext) {
returnVipText(
text,
style: widget.textStyle,
marginLeft: widget.videoTimeMarginLeft,
marginRight: widget.videoTimeMarginRight,
    );
  }

中间的进度条


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


WidgetgetSliderTheme() {
vardivisions=widget.totalTime!/1000;
returnSliderTheme(
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: (doublevalue) {
setState(() {
_progress=value.toInt();
          });
//回调当前进度if (widget.onVideoChanged!=null) {
widget.onVideoChanged!(_progress);
          }
        },
      ),
    );
  }

2、时间转换


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


/** 时间戳转换时间* */StringtimeStampToStringDate(inttime) {
Stringformat=time<1000*60*60?TimeUtil.m_s : TimeUtil.h_m_s;
returnTimeUtil.getTimeStampToStringDate(time, format: format);
  }
/** 时间戳转时间* */staticStringgetTimeStampToStringDate(inttimeStamp,
      {Stringformat=y_M_d}) {
vardateFormat=DateFormat(format);
vardateTime=DateTime.fromMillisecondsSinceEpoch(timeStamp);
returndateFormat.format(dateTime);
  }

3、定时操作


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


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


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


/** 开启定时* */void_startTimer() {
if (widget.isTimer!&&_timer==null) {
//开启定时,一秒执行一次_timer=Timer.periodic(constDuration(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


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

相关文章
|
3月前
|
Android开发
Flutter控件的显示与隐藏
Flutter控件的显示与隐藏
152 3
|
18天前
|
存储 缓存 Dart
Flutter&鸿蒙next 封装 Dio 网络请求详解:登录身份验证与免登录缓存
本文详细介绍了如何在 Flutter 中使用 Dio 封装网络请求,实现用户登录身份验证及免登录缓存功能。首先在 `pubspec.yaml` 中添加 Dio 和 `shared_preferences` 依赖,然后创建 `NetworkService` 类封装 Dio 的功能,包括请求拦截、响应拦截、Token 存储和登录请求。最后,通过一个登录界面示例展示了如何在实际应用中使用 `NetworkService` 进行身份验证。希望本文能帮助你在 Flutter 中更好地处理网络请求和用户认证。
135 1
|
18天前
|
Dart UED 开发者
Flutter&鸿蒙next中的按钮封装:自定义样式与交互
在Flutter应用开发中,按钮是用户界面的重要组成部分。Flutter提供了多种内置按钮组件,但有时这些样式无法满足特定设计需求。因此,封装一个自定义按钮组件变得尤为重要。自定义按钮组件可以确保应用中所有按钮的一致性、可维护性和可扩展性,同时提供更高的灵活性,支持自定义颜色、形状和点击事件。本文介绍了如何创建一个名为CustomButton的自定义按钮组件,并详细说明了其样式、形状、颜色和点击事件的处理方法。
68 1
|
18天前
|
开发工具 UED
Flutter&鸿蒙next中封装一个输入框组件
本文介绍了如何创建一个简单的Flutter播客应用。首先,通过`flutter create`命令创建项目;接着,在`lib`目录下封装一个自定义输入框组件`CustomInput`;然后,在主应用文件`main.dart`中使用该输入框组件,实现简单的UI布局和功能;最后,通过`flutter run`启动应用。本文还提供了后续扩展建议,如状态管理、网络请求和UI优化。
95 1
|
20天前
|
存储 缓存 JavaScript
Flutter 学习之封装 WebView
【10月更文挑战第24天】通过以上的探讨,我们可以看出,在 Flutter 中封装 WebView 是非常有必要的,它可以提高代码的复用性、增强可维护性、提供统一接口。在实际应用中,我们需要根据具体的需求和场景,选择合适的封装方法和技术,以实现更好的效果。
|
17天前
|
开发工具
Flutter&鸿蒙next中封装一个列表组件
Flutter&鸿蒙next中封装一个列表组件
31 0
|
18天前
|
Dart 安全 UED
Flutter&鸿蒙next中的表单封装:提升开发效率与用户体验
在移动应用开发中,表单是用户与应用交互的重要界面。本文介绍了如何在Flutter中封装表单,以提升开发效率和用户体验。通过代码复用、集中管理和一致性的优势,封装表单组件可以简化开发流程。文章详细讲解了Flutter表单的基础、封装方法和表单验证技巧,帮助开发者构建健壮且用户友好的应用。
60 0
|
4月前
|
JSON Dart API
Flutter dio http 封装指南说明
本文介绍了如何实现一个通用、可重构的 Dio 基础类,包括单例访问、日志记录、常见操作封装以及请求、输出、报错拦截等功能。
111 2
Flutter dio http 封装指南说明
|
3月前
|
开发者 监控 开发工具
如何将JSF应用送上云端?揭秘在Google Cloud Platform上部署JSF应用的神秘步骤
【8月更文挑战第31天】本文详细介绍如何在Google Cloud Platform (GCP) 上部署JavaServer Faces (JSF) 应用。首先,确保已准备好JSF应用并通过Maven构建WAR包。接着,使用Google Cloud SDK登录并配置GCP环境。然后,创建`app.yaml`文件以配置Google App Engine,并使用`gcloud app deploy`命令完成部署。最后,通过`gcloud app browse`访问应用,并利用GCP的监控和日志服务进行管理和故障排查。整个过程简单高效,帮助开发者轻松部署和管理JSF应用。
60 0
|
3月前
|
JSON Android开发 数据格式
Flutter笔记:美工设计.导出视频到RIVE
Flutter笔记:美工设计.导出视频到RIVE
57 0