|
|
|
|
@ -0,0 +1,828 @@
|
|
|
|
|
import 'dart:async';
|
|
|
|
|
import 'dart:core';
|
|
|
|
|
import 'dart:math';
|
|
|
|
|
import 'dart:ui';
|
|
|
|
|
import 'dart:typed_data';
|
|
|
|
|
|
|
|
|
|
import 'package:fijkplayer/fijkplayer.dart';
|
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
|
import 'my_fijkvalue.dart';
|
|
|
|
|
import '../components/commonFun.dart';
|
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
|
import '../provider/player_ratio.dart';
|
|
|
|
|
import 'my_slider.dart';
|
|
|
|
|
import '../my_extended_image/common/image_picker/_image_picker_io.dart';
|
|
|
|
|
import '../services/Storage.dart';
|
|
|
|
|
|
|
|
|
|
// class MyFijkPanelWidgetBuilder extends StatefulWidget {
|
|
|
|
|
// const MyFijkPanelWidgetBuilder({Key key, this.path}) : super(key: key);
|
|
|
|
|
// final path;
|
|
|
|
|
// @override
|
|
|
|
|
// State<StatefulWidget> createState() {
|
|
|
|
|
// return MyFijkPanelWidgetBuilderState();
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// snapshot
|
|
|
|
|
ImageProvider myImageProvider;
|
|
|
|
|
//ImageProvider _imageProvider;
|
|
|
|
|
//Timer _snapshotTimer;
|
|
|
|
|
|
|
|
|
|
FijkPanelWidgetBuilder fijkPanel2Builder3(
|
|
|
|
|
{Key key,
|
|
|
|
|
final FijkPlayer player,
|
|
|
|
|
final bool fill = false,
|
|
|
|
|
final int duration = 4000,
|
|
|
|
|
final bool doubleTap = true,
|
|
|
|
|
final bool snapShot = false,
|
|
|
|
|
final VoidCallback onBack}) {
|
|
|
|
|
return (FijkPlayer player, FijkData data, BuildContext context, Size viewSize,
|
|
|
|
|
Rect texturePos) {
|
|
|
|
|
return MyFijkPanelWidgetBuilder(
|
|
|
|
|
key: key,
|
|
|
|
|
player: player,
|
|
|
|
|
data: data,
|
|
|
|
|
onBack: onBack,
|
|
|
|
|
viewSize: viewSize,
|
|
|
|
|
texPos: texturePos,
|
|
|
|
|
fill: fill,
|
|
|
|
|
doubleTap: doubleTap,
|
|
|
|
|
snapShot: snapShot,
|
|
|
|
|
hideDuration: duration,
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MyFijkPanelWidgetBuilder extends StatefulWidget {
|
|
|
|
|
final FijkPlayer player;
|
|
|
|
|
final FijkData data;
|
|
|
|
|
final VoidCallback onBack;
|
|
|
|
|
final Size viewSize;
|
|
|
|
|
final Rect texPos;
|
|
|
|
|
final bool fill;
|
|
|
|
|
final bool doubleTap;
|
|
|
|
|
final bool snapShot;
|
|
|
|
|
final int hideDuration;
|
|
|
|
|
|
|
|
|
|
const MyFijkPanelWidgetBuilder(
|
|
|
|
|
{Key key,
|
|
|
|
|
@required this.player,
|
|
|
|
|
this.data,
|
|
|
|
|
this.fill,
|
|
|
|
|
this.onBack,
|
|
|
|
|
this.viewSize,
|
|
|
|
|
this.hideDuration,
|
|
|
|
|
this.doubleTap,
|
|
|
|
|
this.snapShot,
|
|
|
|
|
this.texPos})
|
|
|
|
|
: assert(player != null),
|
|
|
|
|
assert(
|
|
|
|
|
hideDuration != null && hideDuration > 0 && hideDuration < 10000),
|
|
|
|
|
super(key: key);
|
|
|
|
|
|
|
|
|
|
// @override
|
|
|
|
|
// FijkPanel2State createState() => FijkPanel2State();
|
|
|
|
|
// @override
|
|
|
|
|
//State<StatefulWidget> createState() {
|
|
|
|
|
MyFijkPanelWidgetBuilderState createState() {
|
|
|
|
|
return MyFijkPanelWidgetBuilderState(player);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MyFijkPanelWidgetBuilderState extends State<MyFijkPanelWidgetBuilder>
|
|
|
|
|
with SingleTickerProviderStateMixin {
|
|
|
|
|
FijkPlayer player;
|
|
|
|
|
|
|
|
|
|
MyFijkPanelWidgetBuilderState(@required this.player) {
|
|
|
|
|
_oldFullScreen = player.value.fullScreen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlayerRatioProvide playerRatioProvide;
|
|
|
|
|
AnimationController _controller;
|
|
|
|
|
Animation<Offset> _animation;
|
|
|
|
|
Offset _normalizedOffset;
|
|
|
|
|
double _previousScale;
|
|
|
|
|
double _kMinFlingVelocity = 600.0;
|
|
|
|
|
|
|
|
|
|
Offset _offset = Offset.zero;
|
|
|
|
|
double _scale = 1.0;
|
|
|
|
|
Offset _focalPoint = Offset.zero;
|
|
|
|
|
Offset _deltaPoint = Offset.zero;
|
|
|
|
|
|
|
|
|
|
bool _oldFullScreen;
|
|
|
|
|
bool _bScaleing = false;
|
|
|
|
|
|
|
|
|
|
//FijkPlayer get player => widget.player;
|
|
|
|
|
|
|
|
|
|
Timer _hideTimer;
|
|
|
|
|
bool _hideStuff = true;
|
|
|
|
|
|
|
|
|
|
Timer _statelessTimer;
|
|
|
|
|
bool _prepared = false;
|
|
|
|
|
bool _playing = false;
|
|
|
|
|
|
|
|
|
|
//bool _dragLeft;
|
|
|
|
|
int _nDrag; //0 Left, 1 Center, 2 Right
|
|
|
|
|
double _volume;
|
|
|
|
|
double _brightness;
|
|
|
|
|
double _deltaX = 0;
|
|
|
|
|
double _deltaY = 0;
|
|
|
|
|
|
|
|
|
|
double _seekPos = -1.0;
|
|
|
|
|
Duration _duration = Duration();
|
|
|
|
|
Duration _currentPos = Duration();
|
|
|
|
|
Duration _bufferPos = Duration();
|
|
|
|
|
|
|
|
|
|
StreamSubscription _currentPosSubs;
|
|
|
|
|
StreamSubscription _bufferPosSubs;
|
|
|
|
|
|
|
|
|
|
StreamController<double> _valController;
|
|
|
|
|
|
|
|
|
|
// snapshot
|
|
|
|
|
//ImageProvider _imageProvider;
|
|
|
|
|
Timer _snapshotTimer;
|
|
|
|
|
|
|
|
|
|
// Is it needed to clear seek data in FijkData (widget.data)
|
|
|
|
|
bool _needClearSeekData = true;
|
|
|
|
|
|
|
|
|
|
static const MyFijkSliderColors sliderColors = MyFijkSliderColors();
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
|
|
|
|
|
_controller = AnimationController(vsync: this);
|
|
|
|
|
_controller.addListener(() {
|
|
|
|
|
setState(() {
|
|
|
|
|
_offset = _animation.value;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
_valController = StreamController.broadcast();
|
|
|
|
|
_prepared = player.state.index >= FijkState.prepared.index;
|
|
|
|
|
_playing = player.state == FijkState.started;
|
|
|
|
|
_duration = player.value.duration;
|
|
|
|
|
_currentPos = player.currentPos;
|
|
|
|
|
_bufferPos = player.bufferPos;
|
|
|
|
|
|
|
|
|
|
_currentPosSubs = player.onCurrentPosUpdate.listen((v) {
|
|
|
|
|
if (_hideStuff == false) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_currentPos = v;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
_currentPos = v;
|
|
|
|
|
}
|
|
|
|
|
if (_needClearSeekData) {
|
|
|
|
|
widget.data.clearValue(MyFijkData.fijkViewPanelSeekto);
|
|
|
|
|
}
|
|
|
|
|
_needClearSeekData = false;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (widget.data.contains(MyFijkData.fijkViewPanelSeekto)) {
|
|
|
|
|
var pos = widget.data.getValue(MyFijkData.fijkViewPanelSeekto) as double;
|
|
|
|
|
_currentPos = Duration(milliseconds: pos.toInt());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_bufferPosSubs = player.onBufferPosUpdate.listen((v) {
|
|
|
|
|
if (_hideStuff == false) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_bufferPos = v;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
_bufferPos = v;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
player.addListener(_playerValueChanged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
super.dispose();
|
|
|
|
|
_controller.dispose();
|
|
|
|
|
_valController?.close();
|
|
|
|
|
_hideTimer?.cancel();
|
|
|
|
|
_statelessTimer?.cancel();
|
|
|
|
|
_snapshotTimer?.cancel();
|
|
|
|
|
_currentPosSubs?.cancel();
|
|
|
|
|
_bufferPosSubs?.cancel();
|
|
|
|
|
player.removeListener(_playerValueChanged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double dura2double(Duration d) {
|
|
|
|
|
return d != null ? d.inMilliseconds.toDouble() : 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _playerValueChanged() {
|
|
|
|
|
FijkValue value = player.value;
|
|
|
|
|
|
|
|
|
|
if (value.duration != _duration) {
|
|
|
|
|
if (_hideStuff == false) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_duration = value.duration;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
_duration = value.duration;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool playing = (value.state == FijkState.started);
|
|
|
|
|
bool prepared = value.prepared;
|
|
|
|
|
if (playing != _playing ||
|
|
|
|
|
prepared != _prepared ||
|
|
|
|
|
value.state == FijkState.asyncPreparing) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_playing = playing;
|
|
|
|
|
_prepared = prepared;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _restartHideTimer() {
|
|
|
|
|
_hideTimer?.cancel();
|
|
|
|
|
_hideTimer = Timer(Duration(milliseconds: widget.hideDuration), () {
|
|
|
|
|
setState(() {
|
|
|
|
|
_hideStuff = true;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onTapFun() {
|
|
|
|
|
if (_hideStuff == true) {
|
|
|
|
|
_restartHideTimer();
|
|
|
|
|
}
|
|
|
|
|
setState(() {
|
|
|
|
|
_hideStuff = !_hideStuff;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void playOrPause() {
|
|
|
|
|
if (player.isPlayable() || player.state == FijkState.asyncPreparing) {
|
|
|
|
|
if (player.state == FijkState.started) {
|
|
|
|
|
//bPlaying = false;
|
|
|
|
|
playerRegionProvide.changePlayerState(false);
|
|
|
|
|
Storage.setString('bPlaying', 'false');
|
|
|
|
|
player.pause();
|
|
|
|
|
} else {
|
|
|
|
|
//bPlaying = true;
|
|
|
|
|
playerRegionProvide.changePlayerState(true);
|
|
|
|
|
Storage.setString('bPlaying', 'true');
|
|
|
|
|
player.start();
|
|
|
|
|
}
|
|
|
|
|
//setState(() {});
|
|
|
|
|
} else {
|
|
|
|
|
FijkLog.w("Invalid state ${player.state} ,can't perform play or pause");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onVerticalDragStartFun(DragStartDetails d) {
|
|
|
|
|
// if (_bScaleing) {
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
if (d.localPosition.dx > panelWidth() * 3 / 4) {
|
|
|
|
|
// right, volume
|
|
|
|
|
//_dragLeft = false;
|
|
|
|
|
_nDrag = 2; //0 Left, 1 Center, 2 Right
|
|
|
|
|
|
|
|
|
|
//https://fijkplayer.befovy.com/docs/zh/system-volume.html#gsc.tab=0
|
|
|
|
|
/// never show system volume changed UI.
|
|
|
|
|
int neverShowUI = 2;
|
|
|
|
|
FijkVolume.setUIMode(neverShowUI);
|
|
|
|
|
|
|
|
|
|
FijkVolume.getVol().then((v) {
|
|
|
|
|
if (widget.data != null &&
|
|
|
|
|
!widget.data.contains(MyFijkData.fijkViewPanelVolume)) {
|
|
|
|
|
widget.data.setValue(MyFijkData.fijkViewPanelVolume, v);
|
|
|
|
|
}
|
|
|
|
|
setState(() {
|
|
|
|
|
_volume = v;
|
|
|
|
|
_valController.add(v);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
} else if (d.localPosition.dx < panelWidth() / 4) {
|
|
|
|
|
// left, brightness
|
|
|
|
|
//_dragLeft = true;
|
|
|
|
|
_nDrag = 0; //0 Left, 1 Center, 2 Right
|
|
|
|
|
FijkPlugin.screenBrightness().then((v) {
|
|
|
|
|
if (widget.data != null &&
|
|
|
|
|
!widget.data.contains(MyFijkData.fijkViewPanelBrightness)) {
|
|
|
|
|
widget.data.setValue(MyFijkData.fijkViewPanelBrightness, v);
|
|
|
|
|
}
|
|
|
|
|
setState(() {
|
|
|
|
|
_brightness = v;
|
|
|
|
|
_valController.add(v);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
_nDrag = 1; //0 Left, 1 Center, 2 Right
|
|
|
|
|
// setState(() {
|
|
|
|
|
// if (_oldFullScreen != player.value.fullScreen) {
|
|
|
|
|
// //全屏和窗口之间切换,便初始化相关变量
|
|
|
|
|
// _oldFullScreen = player.value.fullScreen;
|
|
|
|
|
// // _offset = Offset.zero;
|
|
|
|
|
// // _scale = 1.0;
|
|
|
|
|
// //_focalPoint = Offset.zero;
|
|
|
|
|
// //_deltaPoint = Offset.zero;
|
|
|
|
|
// }
|
|
|
|
|
// // _bScaleing = true;
|
|
|
|
|
// // _previousScale = _scale;
|
|
|
|
|
// // _normalizedOffset = (d.localPosition - _offset) / _scale;
|
|
|
|
|
// // // 计算图片放大后的位置
|
|
|
|
|
// // _controller.stop();
|
|
|
|
|
// });
|
|
|
|
|
}
|
|
|
|
|
_statelessTimer?.cancel();
|
|
|
|
|
_statelessTimer = Timer(const Duration(milliseconds: 2000), () {
|
|
|
|
|
setState(() {});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onVerticalDragUpdateFun(DragUpdateDetails d) {
|
|
|
|
|
// if (_bScaleing) {
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
double delta = d.primaryDelta / panelHeight();
|
|
|
|
|
print("d.primaryDelta = ${d.primaryDelta}, delta = {$delta}");
|
|
|
|
|
|
|
|
|
|
delta = -delta.clamp(-1.0, 1.0);
|
|
|
|
|
//if (_dragLeft != null && _dragLeft == false) {
|
|
|
|
|
if (_nDrag != null && _nDrag == 2) {
|
|
|
|
|
if (_volume != null) {
|
|
|
|
|
_volume += delta;
|
|
|
|
|
_volume = _volume.clamp(0.0, 1.0);
|
|
|
|
|
FijkVolume.setVol(_volume);
|
|
|
|
|
setState(() {
|
|
|
|
|
_valController.add(_volume);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
//} else if (_dragLeft != null && _dragLeft == true) {
|
|
|
|
|
} else if (_nDrag != null && _nDrag == 0) {
|
|
|
|
|
if (_brightness != null) {
|
|
|
|
|
_brightness += delta;
|
|
|
|
|
_brightness = _brightness.clamp(0.0, 1.0);
|
|
|
|
|
FijkPlugin.setScreenBrightness(_brightness);
|
|
|
|
|
setState(() {
|
|
|
|
|
_valController.add(_brightness);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
setState(() {
|
|
|
|
|
//_scale = (_previousScale * details.scale).clamp(1.0, 10.0);
|
|
|
|
|
// 限制放大倍数 1~10倍
|
|
|
|
|
// _offset = _clampOffset(d.localPosition - _normalizedOffset * _scale);
|
|
|
|
|
// playerRatioProvide.changeOffset(_offset);
|
|
|
|
|
|
|
|
|
|
_deltaY -= 2 * d.primaryDelta / panelHeight();
|
|
|
|
|
_deltaY = _deltaY.clamp(-1.0, 1.0);
|
|
|
|
|
playerRatioProvide.changeDeltaY(_deltaY);
|
|
|
|
|
// 更新当前位置
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onVerticalDragEndFun(DragEndDetails e) {
|
|
|
|
|
_volume = null;
|
|
|
|
|
_brightness = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget buildPlayButton(BuildContext context, double height) {
|
|
|
|
|
Icon icon = (player.state == FijkState.started)
|
|
|
|
|
? Icon(Icons.pause)
|
|
|
|
|
: Icon(Icons.play_arrow);
|
|
|
|
|
bool fullScreen = player.value.fullScreen;
|
|
|
|
|
return IconButton(
|
|
|
|
|
padding: EdgeInsets.all(0),
|
|
|
|
|
iconSize: fullScreen ? height : height * 0.8,
|
|
|
|
|
color: Color(0xFFFFFFFF),
|
|
|
|
|
icon: icon,
|
|
|
|
|
onPressed: playOrPause,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget buildFullScreenButton(BuildContext context, double height) {
|
|
|
|
|
Icon icon = player.value.fullScreen
|
|
|
|
|
? Icon(Icons.fullscreen_exit)
|
|
|
|
|
: Icon(Icons.fullscreen);
|
|
|
|
|
bool fullScreen = player.value.fullScreen;
|
|
|
|
|
return IconButton(
|
|
|
|
|
padding: EdgeInsets.all(0),
|
|
|
|
|
iconSize: fullScreen ? height : height * 0.8,
|
|
|
|
|
color: Color(0xFFFFFFFF),
|
|
|
|
|
icon: icon,
|
|
|
|
|
onPressed: () {
|
|
|
|
|
player.value.fullScreen
|
|
|
|
|
? player.exitFullScreen()
|
|
|
|
|
: player.enterFullScreen();
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget buildTimeText(BuildContext context, double height) {
|
|
|
|
|
String text = "${mYduration2String(_currentPos)}" +
|
|
|
|
|
"/${mYduration2String(_duration)}";
|
|
|
|
|
return Text(text, style: TextStyle(fontSize: 12, color: Color(0xFFFFFFFF)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget buildSlider(BuildContext context) {
|
|
|
|
|
double duration = dura2double(_duration);
|
|
|
|
|
|
|
|
|
|
double currentValue = _seekPos > 0 ? _seekPos : dura2double(_currentPos);
|
|
|
|
|
currentValue = currentValue.clamp(0.0, duration);
|
|
|
|
|
|
|
|
|
|
double bufferPos = dura2double(_bufferPos);
|
|
|
|
|
bufferPos = bufferPos.clamp(0.0, duration);
|
|
|
|
|
|
|
|
|
|
return Padding(
|
|
|
|
|
padding: EdgeInsets.only(left: 0),
|
|
|
|
|
child: MyFijkSlider(
|
|
|
|
|
colors: sliderColors,
|
|
|
|
|
value: currentValue,
|
|
|
|
|
cacheValue: bufferPos,
|
|
|
|
|
min: 0.0,
|
|
|
|
|
max: duration,
|
|
|
|
|
onChanged: (v) {
|
|
|
|
|
_restartHideTimer();
|
|
|
|
|
setState(() {
|
|
|
|
|
_seekPos = v;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
onChangeEnd: (v) {
|
|
|
|
|
setState(() {
|
|
|
|
|
player.seekTo(v.toInt());
|
|
|
|
|
_currentPos = Duration(milliseconds: _seekPos.toInt());
|
|
|
|
|
widget.data.setValue(MyFijkData.fijkViewPanelSeekto, _seekPos);
|
|
|
|
|
_needClearSeekData = true;
|
|
|
|
|
_seekPos = -1.0;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget buildBottom(BuildContext context, double height) {
|
|
|
|
|
if (_duration != null && _duration.inMilliseconds > 0) {
|
|
|
|
|
return Row(
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
buildPlayButton(context, height),
|
|
|
|
|
buildTimeText(context, height),
|
|
|
|
|
Expanded(child: buildSlider(context)),
|
|
|
|
|
buildFullScreenButton(context, height),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
return Row(
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
buildPlayButton(context, height),
|
|
|
|
|
Expanded(child: Container()),
|
|
|
|
|
buildFullScreenButton(context, height),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void takeSnapshot() {
|
|
|
|
|
player.takeSnapShot().then((v) {
|
|
|
|
|
var provider = MemoryImage(v);
|
|
|
|
|
precacheImage(provider, context).then((_) {
|
|
|
|
|
setState(() {
|
|
|
|
|
myImageProvider = provider;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
FijkLog.d("get snapshot succeed");
|
|
|
|
|
|
|
|
|
|
//Uint8List fileData;
|
|
|
|
|
String fileFath;
|
|
|
|
|
ImageSaver.save('extended_image_cropped_image.jpg', v).then((value) {
|
|
|
|
|
fileFath = value;
|
|
|
|
|
// var fileFath = await ImagePickerSaver.saveFile(fileData: fileData);
|
|
|
|
|
print('my save fileFath : $fileFath');
|
|
|
|
|
});
|
|
|
|
|
}).catchError((e) {
|
|
|
|
|
FijkLog.d("get snapshot failed");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget buildPanel(BuildContext context) {
|
|
|
|
|
double height = panelHeight();
|
|
|
|
|
|
|
|
|
|
bool fullScreen = player.value.fullScreen;
|
|
|
|
|
Widget centerWidget = Container(
|
|
|
|
|
color: Color(0x00000000),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Widget centerChild = Container(
|
|
|
|
|
color: Color(0x00000000),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (fullScreen && widget.snapShot) {
|
|
|
|
|
centerWidget = Row(
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
Expanded(child: centerChild),
|
|
|
|
|
Padding(
|
|
|
|
|
padding: EdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8),
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
IconButton(
|
|
|
|
|
padding: EdgeInsets.all(0),
|
|
|
|
|
color: Color(0xFFFFFFFF),
|
|
|
|
|
icon: Icon(Icons.camera_alt),
|
|
|
|
|
onPressed: () {
|
|
|
|
|
takeSnapshot();
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
Container(
|
|
|
|
|
height: height > 200 ? 80 : height / 5,
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
colors: [Color(0x88000000), Color(0x00000000)],
|
|
|
|
|
begin: Alignment.topCenter,
|
|
|
|
|
end: Alignment.bottomCenter,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: centerWidget,
|
|
|
|
|
),
|
|
|
|
|
Container(
|
|
|
|
|
height: height > 80 ? 80 : height / 2,
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
colors: [Color(0x88000000), Color(0x00000000)],
|
|
|
|
|
end: Alignment.topCenter,
|
|
|
|
|
begin: Alignment.bottomCenter,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
alignment: Alignment.bottomCenter,
|
|
|
|
|
child: Container(
|
|
|
|
|
height: height > 80 ? 45 : height / 2,
|
|
|
|
|
padding: EdgeInsets.only(left: 8, right: 8, bottom: 5),
|
|
|
|
|
child: buildBottom(context, height > 80 ? 40 : height / 2),
|
|
|
|
|
// child: CustomFijkWidgetBottom(
|
|
|
|
|
// player: player,
|
|
|
|
|
// buildContext: context,
|
|
|
|
|
// viewSize:
|
|
|
|
|
// Size(widget.viewSize.width, widget.viewSize.height - 50),
|
|
|
|
|
// texturePos: Rect.fromLTRB(widget.texPos.left, widget.texPos.top,
|
|
|
|
|
// widget.texPos.width, widget.texPos.height - 50),
|
|
|
|
|
//),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rect panelRect() {
|
|
|
|
|
Rect rect = player.value.fullScreen || (true == widget.fill)
|
|
|
|
|
? Rect.fromLTWH(0, 0, widget.viewSize.width, widget.viewSize.height)
|
|
|
|
|
: Rect.fromLTRB(
|
|
|
|
|
max(0.0, widget.texPos.left),
|
|
|
|
|
max(0.0, widget.texPos.top),
|
|
|
|
|
min(widget.viewSize.width, widget.texPos.right),
|
|
|
|
|
min(widget.viewSize.height, widget.texPos.bottom));
|
|
|
|
|
return rect;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double panelHeight() {
|
|
|
|
|
if (player.value.fullScreen || (true == widget.fill)) {
|
|
|
|
|
return widget.viewSize.height;
|
|
|
|
|
} else {
|
|
|
|
|
return min(widget.viewSize.height, widget.texPos.bottom) -
|
|
|
|
|
max(0.0, widget.texPos.top);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double panelWidth() {
|
|
|
|
|
if (player.value.fullScreen || (true == widget.fill)) {
|
|
|
|
|
return widget.viewSize.width;
|
|
|
|
|
} else {
|
|
|
|
|
return min(widget.viewSize.width, widget.texPos.right) -
|
|
|
|
|
max(0.0, widget.texPos.left);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget buildBack(BuildContext context) {
|
|
|
|
|
return IconButton(
|
|
|
|
|
padding: EdgeInsets.only(left: 5),
|
|
|
|
|
icon: Icon(
|
|
|
|
|
Icons.arrow_back_ios,
|
|
|
|
|
color: Color(0xDDFFFFFF),
|
|
|
|
|
),
|
|
|
|
|
onPressed: widget.onBack,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget buildStateless() {
|
|
|
|
|
if (_volume != null || _brightness != null) {
|
|
|
|
|
Widget toast = _volume == null
|
|
|
|
|
? defaultFijkBrightnessToast(_brightness, _valController.stream)
|
|
|
|
|
: defaultFijkVolumeToast(_volume, _valController.stream);
|
|
|
|
|
return IgnorePointer(
|
|
|
|
|
child: AnimatedOpacity(
|
|
|
|
|
opacity: 1,
|
|
|
|
|
duration: Duration(milliseconds: 500),
|
|
|
|
|
child: toast,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} else if (player.state == FijkState.asyncPreparing) {
|
|
|
|
|
return Container(
|
|
|
|
|
alignment: Alignment.center,
|
|
|
|
|
child: SizedBox(
|
|
|
|
|
width: 30,
|
|
|
|
|
height: 30,
|
|
|
|
|
child: CircularProgressIndicator(
|
|
|
|
|
valueColor: AlwaysStoppedAnimation(Colors.white)),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} else if (player.state == FijkState.error) {
|
|
|
|
|
return Container(
|
|
|
|
|
alignment: Alignment.center,
|
|
|
|
|
child: Icon(
|
|
|
|
|
Icons.error,
|
|
|
|
|
size: 30,
|
|
|
|
|
color: Color(0x99FFFFFF),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} else if (myImageProvider != null) {
|
|
|
|
|
_snapshotTimer?.cancel();
|
|
|
|
|
_snapshotTimer = Timer(Duration(milliseconds: 1500), () {
|
|
|
|
|
if (mounted) {
|
|
|
|
|
setState(() {
|
|
|
|
|
myImageProvider = null;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return Center(
|
|
|
|
|
child: IgnorePointer(
|
|
|
|
|
child: Container(
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
border: Border.all(color: Colors.yellowAccent, width: 3)),
|
|
|
|
|
child:
|
|
|
|
|
Image(height: 200, fit: BoxFit.contain, image: myImageProvider),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
return Container();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GestureDetector buildGestureDetector(BuildContext context) {
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
onScaleStart: _handleOnScaleStart,
|
|
|
|
|
onScaleUpdate: _handleOnScaleUpdate,
|
|
|
|
|
onScaleEnd: _handleOnScaleEnd,
|
|
|
|
|
onDoubleTap: () {
|
|
|
|
|
//自定义 FijkView 的双击响应
|
|
|
|
|
print('My onDoubleTap');
|
|
|
|
|
if (1.0 == playerRatioProvide.scale) {
|
|
|
|
|
playOrPause();
|
|
|
|
|
} else {
|
|
|
|
|
_handleScaleBack();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onTap: onTapFun,
|
|
|
|
|
//onDoubleTap: widget.doubleTap ? onDoubleTapFun : null,
|
|
|
|
|
onVerticalDragUpdate: onVerticalDragUpdateFun,
|
|
|
|
|
onVerticalDragStart: onVerticalDragStartFun,
|
|
|
|
|
onVerticalDragEnd: onVerticalDragEndFun,
|
|
|
|
|
//onHorizontalDragUpdate: (d) {},
|
|
|
|
|
child: AbsorbPointer(
|
|
|
|
|
absorbing: _hideStuff,
|
|
|
|
|
child: AnimatedOpacity(
|
|
|
|
|
opacity: _hideStuff ? 0 : 1,
|
|
|
|
|
duration: Duration(milliseconds: 300),
|
|
|
|
|
child: buildPanel(context),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleOnScaleStart(ScaleStartDetails details) {
|
|
|
|
|
setState(() {
|
|
|
|
|
// if (_oldFullScreen != player.value.fullScreen) {
|
|
|
|
|
// //全屏和窗口之间切换,便初始化相关变量
|
|
|
|
|
// _oldFullScreen = player.value.fullScreen;
|
|
|
|
|
// // _offset = Offset.zero;
|
|
|
|
|
// // _scale = 1.0;
|
|
|
|
|
// //_focalPoint = Offset.zero;
|
|
|
|
|
// //_deltaPoint = Offset.zero;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
_bScaleing = true;
|
|
|
|
|
_previousScale = _scale;
|
|
|
|
|
_normalizedOffset = (details.focalPoint - _offset) / _scale;
|
|
|
|
|
// 计算图片放大后的位置
|
|
|
|
|
_controller.stop();
|
|
|
|
|
_focalPoint = details.focalPoint;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleScaleBack() {
|
|
|
|
|
// 更新当前位置
|
|
|
|
|
playerRatioProvide.changeScale(1.0);
|
|
|
|
|
playerRatioProvide.changeOffset(Offset(0, 0));
|
|
|
|
|
playerRatioProvide.changeDeltaX(0.0);
|
|
|
|
|
playerRatioProvide.changeDeltaY(0.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleOnScaleUpdate(ScaleUpdateDetails details) {
|
|
|
|
|
setState(() {
|
|
|
|
|
print('details.scale = ${details.scale}');
|
|
|
|
|
//print('_scale = $_scale, offset.dx = ${_offset.dx}, offset.dy = ${_offset.dy}');
|
|
|
|
|
//print('details.focalPoint = (${details.focalPoint.dx}, ${details.focalPoint.dy})');
|
|
|
|
|
//print('details.horizontalScale = ${details.horizontalScale}; details.verticalScale = ${details.verticalScale})');
|
|
|
|
|
|
|
|
|
|
if (1.0 != details.scale) {
|
|
|
|
|
_scale = (_previousScale * details.scale).clamp(1.0, 10.0);
|
|
|
|
|
playerRatioProvide.changeScale(_scale);
|
|
|
|
|
// 限制放大倍数 1~10倍
|
|
|
|
|
_offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale);
|
|
|
|
|
} else {
|
|
|
|
|
_deltaPoint = _clampdeltaPoint(details.focalPoint - _focalPoint);
|
|
|
|
|
_focalPoint = details.focalPoint;
|
|
|
|
|
print('_deltaPoint = (${_deltaPoint.dx}, ${_deltaPoint.dy})');
|
|
|
|
|
playerRatioProvide.changeDeltaX(_deltaPoint.dx);
|
|
|
|
|
playerRatioProvide.changeDeltaY(_deltaPoint.dy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新当前位置
|
|
|
|
|
playerRatioProvide.changeOffset(_offset);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Offset _clampdeltaPoint(Offset delta) {
|
|
|
|
|
final Size size = context.size; // widget的屏幕尺寸
|
|
|
|
|
double rate = 2.0;
|
|
|
|
|
Offset deltaPoint =
|
|
|
|
|
Offset((rate * delta.dx / size.width), (rate * delta.dy / size.height));
|
|
|
|
|
_deltaPoint -= deltaPoint;
|
|
|
|
|
return Offset(
|
|
|
|
|
_deltaPoint.dx.clamp(-1.0, 1.0), _deltaPoint.dy.clamp(-1.0, 1.0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Offset _clampOffset(Offset offset) {
|
|
|
|
|
final Size size = context.size;
|
|
|
|
|
// widget的屏幕宽度
|
|
|
|
|
final Offset minOffset = Offset(size.width, size.height) * (1.0 - _scale);
|
|
|
|
|
// 限制他的最小尺寸
|
|
|
|
|
return Offset(
|
|
|
|
|
offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _handleOnScaleEnd(ScaleEndDetails details) {
|
|
|
|
|
final double magnitude = details.velocity.pixelsPerSecond.distance;
|
|
|
|
|
if (magnitude < _kMinFlingVelocity) return;
|
|
|
|
|
final Offset direction = details.velocity.pixelsPerSecond / magnitude;
|
|
|
|
|
// 计算当前的方向
|
|
|
|
|
final double distance = (Offset.zero & context.size).shortestSide;
|
|
|
|
|
// 计算放大倍速,并相应的放大宽和高,比如原来是600*480的图片,放大后倍数为1.25倍时,宽和高是同时变化的
|
|
|
|
|
_animation = _controller.drive(Tween<Offset>(
|
|
|
|
|
begin: _offset, end: _clampOffset(_offset + direction * distance)));
|
|
|
|
|
_controller
|
|
|
|
|
..value = 0.0
|
|
|
|
|
..fling(velocity: magnitude / 1000.0);
|
|
|
|
|
_bScaleing = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
playerRatioProvide = Provider.of<PlayerRatioProvide>(context);
|
|
|
|
|
Rect rect = panelRect();
|
|
|
|
|
|
|
|
|
|
List ws = <Widget>[];
|
|
|
|
|
|
|
|
|
|
if (_statelessTimer != null && _statelessTimer.isActive) {
|
|
|
|
|
ws.add(buildStateless());
|
|
|
|
|
} else if (player.state == FijkState.asyncPreparing) {
|
|
|
|
|
ws.add(buildStateless());
|
|
|
|
|
} else if (player.state == FijkState.error) {
|
|
|
|
|
ws.add(buildStateless());
|
|
|
|
|
} else if (myImageProvider != null) {
|
|
|
|
|
ws.add(buildStateless());
|
|
|
|
|
}
|
|
|
|
|
ws.add(buildGestureDetector(context));
|
|
|
|
|
if (widget.onBack != null) {
|
|
|
|
|
ws.add(buildBack(context));
|
|
|
|
|
}
|
|
|
|
|
return Positioned.fromRect(
|
|
|
|
|
rect: rect,
|
|
|
|
|
child: Stack(children: ws),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|