当前位置:网站首页>Fluent 2 Advanced (IX): fijkplayer plays videos and card effects
Fluent 2 Advanced (IX): fijkplayer plays videos and card effects
2022-07-22 20:06:00 【Wu Qingsen】
Use FijkPlayer Play the video , Functions included : Brightness adjustment , Volume adjustment , Long press 2 Speed up , Double click pause / Start , Play full screen . Approximate renderings :
Download link :
CSDN :flutter_blbl.zip-Android Document resources -CSDN download
GitHub:https://github.com/wuqingsen/FlutterLearnDemo
The plug-ins used , Grid layout , Image load cache fade effect , Video player , Volume and brightness controller :
flutter_staggered_grid_view: ^0.4.1
transparent_image: ^2.0.0
cached_network_image: ^2.5.0
fijkplayer: ^0.10.1
volume_controller: ^2.0.2
screen_brightness: ^0.0.2
Card effect :
Encapsulates a video_card.dart :
import 'package:flutter/material.dart';
import 'package:flutter_blbl/model/base/stateful_widget_base.dart';
import 'package:flutter_blbl/model/bean/home_page_bean.dart';
import 'package:flutter_blbl/model/navigator/my_navigator.dart';
import 'package:flutter_blbl/utils/view_util.dart';
class VideoCard extends BaseWidget {
final HomePageBean homePageBean;
VideoCard({this.homePageBean});
@override
_VideoCard createState() => _VideoCard();
@override
BaseWidgetState getState() {
return _VideoCard();
}
}
class _VideoCard extends BaseWidgetState<VideoCard> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
MyNavigator.getInstance().onJumpTo(RouteStatus.detail,
args: {"videoMo": widget.homePageBean});
},
child: SizedBox(
height: 200,
child: Card(
margin: EdgeInsets.only(left: 4, right: 4, bottom: 8),
// Shadow color
color: Colors.white,
// Shadow height
elevation: 1.0,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5)),
child: Column(
children: [
_itemImage(context),
_infoText(),
],
),
),
),
));
}
_itemImage(BuildContext context) {
final size = MediaQuery.of(context).size;
return Stack(
children: [
cachedImage(
widget.homePageBean.imageurl,
height: 120,
width: size.width / 2 - 20,
),
//left: 0, right: 0, bottom: 0 Below and left and right filled
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
padding: EdgeInsets.only(left: 8, right: 8, bottom: 3, top: 5),
// Linear gradient
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [Colors.black54, Colors.transparent]),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_iconText(Icons.ondemand_video, widget.homePageBean.playnum),
_iconText(Icons.favorite_border, widget.homePageBean.playnum),
_iconText(null, widget.homePageBean.videoLength),
],
),
))
],
);
}
_iconText(IconData iconData, String playnum) {
return Row(
children: [
if (iconData != null)
Icon(
iconData,
color: Colors.white,
size: 12,
),
Padding(
padding: EdgeInsets.only(left: 3, bottom: 3),
child: Text(
playnum,
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
],
);
}
_infoText() {
return Expanded(
child: Container(
padding: EdgeInsets.only(top: 5, left: 8, right: 8, bottom: 5),
child: Column(
// Show... From the beginning
crossAxisAlignment: CrossAxisAlignment.start,
// Up and down display
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// There are more than two lines of ellipsis
Text(
widget.homePageBean.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 12, color: Colors.black87),
),
_owner(),
],
),
));
}
_owner() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Padding(
padding: EdgeInsets.only(right: 8),
child: Text(
"up",
style: TextStyle(fontSize: 10, color: Colors.grey),
),
),
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Text(
widget.homePageBean.up,
style: TextStyle(fontSize: 10, color: Colors.grey),
),
),
],
),
Icon(
Icons.more_vert_sharp,
size: 15,
color: Colors.grey,
)
],
);
}
}
Video player
This is changed on the basis of others , Then directly use , Take a look at the packaging and use of :
video_card.dart Code :
import 'dart:async';
import 'dart:math';
import 'package:fijkplayer/fijkplayer.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_blbl/utils/color.dart';
import 'package:flutter_blbl/utils/toast.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:volume_controller/volume_controller.dart';
import 'fijk_controller.dart';
class CustomFijkPanel extends StatefulWidget {
final FijkPlayer player;
final BuildContext buildContext;
final Size viewSize;
final Rect texturePos;
final String videoTitle;
final bool isNextNumber;
final bool isPlayAd;
final void Function() onPlayAd;
final void Function() onBack;
final void Function() onError;
final void Function() onVideoEnd;
final void Function() onVideoPrepared;
final void Function() onVideoTimeChange;
/// Player controller specific to the source code directory View reference fijkplayer\lib\ui\panel.dart
/// ```
/// @param {FijkPlayer} player -
/// @param {BuildContext} buildContext -
/// @param {Size} viewSize -
/// @param {Rect} texturePos -
/// @param {String} videoTitle -
/// @param {bool} isNextNumber - Whether to display the next episode button after full screen
/// @param {bool} isPlayAd - Whether to display the advertisement button
/// @param {void Function()?} onPlayAd - Play ads
/// @param {void Function()?} onBack - Return button
/// @param {void Function()?} onError - Video error click refresh
/// @param {void Function()?} onVideoEnd - End of video
/// @param {void Function()?} onVideoPrepared - The video completes the background task to the stable period
/// @param {void Function()?} onVideoTimeChange - Video time update
/// ```
const CustomFijkPanel({
@required this.player,
@required this.buildContext,
@required this.viewSize,
@required this.texturePos,
@required this.videoTitle,
this.isNextNumber = false,
this.isPlayAd = false,
this.onPlayAd,
this.onBack,
this.onError,
this.onVideoEnd,
this.onVideoPrepared,
this.onVideoTimeChange,
});
@override
State<StatefulWidget> createState() {
return _CustomFijkPanelState();
}
}
class _CustomFijkPanelState extends State<CustomFijkPanel> {
FijkPlayer get player => widget.player;
bool get isFullScreen => player.value.fullScreen;
/// Total time
Duration _duration = Duration();
/// Animation time
Duration get _animatedTime => Duration(milliseconds: 400);
/// Whether playing
bool _playing = false;
/// Whether the background task is preliminarily executed and completed is used for the state of loading
bool _prepared = false;
/// Whether the video state has been executed and become a stable state and _prepared atypism
bool _playStatePrepared = false;
bool _isPlayCompleted = false;
/// Whether it is loading
bool _buffering = false;
int _bufferingPro = 0;
StreamSubscription _bufferingSubs;
/// Drag the fast forward time -1 No display
double _seekPos = -1;
/// current time
Duration _currentPos = Duration();
StreamSubscription _currentPosSubs;
/// Preload time
Duration _bufferPos = Duration();
StreamSubscription _bufferPosSubs;
StreamSubscription<int> _bufferPercunt;
/// Controller hidden
Timer _hideTimer;
bool _hideStuff = true;
/// Video error
bool _isPlayError = false;
/// voice 0-1 Range
double _currentVolume = 0;
bool _showVolume = false;
/// Screen brightness 0-1 Range
double _currentBrightness = 0;
bool _showBrightness = false;
int sendCount = 0;
/// Double speed playback
bool isShowDouble = false;
@override
void initState() {
super.initState();
_duration = player.value.duration;
_currentPos = player.currentPos;
_bufferPos = player.bufferPos;
_prepared = player.value.prepared;
var playerState = player.state;
_playing = playerState == FijkState.started;
_isPlayError = playerState == FijkState.error;
_isPlayCompleted = playerState == FijkState.completed;
_playStatePrepared = playerState == FijkState.prepared;
_buffering = player.isBuffering;
initScreenBrightness();
// FijkVolume.setUIMode(FijkVolume.hideUIWhenPlayable);
VolumeController().getVolume().then((volume) {
print(" Multimedia sound $volume");
_currentVolume = volume;
});
/// Because the change is too small to monitor the adjustment of basic monitoring physical keys
VolumeController().listener((volume) {
print(" Multimedia sound changes $volume");
_currentVolume = volume;
});
player.addListener(_playerValueChanged);
/// Current progress
_currentPosSubs = player.onCurrentPosUpdate.listen((value) {
setState(() {
_currentPos = value;
if (_buffering == true) {
_buffering = false; // Avoid the possibility that it is still in the display buffer when it has been played
}
if (_playing == false) {
_playing = true; // Avoid playing in false Caused by bug
}
});
// Every time n Only enter once every time, otherwise it will increase consumption if it is sent too frequently and the processing business is too complex
if (sendCount % 50 == 0) {
widget.onVideoTimeChange?.call();
}
sendCount++;
});
/// Video preload progress
_bufferPosSubs = player.onBufferPosUpdate.listen((value) {
setState(() {
_bufferPos = value;
});
});
/// Video Caton callback
_bufferingSubs = player.onBufferStateUpdate.listen((value) {
print(" Video loading $value");
if (value == false && _playing == false) {
_playOrPause();
}
setState(() {
_buffering = value;
});
});
/// Video Caton when buffer callback
_bufferPercunt = player.onBufferPercentUpdate.listen((value) {
setState(() {
_bufferingPro = value;
});
});
}
@override
void dispose() {
player.removeListener(_playerValueChanged);
VolumeController().removeListener();
_hideTimer?.cancel();
_currentPosSubs.cancel();
_bufferPosSubs.cancel();
_bufferingSubs.cancel();
_bufferPercunt.cancel();
ScreenBrightness.resetScreenBrightness().catchError((error) {
print(" Reset brightness - abnormal $error");
});
super.dispose();
}
Future<void> initScreenBrightness() async {
double _brightness = 0.5;
try {
_brightness = await ScreenBrightness.initial;
// print(" Get screen brightness $_brightness");
} catch (error) {
print(" Get screen brightness - abnormal $error");
}
setState(() {
_currentBrightness = _brightness;
});
}
void _playerValueChanged() {
var value = player.value;
if (value.duration != _duration) {
setState(() {
_duration = value.duration;
});
}
var valueState = value.state;
var playing = (valueState == FijkState.started);
var prepared = value.prepared;
var isPlayError = valueState == FijkState.error;
var completed = valueState == FijkState.completed;
if (isPlayError != _isPlayError ||
playing != _playing ||
prepared != _prepared ||
completed != _isPlayCompleted) {
setState(() {
_isPlayError = isPlayError;
_playing = playing;
_prepared = prepared;
_isPlayCompleted = completed;
});
}
/// [value.prepared] It's not equal to [playStatePrepared] So judge alone
bool playStatePrepared = valueState == FijkState.prepared;
if (_playStatePrepared != playStatePrepared) {
if (playStatePrepared) {
widget.onVideoPrepared?.call();
}
_playStatePrepared = playStatePrepared;
}
bool isPlayCompleted = valueState == FijkState.completed;
if (isPlayCompleted) {
print(" Whether there is another episode after the video status ends ${widget.isNextNumber}");
if (widget.isNextNumber) {
widget.onVideoEnd?.call();
} else {
_isPlayCompleted = isPlayCompleted;
}
}
}
/// Play begins
void _playOrPause() {
if (_playing == true) {
player.pause();
} else {
player.start();
}
}
void _startHideTimer() {
_hideTimer?.cancel();
_hideTimer = Timer(const Duration(seconds: 10), () {
setState(() {
_hideStuff = true;
});
});
}
/// Controller display hidden
void _cancelAndRestartTimer() {
if (_hideStuff == true) {
_startHideTimer();
}
setState(() {
_hideStuff = !_hideStuff;
});
}
/// Time conversion display
String _duration2String(Duration duration) {
if (duration.inMilliseconds < 0) {
return "00:00";
}
String twoDigits(int n) {
if (n >= 10) {
return "$n";
} else {
return "0$n";
}
}
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
int inHours = duration.inHours;
if (inHours > 0) {
return "$inHours:$twoDigitMinutes:$twoDigitSeconds";
} else {
return "$twoDigitMinutes:$twoDigitSeconds";
}
}
/// Fast forward video time
void _onVideoTimeChangeUpdate(double value) {
if (_duration.inMilliseconds < 0 ||
value < 0 ||
value > _duration.inMilliseconds) {
return;
}
_startHideTimer();
setState(() {
_seekPos = value;
});
}
/// Fast forward video release start jump time
void _onVideoTimeChangeEnd(double value) {
var time = _seekPos.toInt();
_currentPos = Duration(milliseconds: time);
print(" Jump time $time ${_duration.inMilliseconds}");
player.seekTo(time).then((value) {
if (!_playing) {
player.start();
}
});
setState(() {
_seekPos = -1;
});
}
/// Get the current time of the video , If you drag the fast forward time, the fast forward time will be displayed
double getCurrentVideoValue() {
double duration = _duration.inMilliseconds.toDouble();
double currentValue;
if (_seekPos > 0) {
currentValue = _seekPos;
} else {
currentValue = _currentPos.inMilliseconds.toDouble();
}
currentValue = min(currentValue, duration);
currentValue = max(currentValue, 0);
return currentValue;
}
/// Top bar
Widget _buildTopmBar() {
return Stack(
children: <Widget>[
AnimatedOpacity(
opacity: _hideStuff ? 0 : 1,
duration: _animatedTime,
child: Container(
height: 50,
decoration: BoxDecoration(
gradient: LinearGradient(
// Gradient position
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.0, 1.0], // [ Gradient start point , The end of the gradient ]
// Gradient color [ Starting point color , End color ]
colors: [
Color.fromRGBO(0, 0, 0, 1),
Color.fromRGBO(0, 0, 0, 0),
],
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
!isFullScreen ? Container(width: 40) : _backBtn(),
// title
// Expanded(
// child: Text(
// widget.videoTitle,
// style: TextStyle(
// color: Colors.white,
// fontSize: 14,
// ),
// maxLines: 1,
// overflow: TextOverflow.ellipsis,
// ),
// ),
],
),
),
),
/// Return button Display in small screen state Or wrong playback
Positioned(
top: 0,
left: 0,
right: 0,
child: Row(
children: <Widget>[
isFullScreen ? Container() : _backBtn(),
],
),
),
],
);
}
/// Middle zone
Widget _buildCentetContext() {
double currentValue = getCurrentVideoValue();
return FijkPanelCenterController(
size: Size(double.infinity, double.infinity),
onTap: _cancelAndRestartTimer,
onDoubleTap: _playOrPause,
currentTime: currentValue,
onTapUp: (e) {
setState(() {
isShowDouble = false;
player.setSpeed(1.0);
});
},
onTapDown: (e) {
setState(() {
isShowDouble = true;
player.setSpeed(2.0);
});
},
onHorizontalStart: _onVideoTimeChangeUpdate,
onHorizontalChange: _onVideoTimeChangeUpdate,
onHorizontalEnd: _onVideoTimeChangeEnd,
currentBrightness: _currentBrightness,
onLeftVerticalStart: (value) {
setState(() {
_showBrightness = true;
});
},
onLeftVerticalChange: (value) {
ScreenBrightness.setScreenBrightness(value);
setState(() {
_currentBrightness = value;
});
},
currentVolume: _currentVolume,
onLeftVerticalEnd: (value) {
setState(() {
_showBrightness = false;
});
},
onRightVerticalStart: (value) {
setState(() {
_showVolume = true;
});
},
onRightVerticalChange: (value) {
VolumeController().setVolume(value, showSystemUI: false);
// FijkVolume.setVol(value);
setState(() {
_currentVolume = value;
});
},
onRightVerticalEnd: (value) {
setState(() {
_showVolume = false;
});
},
builderChild: (context) {
Widget videoLoading = Container(); // Video buffer
if (_buffering) {
videoLoading = Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 25,
height: 25,
margin: EdgeInsets.only(bottom: 10),
child: CircularProgressIndicator(
backgroundColor: Color.fromRGBO(250, 250, 250, 0.5),
valueColor: AlwaysStoppedAnimation(Colors.white70),
),
),
Text(
" Buffering $_bufferingPro %",
style: TextStyle(
color: Colors.white70,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
);
}
return Stack(
children: <Widget>[
/// There is nothing to show in the middle
AnimatedOpacity(
opacity: _hideStuff ? 0 : 1,
duration: _animatedTime,
),
Positioned(
left: 0,
right: 0,
top: 0,
bottom: 0,
child: videoLoading,
),
/// Fast forward time
Positioned(
left: 0,
right: 0,
top: 0,
bottom: 0,
child: Offstage(
offstage: _seekPos == -1,
child: Center(
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.5),
borderRadius: BorderRadius.circular(5),
),
child: Text(
"${_duration2String(
Duration(milliseconds: _seekPos.toInt()),
)} / ${_duration2String(_duration)}",
style: TextStyle(color: Colors.white),
),
),
),
),
),
/// voice
Positioned(
left: 0,
right: 0,
top: 0,
bottom: 0,
child: AnimatedOpacity(
opacity: _showVolume ? 1 : 0,
duration: _animatedTime,
child: _buildVolumeOrBrightnessProgress(
type: 1,
value: _currentVolume,
maxValue: 1,
),
),
),
/// brightness
Positioned(
left: 0,
right: 0,
top: 0,
bottom: 0,
child: AnimatedOpacity(
opacity: _showBrightness ? 1 : 0,
duration: _animatedTime,
child: _buildVolumeOrBrightnessProgress(
type: 2,
value: _currentBrightness,
maxValue: 1,
),
),
),
_getFlex(),
],
);
},
);
}
_getFlex() {
if (isShowDouble) {
return Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
' Playing at double speed ',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 10, color: primary),
),
]);
} else {
return Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.center,
children: []);
}
}
/// Sound or brightness progress
Widget _buildVolumeOrBrightnessProgress({
@required int type,
@required double value,
@required double maxValue,
}) {
IconData iconData;
if (type == 1) {
iconData = value <= 0 ? Icons.volume_off_sharp : Icons.volume_up;
} else {
iconData = Icons.brightness_4;
}
double maxProgressWidth = 90;
return Center(
child: Container(
width: 130,
padding: EdgeInsets.only(top: 5, bottom: 5, right: 10),
decoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.5),
borderRadius: BorderRadius.circular(5),
),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 5),
child: Icon(
iconData,
size: 20,
color: Colors.white,
),
),
),
Container(
width: maxProgressWidth,
height: 3,
decoration: BoxDecoration(
color: Color.fromRGBO(250, 250, 250, 0.5),
borderRadius: BorderRadius.circular(5),
),
child: Row(
children: <Widget>[
Container(
width: value / maxValue * maxProgressWidth,
height: 3,
color: Colors.white,
),
],
),
),
],
),
),
);
}
/// Video time progress bar
Widget _buildVideoTimeBar() {
double currentValue = getCurrentVideoValue();
return FijkSlider(
min: 0,
max: _duration.inMilliseconds.toDouble(),
value: currentValue,
cacheValue: _bufferPos.inMilliseconds.toDouble(),
colors: FijkSliderColors(
playedColor: Color(0xff4075d1),
cursorColor: Colors.white,
baselineColor: Color(0xff807e7c),
bufferedColor: Color(0xff6494e6),
),
// onChangeStart: _onVideoTimeChangeUpdate,
onChanged: _onVideoTimeChangeUpdate,
onChangeEnd: _onVideoTimeChangeEnd,
);
}
/// Bottom bar
AnimatedOpacity _buildBottomBar() {
return AnimatedOpacity(
opacity: _hideStuff ? 0 : 1,
duration: _animatedTime,
child: Container(
height: isFullScreen ? 80 : 50,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter, // Gradient position
end: Alignment.bottomCenter,
stops: [0, 1], // [ Gradient start point , The end of the gradient ]
colors: [
Color.fromRGBO(0, 0, 0, 0),
Color.fromRGBO(0, 0, 0, 1),
], // Gradient color [ Starting point color , End color ]
),
),
child: Column(
children: <Widget>[
Container(
height: isFullScreen ? 25 : 0,
padding: EdgeInsets.symmetric(horizontal: 10),
child: isFullScreen ? _buildVideoTimeBar() : null,
),
Expanded(
child: Row(
children: <Widget>[
/// Play button
GestureDetector(
onTap: _playOrPause,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15),
color: Colors.transparent,
height: double.infinity,
child: Icon(
_playing ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 18,
),
),
),
/// Next episode button ( You can see )
Offstage(
offstage: !widget.isNextNumber || !isFullScreen,
child: GestureDetector(
onTap: widget.onVideoEnd,
child: Container(
padding: EdgeInsets.only(right: 15),
color: Colors.transparent,
height: double.infinity,
child: Icon(
Icons.skip_next_sharp,
color: Colors.white,
size: 18,
),
),
),
),
/// Current duration
Text(
_duration2String(_currentPos),
style: TextStyle(
fontSize: 14,
color: Colors.white,
),
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: !isFullScreen ? _buildVideoTimeBar() : null,
),
),
/// Total duration
Text(
_duration2String(_duration),
style: TextStyle(
fontSize: 14,
color: Colors.white,
),
),
/// Full screen button
isFullScreen
? SizedBox(width: 30)
: GestureDetector(
onTap: () {
player.enterFullScreen();
Future.delayed(Duration(seconds: 1), () {
setViewStatusBar(true);
});
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 13),
color: Colors.transparent,
height: double.infinity,
child: Icon(
isFullScreen
? Icons.fullscreen_exit
: Icons.fullscreen,
color: Colors.white,
size: 25,
),
),
),
],
),
),
],
),
),
);
}
/// Return button
Widget _backBtn() {
return GestureDetector(
onTap: widget.onBack,
child: Container(
padding: EdgeInsets.all(8),
child: Icon(
Icons.chevron_left,
size: 34,
color: Colors.white,
),
),
);
}
/// Video abnormal state
Widget _renderVideoStatusView() {
var bgImg = BoxDecoration(
color: Colors.black,
// image: DecorationImage(
// fit: BoxFit.cover,
// image: AssetImage(
// "xxx.jpg", // You can set a background image
// ),
// ),
);
if (_isPlayError) {
/// error
return GestureDetector(
onTap: widget.onError,
child: Container(
width: double.infinity,
height: double.infinity,
decoration: bgImg,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 15),
child: Icon(
Icons.error_rounded,
color: Colors.white70,
size: 70,
),
),
RichText(
text: TextSpan(
text: " Abnormal playback !",
style: TextStyle(
color: Colors.white70,
fontSize: 14,
fontWeight: FontWeight.w600,
),
children: <InlineSpan>[
TextSpan(
text: " Refresh ",
style: TextStyle(
color: Color(0xff79b0ff),
),
)
],
),
),
],
),
),
);
} else if (widget.isPlayAd) {
return Container(
width: double.infinity,
height: double.infinity,
decoration: bgImg,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
" Look at the advertisement ",
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
SizedBox(height: 10),
Text(
" Play a video advertisement ",
style: TextStyle(
color: Colors.white70,
fontSize: 12.5,
),
),
SizedBox(height: 20),
GestureDetector(
onTap: widget.onPlayAd,
child: Container(
padding: EdgeInsets.symmetric(
vertical: 8,
horizontal: 20,
),
decoration: BoxDecoration(
color: Color(0xff2d73ed),
borderRadius: BorderRadius.circular(5),
),
child: Text(
" Click advertisement ",
style: TextStyle(
color: Colors.white,
fontSize: 13,
),
),
),
),
],
),
);
} else if (!_prepared) {
/// Loading
return Container(
width: double.infinity,
height: double.infinity,
decoration: bgImg,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 50,
height: 50,
margin: EdgeInsets.only(bottom: 20),
child: CircularProgressIndicator(
backgroundColor: Colors.white70,
valueColor: AlwaysStoppedAnimation(Color(0xff79b0ff)),
),
),
Text(
" Trying to load ...",
style: TextStyle(
color: Colors.white70,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
),
);
} else if (_isPlayCompleted) {
/// Whether to display the end of playback
return GestureDetector(
onTap: () {
player.start();
setState(() {
_isPlayCompleted = false;
});
},
child: Container(
width: double.infinity,
height: double.infinity,
color: Color.fromRGBO(0, 0, 0, 0.5),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.play_circle_outline_outlined,
size: 30,
color: Colors.white70,
),
SizedBox(height: 10),
Text(
" Replay ",
style: TextStyle(
color: Colors.white70,
fontSize: 12.5,
),
),
],
),
),
);
} else {
return Container();
}
}
/// Set the full screen display of the page to hide the status bar and virtual keys
setViewStatusBar(bool isHide) {
if (isHide) {
SystemChrome.setEnabledSystemUIOverlays([]);
} else {
SystemChrome.setEnabledSystemUIOverlays([
SystemUiOverlay.top,
SystemUiOverlay.bottom,
]);
}
}
@override
Widget build(BuildContext context) {
if (_isPlayError || !_prepared || _isPlayCompleted || widget.isPlayAd) {
/// Error playback | Not loaded well | There is no next episode after playing
return Stack(
children: <Widget>[
_renderVideoStatusView(),
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
width: double.infinity,
color: Colors.transparent,
alignment: Alignment.centerLeft,
child: _backBtn(),
),
),
],
);
} else {
var viewSize = widget.viewSize;
return Positioned.fromRect(
rect: Rect.fromLTWH(0, 0, viewSize.width, viewSize.height),
child: Column(
children: <Widget>[
_buildTopmBar(),
Expanded(
child: _buildCentetContext(),
),
_buildBottomBar(),
],
),
);
}
}
}
fijk_controller.dart In the code :
import 'package:flutter/material.dart';
class FijkPanelCenterController extends StatefulWidget {
final Size size;
final void Function() onTap;
final void Function() onDoubleTap;
final void Function(TapUpDetails) onTapUp; // lift
final void Function(TapDownDetails) onTapDown; // Press down
final double currentTime;
final void Function(double) onHorizontalStart;
final void Function(double) onHorizontalChange;
final void Function(double) onHorizontalEnd;
final double currentBrightness;
final void Function(double) onLeftVerticalStart;
final void Function(double) onLeftVerticalChange;
final void Function(double) onLeftVerticalEnd;
final double currentVolume;
final void Function(double) onRightVerticalStart;
final void Function(double) onRightVerticalChange;
final void Function(double) onRightVerticalEnd;
final Widget Function(BuildContext context) builderChild;
/// Customized touch controller
/// @param {Size} size - Box size
/// @param {void Function()?} onTap -
/// @param {void Function()?} onDoubleTap -
/// @param {double} currentTime - Incoming current video time onHorizontal We can get
/// @param {void Function(double)?} onHorizontalStart -
/// @param {void Function(double)?} onHorizontalChange -
/// @param {void Function(double)?} onHorizontalEnd -
/// @param {double} currentBrightness - Pass in the current system brightness onLeftVertical We can get
/// @param {void Function(double)?} onLeftVerticalStart - Drag up and down on the left ( brightness )
/// @param {void Function(double)?} onLeftVerticalChange -
/// @param {void Function(double)?} onLeftVerticalEnd -
/// @param {double} currentVolume - Incoming current system sound onRightVertical We can get
/// @param {void Function(double)?} onRightVerticalStart - Drag up and down on the right ( voice )
/// @param {void Function(double)?} onRightVerticalChange -
/// @param {void Function(double)?} onRightVerticalEnd -
/// @param {Widget Function(BuildContext context)} builderChild - The content of child nodes is directly imported from the outside
/// ```
const FijkPanelCenterController({
@required this.size,
this.onTap,
this.onDoubleTap,
this.onTapUp,
this.onTapDown,
@required this.currentTime,
this.onHorizontalStart,
this.onHorizontalChange,
this.onHorizontalEnd,
@required this.currentBrightness,
this.onLeftVerticalStart,
this.onLeftVerticalChange,
this.onLeftVerticalEnd,
@required this.currentVolume,
this.onRightVerticalStart,
this.onRightVerticalChange,
this.onRightVerticalEnd,
@required this.builderChild,
});
@override
State<StatefulWidget> createState() {
return _FijkPanelCenterController();
}
}
class _FijkPanelCenterController extends State<FijkPanelCenterController> {
/// When sliding up and down, record at the beginning 0- On the left 1- On the right
int verticalType = 0;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
onDoubleTap: widget.onDoubleTap,
onTapUp: widget.onTapUp,
onTapDown: widget.onTapDown,
onHorizontalDragStart: (details) {
widget.onHorizontalStart?.call(widget.currentTime);
},
onHorizontalDragUpdate: (details) {
// From the last update , The amount that the pointer moves along the axis in the coordinate space of the event receiver .
double deltaDx = details.delta.dx;
if (deltaDx == 0) {
return; // Avoid that some mobile phones will return 0.0
}
var dragValue = (deltaDx * 4000) + widget.currentTime;
widget.onHorizontalChange?.call(dragValue);
},
onHorizontalDragEnd: (details) {
widget.onHorizontalEnd?.call(widget.currentTime);
},
onVerticalDragStart: (details) {
double dx = details.localPosition.dx;
var winWidth = context.size?.width ?? 0;
if (dx < winWidth / 2) {
verticalType = 0;
widget.onLeftVerticalStart?.call(widget.currentBrightness);
} else {
verticalType = 1;
widget.onRightVerticalStart?.call(widget.currentVolume);
}
},
onVerticalDragUpdate: (details) {
double deltaDy = details.delta.dy;
if (deltaDy == 0) {
return; // Avoid that some mobile phones will return 0.0
}
double moveTo = 0;
if (deltaDy > 0) {
moveTo = -0.01;
} else {
moveTo = 0.01;
}
double dragValue = 0;
switch (verticalType) {
case 0:
dragValue = moveTo + widget.currentBrightness;
if (dragValue > 1) {
dragValue = 1;
} else if (dragValue < 0) {
dragValue = 0;
}
print(" Set brightness $dragValue");
widget.onLeftVerticalChange?.call(dragValue);
break;
case 1:
dragValue = moveTo + widget.currentVolume;
if (dragValue > 1) {
dragValue = 1;
} else if (dragValue < 0) {
dragValue = 0;
}
print(" Set the sound $dragValue");
widget.onRightVerticalChange?.call(dragValue);
break;
default:
}
},
onVerticalDragEnd: (details) {
switch (verticalType) {
case 0:
widget.onLeftVerticalEnd?.call(widget.currentBrightness);
break;
case 1:
widget.onRightVerticalEnd?.call(widget.currentVolume);
break;
default:
}
},
child: Container(
width: widget.size.width,
height: widget.size.height,
color: Colors.transparent,
child: widget.builderChild(context),
),
);
}
}
Use :
Container(
alignment: Alignment.center,
color: Colors.black,
child: FijkView(
width: double.infinity,
height: _videoHeight,
player: player,
color: Colors.black,
fsFit: FijkFit.contain,
// Fill in full screen mode
fit: FijkFit.contain,
// Filling in normal mode
panelBuilder: (player, data, buildContext, viewSize, texturePos) {
return CustomFijkPanel(
player: player,
buildContext: context,
viewSize: viewSize,
texturePos: texturePos,
videoTitle: widget.homePageBean.title,
isPlayAd: false,
isNextNumber: false,
onPlayAd: () {
/// Play ads isPlayAd true It will be displayed after clicking on post-processing and playing, and then start playing the video
},
onError: () async {
await player.reset();
},
onBack: () {
Navigator.pop(context); // If you need to intercept and return, judge here
},
onVideoEnd: () async {
// At the end of the last episode of the video, there will be UI The layer displayed can trigger a restart
await player.reset();
},
onVideoTimeChange: () {
// Video time change triggers once , You can save the video history. If you don't want to trigger it frequently, you can modify it in sendCount % 50 == 0
},
onVideoPrepared: () async {
// Video initialization is complete , If there is a history period, you can trigger fast forward
},
);
},
),
)
边栏推荐
- pyinstaller打包scrapy
- Decimal operation in shell (BC)
- The problem of overwriting array when pushing
- shell 脚本编写提示
- appstore 上传屏幕快照尺寸
- PLSQL cannot be initialized
- MySQL master-slave synchronization problem, repair the slave Library (transfer)
- Summary of MySQL import and export methods using mysqldump
- NC26 括号生成
- Flutter development (XXXI): flutter starts the white screen
猜你喜欢
【TA-霜狼_may-《百人计划》】图形3.3 曲面细分与几何着色器 大规模草渲染
Open MySQL binlog log on Linux
Oracle 11g installs and starts EM based on centos7
Appstore upload screenshot size
Kotlin学习一:变量、函数、条件语句与循环语句
appstore 上传屏幕快照尺寸
Spark RDD的依赖于DAG的工作原理
vmware nat模式下主机ping不通虚拟机:跟大部分方法不一样
Docker - DB2 database deployment through container installation tutorial
Xcode11 add lanuchimage black screen can't display problem
随机推荐
Spark RDD operator: partition operation, mappartitions and mappartitionswithindex
shell script “<< EOF”我的用途和遇到的问题
HTB- Armageddon
关闭浏览器 如何清除localStorage数据
Spark RDD算子:分区操作,mapPartitions和mapPartitionsWIthIndex
Open MySQL binlog log on Linux
pyplot.plot使用遇到:UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail
Fixed left width, adaptive right width
Typora下载和简单使用教程
pyinstaller打包scrapy
Decimal operation in shell (BC)
stat函数详解
Spark RDD的持久化(缓存、检查点、广播变量和累加器)
Flutter 2 Advanced (I): practical skills of flutter
leetcode 21. 合并两个有序链表
Spark SQL: MySQL classic 50 questions (SQL version and spark version)
leetcode 32. 最长有效括号
grep实际使用 ps/netstat/sort
数组push时 覆盖的问题
Flutter 2进阶(四):基于Navigator 2.0封装