flutter視訊全頻播放,音量亮度的調節

weixin_44276396發表於2020-10-19

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;
}
}

相關文章