我的应用程序中有三个级别的小部件(例如)。
更高级别:_HomePage <-------返回PageView小部件
中级:_PlayerPage <-------页面项(容器)
下级:_YoutubePlayer <------- YoutubePlayerFlutter小部件(插件)
综合浏览量可以垂直滚动,并且可以随着用户滚动来切换页面。 YoutubePlayer窗口小部件使用GlobalKey,因此在页面之间滚动和切换的效果非常流畅且快速。但是,这有一个问题。如果用户翻页失败,但仅滚动了一半,使其返回原始位置,则将导致GlobalKey复制错误并弄乱元素的生命周期,因为即使翻页失败,翻页事件仍然会触发,但用户一旦开始拖动,并在卸载前一个元素的生命周期(如其在文档中所述)之前,使用相同的全局密钥创建新的小部件:
一个元素只能在当前动画帧结束之前保持不活动状态。在动画帧的末尾,所有仍处于非活动状态的元素都将被卸载。
在解决问题之后,我尝试了一些可能的解决方法,我认为它可以解决问题:
不使用全局密钥。
使用全局键,但是创建一个自定义ScrollPhysics以防止用户按住或缓慢拖动页面。用户只能翻页。
。 。 结果: 1.如果我不使用全局密钥,它可以很好地工作而不会引发任何错误,但是整体性能会变慢,因此,这会导致另一个问题。如果快速重复滑动,则视频加载时间无法赶上滑动速度。因此,某些视频在加载时会出错(但不会破坏整个应用程序,只会破坏视频本身)。此外,由于有全局键,因此滑动手势本身也无法正常工作。我也尝试过使用UniqueKey,但是与完全不使用键似乎没有什么不同。
结论:与在性能方面不使用全局密钥相比,我认为使用全局密钥更好。
结论:仅当有两个问题(禁用反向弹射和重复弹射)的解决方案时,这才可能是最佳解决方案,但我找不到解决办法。
如果您还有其他解决方法的建议或解决方案,请告诉我。我很感激。 我的简化代码如下:
import 'dart:math';
import 'package:JustMusic/global_components/api.dart';
import 'package:JustMusic/routes/home/components/youtube_player.dart';
import 'package:JustMusic/utils/custom_scroll_physics.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
ScrollPhysics _pageViewScrollPhysics = new CustomScrollPhysics();
final _pageController = PageController(initialPage: 0);
PageView pageView;
List<dynamic> _sources = [];
int _previousPageIndex = 0;
var _futureVideo;
@override
void initState() {
_futureVideo = Api.getVideoList();
_futureVideo.then((videos) {
_sources = videos;
pageView = PageView(
physics: _pageViewScrollPhysics,
controller: _pageController,
scrollDirection: Axis.vertical,
onPageChanged: (index) {
if (index > _previousPageIndex) {
print('page switched to $index (down)');
} else {
print('page switched to $index (up)');
}
_previousPageIndex = index;
},
children: []..addAll(_sources.map((_source) {
return YoutubePlayerScreen(
source: _source, pageController: _pageController);
})));
});
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _futureVideo,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return pageView;
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else {
return Center(
Text('Error: ${snapshot.error}'),
);
}
});
}
}
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
class YoutubePlayerScreen extends StatefulWidget {
YoutubePlayerScreen({
@required this.source,
@required this.pageController
});
final PageController pageController;
final dynamic source;
State<YoutubePlayerScreen> createState() => _YoutubePlayerScreenState();
}
class _YoutubePlayerScreenState extends State<YoutubePlayerScreen> {
var _controller = YoutubePlayerController();
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: YoutubePlayer(
context: context,
videoId: YoutubePlayer.convertUrlToId(widget.source["videoUrl"]),
flags: YoutubePlayerFlags(
disableDragSeek: true,
autoPlay: false,
mute: false,
showVideoProgressIndicator: true,
),
videoProgressIndicatorColor: Colors.amber,
progressColors: ProgressColors(
playedColor: Colors.amber,
handleColor: Colors.amberAccent,
),
onPlayerInitialized: (controller) {
_controller = controller;
_controller.cue();
_controller.addListener(() {
if (_controller.value.playerState == PlayerState.CUED) {
_controller.play();
}
}
);
}
)
)
);
}
}
您可以从此处找到YoutubePlayerFlutter的源代码: https://github.com/sarbagyastha/youtube_player_flutter/blob/master/lib/src/youtube_player.dart
编辑: 我还想出了另一种解决方法,但不知道如何实现。它仅在onPageChanged上加载YoutubePlayerWidget。因为我已经测试了拖动页面会加载上一个/下一个视频,但是不会触发onPageChanged侦听器。仅当页面实际转到上一页/下一页时,才会触发侦听器。