flutter本地视频播放

第一次写博客不知道该怎么写,对于flutter我也是新手,所以代码还有很多地方完善,但是主要的功能都已经有了,视频全频播放,音量亮度的调节的功能都有了。有需要的朋友可以直接复制。

import ‘dart:async’;

import ‘package:auto_orientation/auto_orientation.dart’;
import ‘package:common_utils/common_utils.dart’;
import ‘package:flutter/material.dart’;
import ‘package:flutter/services.dart’;
import ‘package:flutter_screenutil/flutter_screenutil.dart’;
import ‘package:screen/screen.dart’;
import ‘package:volume_control/volume_control.dart’;
import ‘package:video_player/video_player.dart’;

class Player extends StatefulWidget {

@override
VideoPlay createState() =>
// TODO: implement createState
VideoPlay();
}

// initialize() - 初始化播放器。
// dispose() - 释放播放器资源。
// notifyListeners() - 监听播放消息。
// addListener(listener) - 添加监听器。
// removeListener(listener) - 移除监听器。
// pause() - 暂停播放。
// play() - 开始播放。
// position - 播放位置。
// seekTo(moment) - 指定到某个位置播放。
// setLooping(looping) - 是否循环播放。
// setVolume(volume) - 设置音量大小。

class VideoPlay extends State {
VideoPlayerController _controller;
String Url = ‘https://www.runoob.com/try/demo_source/mov_bbb.mp4’;
bool _isPlaying = false;
Timer _timer; // 计时器,用于延迟隐藏控件ui
bool _hidePlayControl = false; // 控制是否隐藏控件ui
double _playControlOpacity = 1; // 通过透明度动画显示/隐藏控件ui
// 记录video播放进度
// Duration _position = Duration(seconds: 0);
// Duration _totalDuration = Duration(seconds: 0);
double value = 00.00;
int num = 0;
double progressValue; //进度
String labelProgress; //tip内容
bool handle = false; //判断是否在滑动的标识
bool _hideText = true; // 控制是否隐藏文本
Offset startPosition; // 起始位置
double movePan; // 偏移量累计总和
double layoutWidth; // 组件宽度
double layoutHeight; // 组件高度
String volumePercentage = ‘’; // 组件位移描述
double playDialogOpacity = 0.0;
double brightness = 0.0; //亮度
double voice = 0.0; //声音
bool brightnessOk = false; // 是否允许调节亮度

/// 记录是否全屏
bool get _isFullScreen =>
MediaQuery.of(context).orientation == Orientation.landscape;

// 拦截返回键
Future _onWillPop() async {
if (_isFullScreen) {
_toggleFullScreen();
return false;
}
return true;
}

// 供父组件调用刷新页面,减少父组件的build
// void setPosition({position, totalDuration}) {
// setState(() {
// _position = position;
// _totalDuration = totalDuration;
// });
// }
///刷新数据
void reloadData({double ProgressValue, String LabelProgress}) {
if (mounted) {
setState(() {
// if (ProgressValue == 00.00 && LabelProgress == “00.00”) {
// _controller.play();
// }
progressValue = ProgressValue;
labelProgress = LabelProgress;
});
}
}

@override
void initState() {
//重写组件初始化方法
super.initState();

progressValue = 00.00;
labelProgress = '00:00';

// VideoPlayerUI.network, VideoPlayerUI.asset, VideoPlayerUI.file,网络视频、工程视频、本地视频文件

_controller =
    VideoPlayerController.asset(
    'assets/video/mov_bbb.mp4') //10026 31138

  // VideoPlayerController.network(//定义连接器内容,
  //     'http://vfx.mtime.cn/Video/2019/03/21/mp4/190321153853126488.mp4')//99963
  ..addListener(() {
    int duration = _controller.value.duration.inMilliseconds;
    int position = _controller.value.position.inMilliseconds;
    progressValue = position / duration * 100;
    labelProgress =
    _controller.value.position.toString().substring(2).split(".")[0];
    reloadData(ProgressValue: progressValue, LabelProgress: labelProgress);//实时刷新数据
    final bool isPlaying = _controller.value.isPlaying;
    if (isPlaying != _isPlaying) {
      setState(() {
        _isPlaying = isPlaying;
      });
    }
  })
  ..initialize().then((_) {
    // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
    setState(() {
      // _controller.play();
    });
  });
    // ..setLooping(true);//循环播放
// _startPlayControlTimer();

}

@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
// appBar: AppBar(
// title: Text(“视频播放器”),
// ),
body: Container(
width: 10.wp,
child: _controller.value.initialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: Stack(
children: [
GestureDetector(
child: VideoPlayer(_controller),
onTap: _togglePlayControl,
onVerticalDragStart: _onVerticalDragStart,
//指针已经接触到屏幕,而且可能开始垂直移动。
onVerticalDragUpdate:
_onVerticalDragUpdate, //与屏幕接触并垂直移动的指针沿垂直方向移动
),
Offstage(
offstage: _hideText,
child: Center(
child: AnimatedOpacity(
opacity: playDialogOpacity,
duration: Duration(milliseconds: 500),
child: Container(
padding: EdgeInsets.symmetric(
vertical: 5.0, horizontal: 6.0),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius:
BorderRadius.all(Radius.circular(5.0))),
child: Text(
volumePercentage,
style: TextStyle(
color: Colors.white, fontSize: 12),
),
),
),
)),
Positioned(
bottom: 0,
child: WillPopScope(
onWillPop: _onWillPop,
child: Offstage(
offstage: _hidePlayControl,
child: AnimatedOpacity(
// 加入透明度动画
opacity: _playControlOpacity,
duration: Duration(milliseconds: 300),
child: Row(
// 加载完成时才渲染,flex布局
children: [
IconButton(
// 播放按钮
padding: EdgeInsets.zero,
iconSize: 26,
icon: Icon(
// 根据控制器动态变化播放图标还是暂停
_controller.value.isPlaying
? Icons.pause
: Icons.play_arrow,
color: Colors.white,
),
onPressed: () {
setState(() {
_playOrPause();
});
},
),
SizedBox(
width: !_isFullScreen ? 0.5.wp : 1.5.wp,
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
//已拖动的颜色
activeTrackColor: Colors.blue,
//未拖动的颜色
inactiveTrackColor: Colors.white,
overlayShape: RoundSliderOverlayShape(
//可继承SliderComponentShape自定义形状
overlayRadius: 10, //滑块外圈大小
),
thumbShape: RoundSliderThumbShape(
//可继承SliderComponentShape自定义形状
disabledThumbRadius: 7, //禁用是滑块大小
enabledThumbRadius: 7, //滑块大小
)),
child: Slider(
value: progressValue,
// label: labelProgress,
divisions: 100,
onChangeStart: _onChangeStart,
onChangeEnd: _onChangeEnd,
onChanged: (val) {
int duration = _controller
.value.duration.inMilliseconds;
setState(() {
progressValue =
val.floorToDouble(); //转化成double
labelProgress = DateUtil.formatDateMs(
(val / 100 * duration).toInt(),
format: ‘mm:ss’,
);
});
},
min: 0.0,
max: 100,
),
),
),
Container(
// 播放时间

                              margin: EdgeInsets.only(left: 10),
                              child: Text(
                                labelProgress +
                                    "/" +
                                    // _controller.value.duration.inMilliseconds
                                    DateUtil.formatDateMs(
                                      (_controller.value.duration
                                              .inMilliseconds)
                                          .toInt(),
                                      format: 'mm:ss',
                                    ),
                                style: TextStyle(color: Colors.white),
                              ),
                            ),
                            IconButton(
                              // 全屏/横屏按钮
                              padding: EdgeInsets.zero,
                              iconSize: 26,
                              icon: Icon(
                                // 根据当前屏幕方向切换图标
                                _isFullScreen
                                    ? Icons.fullscreen_exit
                                    : Icons.fullscreen,
                                color: Colors.white,
                              ),
                              onPressed: () {
                                // 点击切换是否全屏
                                _toggleFullScreen();
                              },
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ))
        : Container(),
  ),
);

}

@override
void dispose() {
super.dispose();
_controller.dispose();
brightnessOk = false;
// allowHorizontal = false;
}

void _playOrPause() {
/// 同样的,点击动态播放或者暂停
// int duration = _controller.value.duration.inMilliseconds;
// int position = _controller.value.position.inMilliseconds;
// duration == position ? _replay():
_controller.value.isPlaying ? _controller.pause() : _controller.play();
_startPlayControlTimer(); // 操作控件后,重置延迟隐藏控件的timer
}

//重播
// void _replay() {
//
// super.initState();
// initState();
// }

@override
void _toggleFullScreen() {
setState(() {
if (_isFullScreen) {
/// 如果是全屏就切换竖屏
// _controller.pause();
AutoOrientation.portraitAutoMode();

    ///显示状态栏,与底部虚拟操作按钮
    SystemChrome.setEnabledSystemUIOverlays(
        [SystemUiOverlay.top, SystemUiOverlay.bottom]);
  } else {
    // _controller.play();
    AutoOrientation.landscapeAutoMode();
    ///关闭状态栏,与底部虚拟操作按钮
    SystemChrome.setEnabledSystemUIOverlays([]);
  }
  _startPlayControlTimer(); // 操作完控件开始计时隐藏
});

}

void backPress() {
// 如果是全屏,点击返回键则关闭全屏,如果不是,则系统返回键
if (_isFullScreen) {
_toggleFullScreen();
} else if (ModalRoute.of(context).isFirst) {
SystemNavigator.pop();
} else {
Navigator.pop(context);
}
}

void onChangeStart() {
// 开始手动操作标识
handle = true;
}

void onChangeEnd() {
// if (!videoInit) {
// return;
// }
// widget.startPlayControlTimer();
// 关闭手动操作标识
handle = false;
// 跳转到滑动时间
int duration = _controller.value.duration.inMilliseconds;
_controller.seekTo(
Duration(milliseconds: (progressValue / 100 * duration).toInt()),
);
if (!_controller.value.isPlaying) {
_controller.play();
}
}
///状态栏隐藏
void _startPlayControlTimer() {
/// 计时器,用法和前端js的大同小异
if (_timer != null) _timer.cancel();
_timer = Timer(
Duration(seconds: 3),
() {
/// 延迟3s后隐藏
setState(() {
_playControlOpacity = 0;
Future.delayed(Duration(milliseconds: 500)).whenComplete(() {
_hidePlayControl = true;
});
});
},
);
// _timer = Timer(Duration(seconds: 1), () {
// /// 延迟3s后隐藏
// setState(() {
// playDialogOpacity = 0.0;
// Future.delayed(Duration(milliseconds: 500)).whenComplete(() {
// _hideText = true;
// });
// });
// });
}
///音量和亮度隐藏
void _startTimer() {
/// 计时器,用法和前端js的大同小异
if (_timer != null) _timer.cancel();

_timer = Timer(Duration(seconds: 1), () {
  /// 延迟3s后隐藏
  setState(() {
    playDialogOpacity = 0.0;
    Future.delayed(Duration(milliseconds: 500)).whenComplete(() {
      _hideText = true;
    });
  });
});

}

void _togglePlayControl() {
setState(() {
num++;
///双击暂停或者播放
if (num % 2 == 0) {
if (_controller.value.isPlaying) {
_controller.pause();
} else {
_controller.play();
}
}
if (_hidePlayControl) {
/// 如果隐藏则显示
_hidePlayControl = false;
_playControlOpacity = 1;
_startPlayControlTimer(); // 开始计时器,计时后隐藏
} else {
/// 如果显示就隐藏
if (_timer != null) _timer.cancel(); // 有计时器先移除计时器
_playControlOpacity = 0;
playDialogOpacity = 0.0;
Future.delayed(Duration(milliseconds: 500)).whenComplete(() {
_hidePlayControl = true; // 延迟500ms(透明度动画结束)后,隐藏
_hideText = true;
});
}
});
}

void _reset(BuildContext context) {
startPosition = Offset(0, 0);
movePan = 0;
layoutHeight = context.size.height;
layoutWidth = context.size.width;
volumePercentage = ‘’;
}

void _onVerticalDragStart(details) async {
_reset(context);
startPosition = details.globalPosition;
if (startPosition.dx < (layoutWidth / 2)) {
/// 左边触摸
brightnessOk = true;

  brightness = await Screen.brightness;
} else {
  voice = await VolumeControl.volume;
  print(voice);
}

}

void _onVerticalDragUpdate(details) {
// if (!videoInit) {
// return;
// }

/// 累计计算偏移量(下滑减少百分比,上滑增加百分比)
movePan += (-details.delta.dy);
if (startPosition.dx < (layoutWidth / 2)) {
  /// 左边触摸
  if (brightnessOk = true) {
    setState(() {
      volumePercentage = '亮度:${(_setBrightnessValue() * 100).toInt()}%';
      _hideText = false;
      playDialogOpacity = 1.0;
      // 设置亮度:
    });
  }
} else {
  /// 右边触摸
  setState(() {
    volumePercentage = '音量:${(_setVerticalValue(num: 2) * 100).toInt()}%';
    _hideText = false;
    playDialogOpacity = 1.0;
  });
}

_startTimer();

}

double _setVerticalValue({int num = 1}) {
// 声音百分控制
double value =
double.parse((movePan / layoutHeight + voice) //_controller.value.volume
.toStringAsFixed(num));
if (value >= 1.0) {
value = 1.0;
} else if (value <= 0.0) {
value = 0.0;
}
VolumeControl.setVolume(value);
return value;
}

double _setBrightnessValue() {
// 亮度百分控制
double value =
double.parse((movePan / layoutHeight + brightness).toStringAsFixed(2));
if (value >= 1.00) {
value = 1.00;
} else if (value <= 0.00) {
value = 0.00;
}
Screen.setBrightness(value);
return value;
}
}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐