Video controller, the style provided by the three parties, sometimes it is difficult to meet our needs, for this situation, we have to encapsulate ourselves on this basis, the article shared today is a very simple controller encapsulation case, including the basic playback pause, full screen and exit full screen, as well as time and progress display, encapsulated event callbacks and the control of various properties, basically can meet most of the business needs, even if not satisfied, you can also expand on this basis.
Let's still follow the convention and briefly list an outline:
1. Basic effect display
2. Specific use and introduction of related attributes
3. Controller packaging considerations
4. Analysis of the functional code of the controller part
5. Summary and source code address
First, the basic effect display
The specific effects, there is nothing to say, are the common styles of the public, from left to right: playback pause button, playback time, playback progress, total time, full screen and exit full screen button.
The functions that can be realized are, the dynamic setting of the icon, the color and size control of the time progress, and the opening of the timer, you can see the second item for details.
2. Introduction to specific use and related attributes
1. Specific use
As a widget, you can use it at will, alone or in combination with a video player.
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. Related attributes
attribute | type | overview |
height | double | Set the controller height |
progressHeight | double | Progress bar height |
videoPlayIcon | String | Video playback Icon |
videoPauseIcon | String | Video Pause Icon |
videoFullScreenIcon | String | Video full-screen icon |
videoExitFullScreenIcon | String | Exit the full-screen Icon |
textStyle | TextStyle | Text style |
backgroundColor | Color | Background color |
progressColor | Color | Progress color |
thumbColor | Color | Drag the color |
thumbRadius | double | Thumb size |
playTimeMarginLeft | double | The distance from the left of the playback time |
playTimeMarginRight | double | The distance from the left of the playback time |
videoTimeMarginLeft | double | The distance from the left of the video time |
videoTimeMarginRight | double | The distance from the left of the video time |
totalTime | int | Total duration |
changeTime | int | Change the duration |
isTimer | bool | Whether timing is required |
onVideoPlayClick | ValueChanged<bool> | Video playback clicks |
onVideoFullScreenClick | ValueChanged<bool> | Click Click to click the video in full screen |
onVideoChanged | ValueChanged<int> | Slide callbacks |
onVideoChangeStart | ValueChanged<int> | Drag to start |
onVideoChangeEnd | ValueChanged<int> | End of dragging |
isPlayed | bool | Play controls status, pause or start |
isFullScreen | bool | Whether it is full screen |
Third, controller packaging considerations
Although the video controller is simple, there are still many factors to consider, such as click play and pause, full-screen and exit full-screen event callbacks, drag progress in addition to changing its own progress but also change the time progress, time conversion of transmission, timing on and off, etc. are all needs to be solved.
1. Basic UI settings
The UI of the controller must be based on the UI draft set by the design students, otherwise it is difficult to drive design changes with technology, but there are special cases. So when encapsulating, it is either based on the UI draft or dynamically configurable, changing the basic style or position through properties.
2. Implementation of drag progress
The drag progress is relatively simple, using the native provided Slider, that is, the slider, similar to SeekBar in Android, it should be noted that the dynamic configuration of attributes such as color.
3. Time conversion and progress binding
Time conversion, need to convert the incoming timestamp, into the time format we need, that is, the format of hours, minutes and seconds, here the use of intl internationalization plug-in, mainly used to format conversion DateFormat.
4. Control of timer
The timer is very simple, instantiate a timer can be, however, when to start, when to pause are we need to consider, in general, directly and the video player to bind, directly change the progress can be, do not use this timing, if you want to use, you can use a property control; In the case of timekeeping, click pause, you need to pause the timing, in addition, you also need to pause the timing after playing; When the drag is completed, you need to turn on the timing, click play, and also need to turn on the timing, so for the timer control block, be clear.
Fourth, the controller part of the function code analysis
1. Basic layout
Quite simply, a horizontal component Row, wrapped 5 sub-components, and the progress bar uses Expanded to occupy the remaining space.
return SizedBox(
height: widget.height,
child: Row(
children: [
getPlayIcon(), //开始和暂停
getPlayTime(timeStampToStringDate(_progress)), //时间
Expanded(child: getSliderTheme()), //进度
getVideoTime(timeStampToStringDate(widget.totalTime!)), //时间
getFullScreenIcon() //全屏
],
));
Play Icon and Full Screen Icon
In the case of not uploading an icon, directly use the default icon, if passed, then directly use the passed, you need to show the play button or pause button according to the playback status, the full-screen Icon needs to show the corresponding icon according to whether the full-screen state, and at the same time call back click event, VipImage is a previously encapsulated picture component, you can view the previous sharing.
/*
* 获取播放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,
);
}
}
Duration and total duration
VipText is a previously encapsulated text component, you can view the past sharing, it should be noted that the incoming time needs to be formatted and converted into the corresponding hour, minute and second structure.
/*
*获取播放时长
* */
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,
);
}
Progress bar in the middle
The progress bar uses Slider, which can be used directly according to the native API, it should be noted that the maximum progress is max, which needs to be bound to the total duration of the setting, and divisions segmentation, which needs to be distinguished by seconds, otherwise when the slide changes, it may cause conflicts with the timing.
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. Time conversion
As mentioned earlier, the intl internationalization plugin is used, and the main use is 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. Timing operation
Timing needs to be noted that when the timing needs to be turned on, such as when the defined attribute is true, the opening action is executed, and when the timing is turned on, our progress is greater than the total duration, we need to pause the timing.
Enable timing scenarios: 1. When the defined attribute is true, enter to directly enable timing. 2. When you click the start playback button, if you use the timing, you need to turn it on, 3. When the playback is finished, drag it again, you also need to turn on the timing.
Pause the timing scenario: 1. Turn off the timer when you click to pause the video, 2. Turn off the timer when the playback ends, 3. When the page is destroyed, you also need to turn off the timing.
/*
* 开启定时
* */
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;
}
}
5. Summary and source code address
The source code is a simple file with an address: https://github.com/AbnerMing888/flutter_widget/blob/master/lib/ui/widget/vip_video_controller.dart
The source code has the previously packaged components, please know that the currently packaged components, styles and icons can be replaced, but there is a position and whether the components show that there is no encapsulation, but the source code has been posted, you can change it on the basis of the source code.