|
|
@@ -0,0 +1,471 @@
|
|
|
+import 'dart:async';
|
|
|
+
|
|
|
+import 'package:chewie/src/chewie_player.dart';
|
|
|
+import 'package:chewie/src/chewie_progress_colors.dart';
|
|
|
+import 'package:chewie/src/material_progress_bar.dart';
|
|
|
+import 'package:chewie/src/utils.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter/services.dart';
|
|
|
+import 'package:video_player/video_player.dart';
|
|
|
+
|
|
|
+class I2MaterialControls extends StatefulWidget {
|
|
|
+
|
|
|
+ const I2MaterialControls({Key key}) : super(key: key);
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<StatefulWidget> createState() {
|
|
|
+ return _MaterialControlsState();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _MaterialControlsState extends State<I2MaterialControls> {
|
|
|
+ VideoPlayerValue _latestValue;
|
|
|
+ double _latestVolume;
|
|
|
+ bool _hideStuff = true;
|
|
|
+ Timer _hideTimer;
|
|
|
+ Timer _initTimer;
|
|
|
+ Timer _showAfterExpandCollapseTimer;
|
|
|
+ bool _dragging = false;
|
|
|
+ bool _displayTapped = false;
|
|
|
+
|
|
|
+ final barHeight = 36.0;
|
|
|
+ final marginSize = 5.0;
|
|
|
+
|
|
|
+ VideoPlayerController controller;
|
|
|
+ ChewieController chewieController;
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ if (_latestValue.hasError) {
|
|
|
+ return chewieController.errorBuilder != null
|
|
|
+ ? chewieController.errorBuilder(
|
|
|
+ context,
|
|
|
+ chewieController.videoPlayerController.value.errorDescription,
|
|
|
+ )
|
|
|
+ : Center(
|
|
|
+ child: Icon(
|
|
|
+ Icons.error,
|
|
|
+ color: Colors.white,
|
|
|
+ size: 42,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return MouseRegion(
|
|
|
+ onHover: (_) {
|
|
|
+ _cancelAndRestartTimer();
|
|
|
+ },
|
|
|
+ child: GestureDetector(
|
|
|
+ onTap: () => _cancelAndRestartTimer(),
|
|
|
+ child: AbsorbPointer(
|
|
|
+ absorbing: _hideStuff,
|
|
|
+ child: Stack(
|
|
|
+ children: <Widget>[
|
|
|
+ Positioned(
|
|
|
+ child: _buildTopBar(context),
|
|
|
+ top: 20,
|
|
|
+ ),
|
|
|
+ Column(
|
|
|
+ children: <Widget>[
|
|
|
+ _latestValue != null &&
|
|
|
+ !_latestValue.isPlaying &&
|
|
|
+ _latestValue.duration == null ||
|
|
|
+ _latestValue.isBuffering
|
|
|
+ ? const Expanded(
|
|
|
+ child: const Center(
|
|
|
+ child: const CircularProgressIndicator(),
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ : _buildHitArea(),
|
|
|
+ _buildBottomBar(context),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void dispose() {
|
|
|
+ _dispose();
|
|
|
+ super.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ void _dispose() async {
|
|
|
+ controller.removeListener(_updateState);
|
|
|
+ _hideTimer?.cancel();
|
|
|
+ _initTimer?.cancel();
|
|
|
+ _showAfterExpandCollapseTimer?.cancel();
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void didChangeDependencies() {
|
|
|
+ final _oldController = chewieController;
|
|
|
+ chewieController = ChewieController.of(context);
|
|
|
+ controller = chewieController.videoPlayerController;
|
|
|
+
|
|
|
+ if (_oldController != chewieController) {
|
|
|
+ _dispose();
|
|
|
+ _initialize();
|
|
|
+ }
|
|
|
+
|
|
|
+ super.didChangeDependencies();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ AnimatedOpacity _buildTopBar(
|
|
|
+ BuildContext context,
|
|
|
+ ) {
|
|
|
+ return AnimatedOpacity(
|
|
|
+ opacity: _hideStuff ? 0.0 : 1.0,
|
|
|
+ duration: Duration(milliseconds: 300),
|
|
|
+ child: Container(
|
|
|
+// color: chewieController.isFullScreen ? Colors.black26 : Colors.transparent,
|
|
|
+ alignment: Alignment.centerLeft,
|
|
|
+// height: 44.0 + MediaQuery.of(context).padding.top != 0 ? MediaQuery.of(context).padding.top : 0,
|
|
|
+ width: MediaQuery.of(context).size.width,
|
|
|
+// margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top != 0 ? MediaQuery.of(context).padding.top : 0),
|
|
|
+ padding: EdgeInsets.only(left: 13,),
|
|
|
+ child: Row(
|
|
|
+ children: <Widget>[
|
|
|
+ GestureDetector(
|
|
|
+ onTap: onTapBack,
|
|
|
+ child: Image(image: AssetImage("assets/dub_user_play/zuojiantou.png", package: 'chewie'), width: 20, height: 20,)
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ AnimatedOpacity _buildBottomBar(
|
|
|
+ BuildContext context,
|
|
|
+ ) {
|
|
|
+ final iconColor = Theme.of(context).textTheme.button.color;
|
|
|
+
|
|
|
+ return AnimatedOpacity(
|
|
|
+ opacity: _hideStuff ? 0.0 : 1.0,
|
|
|
+ duration: Duration(milliseconds: 300),
|
|
|
+ child: Container(
|
|
|
+ height: barHeight,
|
|
|
+// color: Theme.of(context).dialogBackgroundColor,
|
|
|
+ color: Colors.transparent,
|
|
|
+ child: Row(
|
|
|
+ children: <Widget>[
|
|
|
+ _buildPlayPause(controller),
|
|
|
+ chewieController.isLive
|
|
|
+ ? Expanded(child: const Text('LIVE'))
|
|
|
+ : _buildPosition(iconColor),
|
|
|
+ chewieController.isLive ? const SizedBox() : _buildProgressBar(),
|
|
|
+ chewieController.isLive
|
|
|
+ ? const SizedBox()
|
|
|
+ : _buildFullPosition(iconColor),
|
|
|
+// chewieController.allowMuting
|
|
|
+// ? _buildMuteButton(controller)
|
|
|
+// : Container(),
|
|
|
+ chewieController.allowFullScreen
|
|
|
+ ? _buildExpandButton()
|
|
|
+ : Container(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ GestureDetector _buildExpandButton() {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: _onExpandCollapse,
|
|
|
+ child: AnimatedOpacity(
|
|
|
+ opacity: _hideStuff ? 0.0 : 1.0,
|
|
|
+ duration: Duration(milliseconds: 300),
|
|
|
+ child: Container(
|
|
|
+ height: barHeight,
|
|
|
+ margin: EdgeInsets.only(right: 12.0),
|
|
|
+ padding: EdgeInsets.only(
|
|
|
+ left: 8.0,
|
|
|
+ right: 8.0,
|
|
|
+ ),
|
|
|
+ child: Center(
|
|
|
+ child: Image(image: AssetImage("assets/dub_user_play/${chewieController.isFullScreen ? "shousuo.png" : "fangda.png"}", package: 'chewie'), width: 20, height: 20,),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Expanded _buildHitArea() {
|
|
|
+ return Expanded(
|
|
|
+ child: GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ if (_latestValue != null && _latestValue.isPlaying) {
|
|
|
+ if (_displayTapped) {
|
|
|
+ _hideStatusBar();
|
|
|
+ setState(() {
|
|
|
+ _hideStuff = true;
|
|
|
+ });
|
|
|
+ } else
|
|
|
+ _cancelAndRestartTimer();
|
|
|
+ } else {
|
|
|
+ _playPause();
|
|
|
+ _hideStatusBar();
|
|
|
+ setState(() {
|
|
|
+ _hideStuff = true;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ child: Container(
|
|
|
+ color: Colors.transparent,
|
|
|
+ child: Center(
|
|
|
+ child: AnimatedOpacity(
|
|
|
+ opacity:
|
|
|
+ _latestValue != null && !_latestValue.isPlaying && !_dragging
|
|
|
+ ? 1.0
|
|
|
+ : 0.0,
|
|
|
+ duration: Duration(milliseconds: 300),
|
|
|
+ child: GestureDetector(
|
|
|
+ child: Container(
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Theme.of(context).dialogBackgroundColor,
|
|
|
+ borderRadius: BorderRadius.circular(48.0),
|
|
|
+ ),
|
|
|
+ child: Padding(
|
|
|
+ padding: EdgeInsets.all(12.0),
|
|
|
+ child: Icon(Icons.play_arrow, size: 32.0, color: Colors.black,),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ GestureDetector _buildMuteButton(
|
|
|
+ VideoPlayerController controller,
|
|
|
+ ) {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ _cancelAndRestartTimer();
|
|
|
+
|
|
|
+ if (_latestValue.volume == 0) {
|
|
|
+ controller.setVolume(_latestVolume ?? 0.5);
|
|
|
+ } else {
|
|
|
+ _latestVolume = controller.value.volume;
|
|
|
+ controller.setVolume(0.0);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ child: AnimatedOpacity(
|
|
|
+ opacity: _hideStuff ? 0.0 : 1.0,
|
|
|
+ duration: Duration(milliseconds: 300),
|
|
|
+ child: ClipRect(
|
|
|
+ child: Container(
|
|
|
+ child: Container(
|
|
|
+ height: barHeight,
|
|
|
+ padding: EdgeInsets.only(
|
|
|
+ left: 8.0,
|
|
|
+ right: 8.0,
|
|
|
+ ),
|
|
|
+ child: Icon(
|
|
|
+ (_latestValue != null && _latestValue.volume > 0)
|
|
|
+ ? Icons.volume_up
|
|
|
+ : Icons.volume_off,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ GestureDetector _buildPlayPause(VideoPlayerController controller) {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: _playPause,
|
|
|
+ child: Container(
|
|
|
+ height: barHeight,
|
|
|
+ color: Colors.transparent,
|
|
|
+ margin: EdgeInsets.only(left: 8.0, right: 4.0),
|
|
|
+ padding: EdgeInsets.only(
|
|
|
+ left: 12.0,
|
|
|
+ right: 12.0,
|
|
|
+ ),
|
|
|
+ child: Image(image: AssetImage("assets/dub_user_play/${controller.value.isPlaying ? "stop.png" : "bofang.png"}", package: 'chewie'), width: 20, height: 20,),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildPosition(Color iconColor) {
|
|
|
+ final position = _latestValue != null && _latestValue.position != null
|
|
|
+ ? _latestValue.position
|
|
|
+ : Duration.zero;
|
|
|
+ final duration = _latestValue != null && _latestValue.duration != null
|
|
|
+ ? _latestValue.duration
|
|
|
+ : Duration.zero;
|
|
|
+
|
|
|
+ return Padding(
|
|
|
+ padding: EdgeInsets.only(right: 14.0),
|
|
|
+ child: Text(
|
|
|
+ '${formatDuration(position)}',
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontSize: 12.0,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildFullPosition(Color iconColor) {
|
|
|
+ final position = _latestValue != null && _latestValue.position != null
|
|
|
+ ? _latestValue.position
|
|
|
+ : Duration.zero;
|
|
|
+ final duration = _latestValue != null && _latestValue.duration != null
|
|
|
+ ? _latestValue.duration
|
|
|
+ : Duration.zero;
|
|
|
+
|
|
|
+ return Padding(
|
|
|
+ padding: EdgeInsets.only(left: 14.0, right: 20.0),
|
|
|
+ child: Text(
|
|
|
+ '${formatDuration(duration)}',
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontSize: 12.0,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ void _cancelAndRestartTimer() {
|
|
|
+ _hideTimer?.cancel();
|
|
|
+ _startHideTimer();
|
|
|
+ // 显示status bar
|
|
|
+ _showStatusBar();
|
|
|
+ setState(() {
|
|
|
+ _hideStuff = false;
|
|
|
+ _displayTapped = true;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<Null> _initialize() async {
|
|
|
+ controller.addListener(_updateState);
|
|
|
+
|
|
|
+ _updateState();
|
|
|
+
|
|
|
+ if ((controller.value != null && controller.value.isPlaying) ||
|
|
|
+ chewieController.autoPlay) {
|
|
|
+ _startHideTimer();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (chewieController.showControlsOnInitialize) {
|
|
|
+ _initTimer = Timer(Duration(milliseconds: 200), () {
|
|
|
+ // 显示status bar
|
|
|
+ _showStatusBar();
|
|
|
+ setState(() {
|
|
|
+ _hideStuff = false;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void _hideStatusBar() {
|
|
|
+ SystemChrome.setEnabledSystemUIOverlays ([SystemUiOverlay.bottom]);
|
|
|
+ }
|
|
|
+
|
|
|
+ void _showStatusBar() {
|
|
|
+ SystemChrome.setEnabledSystemUIOverlays ([SystemUiOverlay.top, SystemUiOverlay.bottom]);
|
|
|
+ }
|
|
|
+
|
|
|
+ void _onExpandCollapse() {
|
|
|
+ // 隐藏status bar
|
|
|
+ _hideStatusBar();
|
|
|
+ setState(() {
|
|
|
+ _hideStuff = true;
|
|
|
+ chewieController.toggleFullScreen();
|
|
|
+ _showAfterExpandCollapseTimer = Timer(Duration(milliseconds: 300), () {
|
|
|
+ setState(() {
|
|
|
+ _cancelAndRestartTimer();
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ void _playPause() {
|
|
|
+ bool isFinished = _latestValue.position >= _latestValue.duration;
|
|
|
+
|
|
|
+ setState(() {
|
|
|
+ if (controller.value.isPlaying) {
|
|
|
+ // 显示status bar
|
|
|
+ _showStatusBar();
|
|
|
+ _hideStuff = false;
|
|
|
+ _hideTimer?.cancel();
|
|
|
+ controller.pause();
|
|
|
+ } else {
|
|
|
+ _cancelAndRestartTimer();
|
|
|
+
|
|
|
+ if (!controller.value.initialized) {
|
|
|
+ controller.initialize().then((_) {
|
|
|
+ controller.play();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ if (isFinished) {
|
|
|
+ controller.seekTo(Duration(seconds: 0));
|
|
|
+ }
|
|
|
+ controller.play();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ void _startHideTimer() {
|
|
|
+ _hideTimer = Timer(const Duration(seconds: 3), () {
|
|
|
+ // 隐藏status bar
|
|
|
+ _hideStatusBar();
|
|
|
+ setState(() {
|
|
|
+ _hideStuff = true;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ void _updateState() {
|
|
|
+ setState(() {
|
|
|
+ _latestValue = controller.value;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildProgressBar() {
|
|
|
+ return Expanded(
|
|
|
+ child: MaterialVideoProgressBar(
|
|
|
+ controller,
|
|
|
+ onDragStart: () {
|
|
|
+ setState(() {
|
|
|
+ _dragging = true;
|
|
|
+ });
|
|
|
+
|
|
|
+ _hideTimer?.cancel();
|
|
|
+ },
|
|
|
+ onDragEnd: () {
|
|
|
+ setState(() {
|
|
|
+ _dragging = false;
|
|
|
+ });
|
|
|
+
|
|
|
+ _startHideTimer();
|
|
|
+ },
|
|
|
+ colors: chewieController.materialProgressColors ??
|
|
|
+ ChewieProgressColors(
|
|
|
+ playedColor: Color(0xFFC7000B),
|
|
|
+ handleColor: Color(0xFFC7000B),
|
|
|
+ bufferedColor: Color(0x5AC7000B),
|
|
|
+ backgroundColor: Color(0x5AFFFFFF)),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ void onTapBack() {
|
|
|
+ if (chewieController.isFullScreen) {
|
|
|
+ chewieController.exitFullScreen();
|
|
|
+ } else {
|
|
|
+ Navigator.of(context).maybePop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|