| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- import 'dart:async';
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
- import 'package:flutter_ijkplayer/src/helper/time_helper.dart';
- import 'package:flutter_ijkplayer/src/logutil.dart';
- import 'package:flutter_ijkplayer/src/widget/progress_bar.dart';
- /// Using mediaController to Construct a Controller UI
- typedef Widget ControllerWidgetBuilder(IjkMediaController controller);
- /// default create IJK Controller UI
- Widget defaultBuildIjkControllerWidget(IjkMediaController controller) {
- return DefaultControllerWidget(
- controller: controller,
- // verticalGesture: false,
- // horizontalGesture: false,
- );
- }
- /// Default Controller Widget
- ///
- /// see [IjkPlayer] and [ControllerWidgetBuilder]
- class DefaultControllerWidget extends StatefulWidget {
- final IjkMediaController controller;
- /// If [doubleTapPlay] is true, can double tap to play or pause media.
- final bool doubleTapPlay;
- /// If [verticalGesture] is false, vertical gesture will be ignored.
- final bool verticalGesture;
- /// If [horizontalGesture] is false, horizontal gesture will be ignored.
- final bool horizontalGesture;
- /// Controlling [verticalGesture] is controlling system volume or media volume.
- final VolumeType volumeType;
- /// The UI of the controller.
- const DefaultControllerWidget({
- @required this.controller,
- this.doubleTapPlay = false,
- this.verticalGesture = true,
- this.horizontalGesture = true,
- this.volumeType = VolumeType.system,
- });
- @override
- _DefaultControllerWidgetState createState() =>
- _DefaultControllerWidgetState();
- }
- class _DefaultControllerWidgetState extends State<DefaultControllerWidget>
- implements TooltipDelegate {
- IjkMediaController get controller => widget.controller;
- bool _isShow = true;
- set isShow(bool value) {
- _isShow = value;
- setState(() {});
- if (value == true) {
- controller.refreshVideoInfo();
- }
- }
- bool get isShow => _isShow;
- Timer progressTimer;
- StreamSubscription controllerSubscription;
- @override
- void initState() {
- super.initState();
- startTimer();
- controllerSubscription = controller.textureIdStream.listen(_onTextIdChange);
- }
- void _onTextIdChange(int textId) {
- LogUtils.log("onTextChange");
- if (textId != null) {
- startTimer();
- } else {
- stopTimer();
- }
- }
- @override
- void deactivate() {
- super.deactivate();
- }
- @override
- void dispose() {
- controllerSubscription.cancel();
- stopTimer();
- super.dispose();
- }
- void startTimer() {
- if (controller.textureId == null) {
- return;
- }
- progressTimer?.cancel();
- progressTimer = Timer.periodic(Duration(milliseconds: 400), (timer) {
- LogUtils.log("will refresh info");
- controller.refreshVideoInfo();
- });
- }
- void stopTimer() {
- progressTimer?.cancel();
- }
- @override
- Widget build(BuildContext context) {
- return GestureDetector(
- behavior: HitTestBehavior.opaque,
- child: buildContent(),
- onDoubleTap: onDoubleTap(),
- onHorizontalDragStart: wrapHorizontalGesture(_onHorizontalDragStart),
- onHorizontalDragUpdate: wrapHorizontalGesture(_onHorizontalDragUpdate),
- onHorizontalDragEnd: wrapHorizontalGesture(_onHorizontalDragEnd),
- onVerticalDragStart: wrapVerticalGesture(_onVerticalDragStart),
- onVerticalDragUpdate: wrapVerticalGesture(_onVerticalDragUpdate),
- onVerticalDragEnd: wrapVerticalGesture(_onVerticalDragEnd),
- onTap: onTap,
- );
- }
- Widget buildContent() {
- if (!isShow) {
- return Container();
- }
- return StreamBuilder<VideoInfo>(
- stream: controller.videoInfoStream,
- builder: (context, snapshot) {
- var info = snapshot.data;
- if (info == null || !info.hasData) {
- return Container();
- }
- return buildPortrait(info);
- },
- );
- }
- Widget buildPortrait(VideoInfo info) {
- return PortraitController(
- controller: controller,
- info: info,
- tooltipDelegate: this,
- );
- }
- OverlayEntry _tipOverlay;
- Widget createTooltipWidgetWrapper(Widget widget) {
- var typography = Typography(platform: TargetPlatform.android);
- var theme = typography.white;
- const style = const TextStyle(
- fontSize: 15.0,
- color: Colors.white,
- fontWeight: FontWeight.normal,
- );
- var mergedTextStyle = theme.body2.merge(style);
- return Container(
- decoration: BoxDecoration(
- color: Colors.black.withOpacity(0.5),
- borderRadius: BorderRadius.circular(20.0),
- ),
- height: 100.0,
- width: 100.0,
- child: DefaultTextStyle(
- child: widget,
- style: mergedTextStyle,
- ),
- );
- }
- void showTooltip(Widget widget) {
- hideTooltip();
- _tipOverlay = OverlayEntry(
- builder: (BuildContext context) {
- return IgnorePointer(
- child: Center(
- child: widget,
- ),
- );
- },
- );
- Overlay.of(context).insert(_tipOverlay);
- }
- void hideTooltip() {
- _tipOverlay?.remove();
- _tipOverlay = null;
- }
- _ProgressCalculator _calculator;
- onTap() => isShow = !isShow;
- Function onDoubleTap() {
- return widget.doubleTapPlay
- ? () {
- LogUtils.log("ondouble tap");
- controller.playOrPause();
- }
- : null;
- }
- Function wrapHorizontalGesture(Function function) =>
- widget.horizontalGesture == true ? function : null;
- Function wrapVerticalGesture(Function function) =>
- widget.verticalGesture == true ? function : null;
- void _onHorizontalDragStart(DragStartDetails details) async {
- var videoInfo = await controller.getVideoInfo();
- _calculator = _ProgressCalculator(details, videoInfo);
- }
- void _onHorizontalDragUpdate(DragUpdateDetails details) {
- if (_calculator == null || details == null) {
- return;
- }
- var updateText = _calculator.calcUpdate(details);
- var offsetPosition = _calculator.getOffsetPosition();
- IconData iconData =
- offsetPosition > 0 ? Icons.fast_forward : Icons.fast_rewind;
- var w = Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: <Widget>[
- Icon(
- iconData,
- color: Colors.white,
- size: 40.0,
- ),
- Text(
- updateText,
- textAlign: TextAlign.center,
- ),
- ],
- );
- showTooltip(createTooltipWidgetWrapper(w));
- }
- void _onHorizontalDragEnd(DragEndDetails details) async {
- hideTooltip();
- var targetSeek = _calculator.getTargetSeek(details);
- _calculator = null;
- await controller.seekTo(targetSeek);
- var videoInfo = await controller.getVideoInfo();
- if (targetSeek < videoInfo.duration) await controller.play();
- }
- void _onVerticalDragStart(DragStartDetails details) {}
- void _onVerticalDragUpdate(DragUpdateDetails details) async {
- if (details.delta.dy > 0) {
- volumeDown();
- } else if (details.delta.dy < 0) {
- volumeUp();
- }
- var currentVolume = await getVolume();
- var column = Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: <Widget>[
- Icon(
- Icons.volume_up,
- color: Colors.white,
- size: 25.0,
- ),
- Padding(
- padding: const EdgeInsets.only(top: 10.0),
- child: Text(currentVolume.toString()),
- ),
- ],
- );
- showTooltip(createTooltipWidgetWrapper(column));
- }
- void _onVerticalDragEnd(DragEndDetails details) {
- hideTooltip();
- }
- Future<int> getVolume() async {
- switch (widget.volumeType) {
- case VolumeType.media:
- return controller.volume;
- case VolumeType.system:
- return controller.getSystemVolume();
- }
- return 0;
- }
- Future<void> volumeUp() async {
- var volume = await getVolume();
- volume++;
- switch (widget.volumeType) {
- case VolumeType.media:
- controller.volume = volume;
- break;
- case VolumeType.system:
- await IjkManager.systemVolumeUp();
- break;
- }
- }
- Future<void> volumeDown() async {
- var volume = await getVolume();
- volume--;
- switch (widget.volumeType) {
- case VolumeType.media:
- controller.volume = volume;
- break;
- case VolumeType.system:
- await IjkManager.systemVolumeDown();
- break;
- }
- }
- }
- class _ProgressCalculator {
- DragStartDetails startDetails;
- VideoInfo info;
- double dx;
- _ProgressCalculator(this.startDetails, this.info);
- String calcUpdate(DragUpdateDetails details) {
- dx = details.globalPosition.dx - startDetails.globalPosition.dx;
- var f = dx > 0 ? "+" : "-";
- var offset = getOffsetPosition().round().abs();
- return "$f${offset}s";
- }
- double getTargetSeek(DragEndDetails details) {
- var target = info.currentPosition + getOffsetPosition();
- if (target < 0) {
- target = 0;
- } else if (target > info.duration) {
- target = info.duration;
- }
- return target;
- }
- double getOffsetPosition() {
- return dx / 10;
- }
- }
- class PortraitController extends StatelessWidget {
- final IjkMediaController controller;
- final VideoInfo info;
- final TooltipDelegate tooltipDelegate;
- const PortraitController({
- Key key,
- this.controller,
- this.info,
- this.tooltipDelegate,
- }) : super(key: key);
- bool get haveTime {
- return info.hasData && info.duration > 0;
- }
- @override
- Widget build(BuildContext context) {
- if (!info.hasData) {
- return Container();
- }
- Widget bottomBar = buildBottomBar(context);
- return Column(
- children: <Widget>[
- Expanded(
- child: Container(),
- ),
- bottomBar,
- ],
- );
- }
- Widget buildBottomBar(BuildContext context) {
- var currentTime = buildCurrentText();
- var maxTime = buildMaxTimeText();
- var progress = buildProgress(info);
- var playButton = buildPlayButton(context);
- Widget widget = Row(
- children: <Widget>[
- playButton,
- Padding(
- padding: const EdgeInsets.all(8.0),
- child: currentTime,
- ),
- Expanded(child: progress),
- Padding(
- padding: const EdgeInsets.all(8.0),
- child: maxTime,
- ),
- ],
- );
- widget = DefaultTextStyle(
- style: const TextStyle(
- color: Colors.white,
- ),
- child: widget,
- );
- widget = Container(
- color: Colors.black.withOpacity(0.12),
- child: widget,
- );
- return widget;
- }
- Widget buildProgress(VideoInfo info) {
- if (!info.hasData || info.duration == 0) {
- return Container();
- }
- return Container(
- height: 22,
- child: ProgressBar(
- current: info.currentPosition,
- max: info.duration,
- changeProgressHandler: (progress) async {
- await controller.seekToProgress(progress);
- tooltipDelegate?.hideTooltip();
- },
- tapProgressHandler: (progress) {
- showProgressTooltip(info, progress);
- },
- ),
- );
- }
- buildCurrentText() {
- return haveTime
- ? Text(
- TimeHelper.getTimeText(info.currentPosition),
- )
- : Container();
- }
- buildMaxTimeText() {
- return haveTime
- ? Text(
- TimeHelper.getTimeText(info.duration),
- )
- : Container();
- }
- buildPlayButton(BuildContext context) {
- return IconButton(
- onPressed: () {
- controller.playOrPause();
- },
- color: Colors.white,
- icon: Icon(info.isPlaying ? Icons.pause : Icons.play_arrow),
- iconSize: 25.0,
- );
- }
- void showProgressTooltip(VideoInfo info, double progress) {
- var target = info.duration * progress;
- var diff = info.currentPosition - target;
- String diffString;
- if (diff < 1 && diff > -1) {
- diffString = "0s";
- } else if (diff < 0) {
- diffString = "+${TimeHelper.getTimeText(diff.abs())}";
- } else if (diff > 0) {
- diffString = "-${TimeHelper.getTimeText(diff.abs())}";
- } else {
- diffString = "0s";
- }
- Widget text = Container(
- alignment: Alignment.center,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: <Widget>[
- Text(
- TimeHelper.getTimeText(target),
- style: TextStyle(fontSize: 20),
- ),
- Container(
- height: 10,
- ),
- Text(diffString),
- ],
- ),
- );
- var tooltip = tooltipDelegate?.createTooltipWidgetWrapper(text);
- tooltipDelegate?.showTooltip(tooltip);
- }
- }
- abstract class TooltipDelegate {
- void showTooltip(Widget widget);
- Widget createTooltipWidgetWrapper(Widget widget);
- void hideTooltip();
- }
- enum VolumeType {
- system,
- media,
- }
|