forked from Vacryptogodfather45/flutter_tiktok
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
MaJialun
committed
Jul 5, 2021
1 parent
ffe9266
commit 94f6e69
Showing
4 changed files
with
277 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:flutter_tiktok/mock/video.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_tiktok/other/pageView.dart'; | ||
import 'package:video_player/video_player.dart'; | ||
|
||
typedef LoadMoreVideo = Future<List<VPVideoController>> Function( | ||
int index, | ||
List<VPVideoController> list, | ||
); | ||
|
||
class TikTokVideoListController extends ChangeNotifier { | ||
/// 构造方法 | ||
TikTokVideoListController({ | ||
this.preloadCount = 3, | ||
this.disposeCount = 5, | ||
}); | ||
|
||
/// 预加载多少个视频 | ||
final int preloadCount; | ||
|
||
/// 超出多少个,就释放视频 | ||
final int disposeCount; | ||
|
||
/// 提供视频的builder | ||
LoadMoreVideo? _videoProvider; | ||
|
||
loadIndex(int target) { | ||
if (index.value == target) return; | ||
// 播放当前的,暂停其他的 | ||
var oldIndex = index.value; | ||
var newIndex = target; | ||
|
||
// 暂停之前的视频 | ||
if (!(oldIndex == 0 && newIndex == 0)) { | ||
playerOfIndex(oldIndex)?.controller.seekTo(Duration.zero); | ||
playerOfIndex(oldIndex)?.pause(); | ||
playerOfIndex(oldIndex)?.controller.addListener(_didUpdateValue); | ||
playerOfIndex(oldIndex)?.showPauseIcon.addListener(_didUpdateValue); | ||
print('暂停$oldIndex'); | ||
} | ||
// 开始播放当前的视频 | ||
playerOfIndex(newIndex)?.play(); | ||
playerOfIndex(newIndex)?.controller.addListener(_didUpdateValue); | ||
playerOfIndex(newIndex)?.showPauseIcon.addListener(_didUpdateValue); | ||
print('播放$newIndex'); | ||
// 处理预加载/释放内存 | ||
for (var i = 0; i < playerList.length; i++) { | ||
// 需要释放[disposeCount]之前的视频 | ||
if (i < newIndex - disposeCount) { | ||
print('释放$i'); | ||
playerOfIndex(i)?.controller.removeListener(_didUpdateValue); | ||
playerOfIndex(i)?.showPauseIcon.removeListener(_didUpdateValue); | ||
playerOfIndex(i)?.dispose(); | ||
} else { | ||
// 需要预加载 | ||
if (i > newIndex && i < newIndex + preloadCount) { | ||
print('预加载$i'); | ||
playerOfIndex(i)?.init(); | ||
} | ||
} | ||
} | ||
// 快到最底部,添加更多视频 | ||
if (playerList.length - newIndex <= 2) { | ||
_videoProvider?.call(newIndex, playerList).then( | ||
(list) async { | ||
playerList.addAll(list); | ||
notifyListeners(); | ||
}, | ||
); | ||
} | ||
|
||
// 完成 | ||
index.value = target; | ||
} | ||
|
||
_didUpdateValue() { | ||
notifyListeners(); | ||
} | ||
|
||
/// 获取指定index的player | ||
VPVideoController? playerOfIndex(int index) { | ||
if (index < 0 || index > playerList.length - 1) { | ||
return null; | ||
} | ||
return playerList[index]; | ||
} | ||
|
||
/// 视频总数目 | ||
int get videoCount => playerList.length; | ||
|
||
/// 初始化 | ||
init({ | ||
required TikTokPageController pageController, | ||
required List<VPVideoController> initialList, | ||
required LoadMoreVideo videoProvider, | ||
}) async { | ||
playerList.addAll(initialList); | ||
_videoProvider = videoProvider; | ||
pageController.addListener(() { | ||
var p = pageController.page!; | ||
if (p % 1 == 0) { | ||
loadIndex(p ~/ 1); | ||
} | ||
}); | ||
playerList.first.play(); | ||
notifyListeners(); | ||
} | ||
|
||
/// 目前的视频序号 | ||
ValueNotifier<int> index = ValueNotifier<int>(0); | ||
|
||
/// 视频列表 | ||
List<VPVideoController> playerList = []; | ||
|
||
/// | ||
VPVideoController get currentPlayer => playerList[index.value]; | ||
|
||
/// 销毁全部 | ||
void dispose() { | ||
// 销毁全部 | ||
for (var player in playerList) { | ||
player.dispose(); | ||
} | ||
playerList = []; | ||
super.dispose(); | ||
} | ||
} | ||
|
||
typedef ControllerSetter<T> = Future<void> Function(T controller); | ||
typedef ControllerBuilder<T> = T Function(); | ||
|
||
/// 抽象类,作为视频控制器必须实现这些方法 | ||
abstract class TikTokVideoController<T> { | ||
/// 获取当前的控制器实例 | ||
T? get controller; | ||
|
||
/// 是否显示暂停按钮 | ||
ValueNotifier<bool> get showPauseIcon; | ||
|
||
/// 加载视频,在init后,应当开始下载视频内容 | ||
Future<void> init({ControllerSetter<T>? afterInit}); | ||
|
||
/// 视频销毁,在dispose后,应当释放任何内存资源 | ||
Future<void> dispose(); | ||
|
||
/// 播放 | ||
Future<void> play(); | ||
|
||
/// 暂停 | ||
Future<void> pause({bool showPauseIcon: false}); | ||
} | ||
|
||
class VPVideoController extends TikTokVideoController<VideoPlayerController> { | ||
VideoPlayerController? _controller; | ||
ValueNotifier<bool> _showPauseIcon = ValueNotifier<bool>(false); | ||
|
||
final UserVideo? videoInfo; | ||
|
||
final ControllerBuilder<VideoPlayerController> _builder; | ||
final ControllerSetter<VideoPlayerController>? _afterInit; | ||
VPVideoController({ | ||
this.videoInfo, | ||
required ControllerBuilder<VideoPlayerController> builder, | ||
ControllerSetter<VideoPlayerController>? afterInit, | ||
}) : this._builder = builder, | ||
this._afterInit = afterInit; | ||
|
||
@override | ||
VideoPlayerController get controller { | ||
if (_controller == null) { | ||
_controller = _builder.call(); | ||
} | ||
return _controller!; | ||
} | ||
|
||
/// 阻止在init的时候dispose,或者在dispose前init | ||
List<Future> _actLocks = []; | ||
|
||
bool get isDispose => _disposeLock != null; | ||
bool get prepared => _prepared; | ||
bool _prepared = false; | ||
|
||
Completer<void>? _disposeLock; | ||
|
||
@override | ||
Future<void> dispose() async { | ||
if (!prepared) return; | ||
await Future.wait(_actLocks); | ||
_actLocks.clear(); | ||
var completer = Completer<void>(); | ||
_actLocks.add(completer.future); | ||
_prepared = false; | ||
await this.controller.dispose(); | ||
_controller = null; | ||
_disposeLock = Completer<void>(); | ||
completer.complete(); | ||
} | ||
|
||
@override | ||
Future<void> init({ | ||
ControllerSetter<VideoPlayerController>? afterInit, | ||
}) async { | ||
if (prepared) return; | ||
await Future.wait(_actLocks); | ||
_actLocks.clear(); | ||
var completer = Completer<void>(); | ||
_actLocks.add(completer.future); | ||
await this.controller.initialize(); | ||
afterInit ??= this._afterInit; | ||
await afterInit?.call(this.controller); | ||
_prepared = true; | ||
completer.complete(); | ||
if (_disposeLock != null) { | ||
_disposeLock?.complete(); | ||
_disposeLock = null; | ||
} | ||
} | ||
|
||
@override | ||
Future<void> pause({bool showPauseIcon: false}) async { | ||
await Future.wait(_actLocks); | ||
if (!prepared) await init(); | ||
_actLocks.clear(); | ||
if (_disposeLock != null) { | ||
await _disposeLock?.future; | ||
} | ||
await this.controller.pause(); | ||
if (showPauseIcon) { | ||
_showPauseIcon.value = true; | ||
} | ||
} | ||
|
||
@override | ||
Future<void> play() async { | ||
await Future.wait(_actLocks); | ||
if (!prepared) await init(); | ||
_actLocks.clear(); | ||
if (_disposeLock != null) { | ||
await _disposeLock?.future; | ||
} | ||
await this.controller.play(); | ||
_showPauseIcon.value = false; | ||
} | ||
|
||
@override | ||
ValueNotifier<bool> get showPauseIcon => _showPauseIcon; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.